feat(domain): SQLite repo for Package aggregate (task 26)
Summary
Persists the Package aggregate to SQLite with full CRUD operations and proper foreign key handling. Implements the repository pattern per PRD-v2 §P1.7, unblocking downstream package management tasks (27–31).
Why
Packages (containers, playlists, manual groups, split archives) were in-memory only. Persistence enables user workflows: install container → convert to package → redownload members, or group related downloads manually. Foreign key ON DELETE SET NULL preserves download records even if their package is deleted.
Changes
- Domain : Refactored
Packageaggregate with typedPackageId(String, not u64) and newPackageSourceTypeenum (Container, Playlist, Manual, SplitArchive). Added setters for folder_path, password, auto_extract, priority. Kept in-memorydownload_idsvec separate from persisted FK inverse. - Port : New
PackageRepositorytrait with methodsfind_by_id,save,list,delete,list_downloads. Contracts match hexagonal architecture (domain never knows about ORM). - Migration :
m20260429_000007_create_packagescreates table with TEXT primary key, source_type enum, optional folder_path/password, defaults for auto_extract (1) and priority (5). Adds package_id FK to downloads with SET NULL cascade. Creates idx_downloads_package. - Adapter :
SqlitePackageRepoimplements trait via sea-orm upsert (insert…on_conflict). Preserves created_at timestamp on update. Uses raw SQL for list_downloads since download entity FK not yet exposed (future task). - Entity : sea-orm model with
into_domain()andfrom_domain()converters. Handles type conversions: source_type string ↔ enum, i64 timestamp → u64, validation on unknown enum variants.
Testing
- 23 unit tests in
package.rscovering creation, setters, source-type round-trip, reconstruction. - 19 unit tests in
package_repo.rscovering save/find/list/delete, upsert with created_at preservation, FK SET NULL validation, list_downloads ordering. - 5 unit tests in
ports/driven/tests.rsin-memory mock implementation. - In-database migration test verifies schema on fresh and migrated DBs (from pre-packages state).
- Coverage: 99.73% (domain), 100% (entity), 98.82% (adapter). Acceptance criteria: [x] migration applies on fresh + existing, [x] FK SET NULL works, [x] all fields round-trip, [x] ≥85% coverage.
```bash
cargo test --workspace -- --nocapture
cargo llvm-cov --workspace --fail-under-lines 85
```
All 1204 tests pass (1201 existing + 3 new error-path).
Related Issues
- Closes #Task-26
- Refs PRD-v2 §P1.7, PRD §6.3
- Unblocks Task-27 (Commands), Task-28 (Queries), Task-29 (Vue), Task-30 (Auto-grouping), Task-31 (Split archives)
Notes for Reviewer
package_idtype changed from u64 to String (TEXT primary key in SQL). Required touchingDomainEvent::PackageCreatedto usePackageIdwrapper instead of raw u64.list_downloads()uses raw SQL parameterized query because download entity hasn't yet exposed the new package_id FK field (that wiring happens in future tasks). This keeps the repo self-contained without blocking on downstream changes.save()explicitly excludescreated_atfrom the upsert update columns via sea-orm'son_conflict().do_update().col()chain. This prevents timestamp mutation on subsequent saves.
Checklist
- Tests added and passing (1204 total)
- CHANGELOG.md updated
- No
.unwrap()outside tests - No new external dependencies
- No warnings or clippy errors
- Git workflow followed (feat/task-26 branch, conventional commit, clean tree)
- Acceptance criteria met (85% coverage, FK validation, round-trip preservation, fresh + existing DB)
- Self-reviewed diff
- CI will be green
Summary by cubic
Adds SQLite persistence for packages with a new PackageRepository + SqlitePackageRepo, full CRUD, and strict validation. Implements Task-26 (PRD-v2 §P1.7) and updates events to use string PackageId.
-
New Features
- Repository:
find_by_id,save(upsert, preservescreated_at),list(ordered by(created_at, id)),delete,list_downloads(ordered byqueue_position; raw SQL now uses?placeholder for SQLite). - Domain:
PackageId(String),PackageSourceType(serializes assplit_archive), setters; priority 1..=10 enforced;auto_extractis 0/1;created_atmust be non-negative;DomainEvent::PackageCreated.idisPackageId.
- Repository:
-
Migration
- Create real
packagestable; adddownloads.package_id TEXT REFERENCES packages(id) ON DELETE SET NULLandidx_downloads_package. - Down migration restores the legacy
packagestable (BIGINT id, name, created_at).
- Create real
Summary by CodeRabbit
-
New Features
- Persistent package storage with richer metadata, deterministic ordering, safe upserts, and preserved downloads when packages are removed.
- Full Accounts management UI with quota-driven rotation, cooldown/expiry rules, extended selection strategies (including exclusions), credential import/export, and keyring-backed credential storage.
- Improved package events and repository wiring for UI interactions via the app bridge.
-
Tests
- Expanded migration, persistence, and round-trip tests covering ordering, upsert stability, detach behavior, and error cases.