import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:drift/drift.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()); // Create a room for task tests roomId = await db.roomsDao.insertRoom( RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'), ); }); tearDown(() async { await db.close(); }); group('TasksDao', () { test('insertTask returns a valid id', () async { final id = await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Abspuelen', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 15), ), ); expect(id, greaterThan(0)); }); test('watchTasksInRoom emits tasks sorted by nextDueDate ascending', () async { // Insert tasks with different due dates (out of order) await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Later Task', intervalType: IntervalType.weekly, effortLevel: EffortLevel.medium, nextDueDate: DateTime(2026, 3, 20), ), ); await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Early Task', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 10), ), ); await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Middle Task', intervalType: IntervalType.biweekly, effortLevel: EffortLevel.high, nextDueDate: DateTime(2026, 3, 15), ), ); final tasks = await db.tasksDao.watchTasksInRoom(roomId).first; expect(tasks.length, 3); expect(tasks[0].name, 'Early Task'); expect(tasks[1].name, 'Middle Task'); expect(tasks[2].name, 'Later Task'); }); test('updateTask changes name, description, interval, effort', () async { final id = await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Abspuelen', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 15), ), ); // Get the task, modify it, and update final tasks = await db.tasksDao.watchTasksInRoom(roomId).first; final task = tasks.first; final updated = task.copyWith( name: 'Geschirr spuelen', description: Value('Mit Spuelmittel'), intervalType: IntervalType.weekly, effortLevel: EffortLevel.medium, ); await db.tasksDao.updateTask(updated); final result = await db.tasksDao.watchTasksInRoom(roomId).first; expect(result.first.name, 'Geschirr spuelen'); expect(result.first.description, 'Mit Spuelmittel'); expect(result.first.intervalType, IntervalType.weekly); expect(result.first.effortLevel, EffortLevel.medium); }); test('deleteTask removes the task', () async { final id = await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Abspuelen', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 15), ), ); await db.tasksDao.deleteTask(id); final tasks = await db.tasksDao.watchTasksInRoom(roomId).first; expect(tasks, isEmpty); }); test('completeTask records completion and updates nextDueDate', () async { final id = await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Abspuelen', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 15), ), ); await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 15)); // Check that the next due date was updated (daily: +1 day) final tasks = await db.tasksDao.watchTasksInRoom(roomId).first; expect(tasks.first.nextDueDate, DateTime(2026, 3, 16)); }); test('completeTask with overdue task catches up to present', () async { // Task was due 2 weeks ago with weekly interval final id = await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Staubsaugen', intervalType: IntervalType.weekly, effortLevel: EffortLevel.medium, nextDueDate: DateTime(2026, 3, 1), ), ); // Complete on March 15 await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 15)); // Should catch up: Mar 1 -> Mar 8 (still past) -> Mar 15 (equals today, done) final tasks = await db.tasksDao.watchTasksInRoom(roomId).first; final nextDue = tasks.first.nextDueDate; // Next due should be on or after Mar 15 expect( nextDue.isAfter(DateTime(2026, 3, 14)) || nextDue.isAtSameMomentAs(DateTime(2026, 3, 15)), isTrue, ); }); test('tasks with nextDueDate before today are detected as overdue', () async { // Insert an overdue task await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Overdue Task', intervalType: IntervalType.weekly, effortLevel: EffortLevel.medium, nextDueDate: DateTime(2026, 3, 10), ), ); // Insert a task due today (not overdue) await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Today Task', intervalType: IntervalType.daily, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 15), ), ); // Insert a future task (not overdue) await db.tasksDao.insertTask( TasksCompanion.insert( roomId: roomId, name: 'Future Task', intervalType: IntervalType.monthly, effortLevel: EffortLevel.low, nextDueDate: DateTime(2026, 3, 20), ), ); final overdueCount = await db.tasksDao.getOverdueTaskCount( roomId, today: DateTime(2026, 3, 15), ); // Only the task due Mar 10 is overdue (before Mar 15) expect(overdueCount, 1); }); }); }