Files
HouseHoldKeaper/test/features/tasks/data/task_history_dao_test.dart
Jean-Luc Makiola ceae7d7d61 feat(06-01): add watchCompletionsForTask DAO method and history localization strings
- Add watchCompletionsForTask(taskId) to TasksDao: Stream<List<TaskCompletion>> sorted newest first
- Regenerate tasks_dao.g.dart with build_runner
- Add taskHistoryTitle, taskHistoryEmpty, taskHistoryCount to app_de.arb
- Regenerate app_localizations.dart and app_localizations_de.dart
- All 5 new DAO tests pass, zero analyze issues
2026-03-16 21:55:44 +01:00

158 lines
5.0 KiB
Dart

import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:household_keeper/core/database/database.dart';
import 'package:household_keeper/features/tasks/domain/effort_level.dart';
import 'package:household_keeper/features/tasks/domain/frequency.dart';
void main() {
late AppDatabase db;
late int roomId;
setUp(() async {
db = AppDatabase(NativeDatabase.memory());
roomId = await db.roomsDao.insertRoom(
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
);
});
tearDown(() async {
await db.close();
});
group('TasksDao.watchCompletionsForTask', () {
test('returns empty list when task has no completions', () async {
final taskId = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Staubsaugen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 15),
),
);
final completions =
await db.tasksDao.watchCompletionsForTask(taskId).first;
expect(completions, isEmpty);
});
test('returns completion after completeTask is called', () async {
final taskId = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Abspuelen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
),
);
final completionTime = DateTime(2026, 3, 15, 10, 30);
await db.tasksDao.completeTask(taskId, now: completionTime);
final completions =
await db.tasksDao.watchCompletionsForTask(taskId).first;
expect(completions.length, 1);
expect(completions.first.taskId, taskId);
expect(completions.first.completedAt, completionTime);
});
test('returns multiple completions in reverse-chronological order', () async {
final taskId = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Fenster putzen',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.high,
nextDueDate: DateTime(2026, 1, 1),
),
);
// Complete multiple times with specific timestamps
final time1 = DateTime(2026, 1, 10, 9, 0);
final time2 = DateTime(2026, 2, 12, 14, 30);
final time3 = DateTime(2026, 3, 15, 8, 0);
// Insert out of order to verify ordering is enforced by query
await db.tasksDao.completeTask(taskId, now: time1);
await db.tasksDao.completeTask(taskId, now: time2);
await db.tasksDao.completeTask(taskId, now: time3);
final completions =
await db.tasksDao.watchCompletionsForTask(taskId).first;
expect(completions.length, 3);
// Newest first (reverse-chronological)
expect(completions[0].completedAt, time3);
expect(completions[1].completedAt, time2);
expect(completions[2].completedAt, time1);
});
test('completions for different tasks are isolated', () async {
final taskId1 = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Task A',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
),
);
final taskId2 = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Task B',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
),
);
await db.tasksDao.completeTask(taskId1, now: DateTime(2026, 3, 15));
final completionsForTask1 =
await db.tasksDao.watchCompletionsForTask(taskId1).first;
final completionsForTask2 =
await db.tasksDao.watchCompletionsForTask(taskId2).first;
expect(completionsForTask1.length, 1);
expect(completionsForTask1.first.taskId, taskId1);
expect(completionsForTask2, isEmpty);
});
test('stream emits updated list after new completion is added', () async {
final taskId = await db.tasksDao.insertTask(
TasksCompanion.insert(
roomId: roomId,
name: 'Bodenwischen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 15),
),
);
// Collect stream emissions
final emissions = <List<TaskCompletion>>[];
final subscription = db.tasksDao
.watchCompletionsForTask(taskId)
.listen(emissions.add);
// Wait for initial empty emission
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(emissions.isNotEmpty, isTrue);
expect(emissions.last, isEmpty);
// Complete the task
await db.tasksDao.completeTask(taskId, now: DateTime(2026, 3, 15, 9, 0));
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(emissions.last.length, 1);
await subscription.cancel();
});
});
}