timetracker/.tasks/done/TT-012-unit-tests.md
2026-08-03 21:51:48 +02:00

96 lines
3 KiB
Markdown

# TT-012 — Unit Tests
**Type:** Task
**Priority:** Medium
**Labels:** testing, unit-tests
**Depends on:** TT-004, TT-005
**Blocks:**
---
## Summary
Write comprehensive unit tests for all repository methods and Riverpod
notifiers. See `.ai/testing.md` for full conventions.
## Scope
### Database / Repository tests
All tests use `NativeDatabase.memory()` — never the real file DB.
| Test file | What to test |
|-----------|-------------|
| `test/core/database/app_database_test.dart` | Schema creation, schemaVersion |
| `test/features/projects/projects_repository_test.dart` | CRUD, watchActive stream, archive |
| `test/features/entries/entries_repository_test.dart` | CRUD, watchByDateRange, getActiveEntry |
| `test/features/entries/tags_repository_test.dart` | findOrCreate, setTagsForEntry |
| `test/features/reports/reports_repository_test.dart` | getDurationByProject, getDurationByWeekday |
### Notifier tests
All notifier tests use `ProviderContainer` with mocked repositories.
| Test file | What to test |
|-----------|-------------|
| `test/features/timer/timer_notifier_test.dart` | start, stop, discard, restore from open entry |
| `test/features/projects/projects_notifier_test.dart` | create, update, archive, delete |
| `test/features/entries/entries_notifier_test.dart` | create, update, delete |
## Test Patterns
### Repository test pattern
```dart
late AppDatabase db;
late DriftProjectsRepository repository;
setUp(() {
db = AppDatabase(NativeDatabase.memory());
repository = DriftProjectsRepository(db);
});
tearDown(() => db.close());
test('insert and retrieve project', () async {
await repository.create(name: 'Test', colorValue: 0xFF0000FF);
final projects = await repository.watchActive().first;
expect(projects, hasLength(1));
expect(projects.first.name, equals('Test'));
});
```
### Notifier test pattern
```dart
late ProviderContainer container;
late MockProjectsRepository mockRepo;
setUp(() {
mockRepo = MockProjectsRepository();
container = ProviderContainer(overrides: [
projectsRepositoryProvider.overrideWithValue(mockRepo),
]);
});
tearDown(container.dispose);
```
## Coverage Goals
| Layer | Target |
|-------|--------|
| Repository (CRUD methods) | 100% |
| Notifiers | ≥ 80% |
| Export service | ≥ 70% |
## Acceptance Criteria
- [ ] All listed test files exist and pass
- [ ] `flutter test` exits with code 0
- [ ] No test uses the real file database
- [ ] Each repository test covers: insert, read, update, delete, stream emission
- [ ] `TimerNotifier` test covers: start → running state, stop → idle + entry saved,
discard → idle + entry deleted, build with open entry → running state restored
- [ ] `flutter test --coverage` generates coverage report
## Running tests
```bash
flutter test # all tests
flutter test test/features/timer/ # single feature
flutter test --coverage # with lcov coverage
genhtml coverage/lcov.info -o coverage/html # HTML report
```