- Tests for softDeleteTask sets isActive=false without removing from DB - Tests for getCompletionCount with 0 and N completions - Tests for watchTasksInRoom excludes inactive tasks - Tests for getOverdueTaskCount excludes inactive tasks - Test for hard deleteTask still removes task and completions
341 lines
11 KiB
Dart
341 lines
11 KiB
Dart
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 {
|
|
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);
|
|
});
|
|
|
|
test('softDeleteTask sets isActive to false without removing 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.softDeleteTask(id);
|
|
|
|
// Task should still exist in DB but isActive == false
|
|
final allTasks = await (db.select(db.tasks)).get();
|
|
expect(allTasks.length, 1);
|
|
expect(allTasks.first.isActive, isFalse);
|
|
});
|
|
|
|
test('getCompletionCount returns 0 for task with no completions', () async {
|
|
final id = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Abspuelen',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 15),
|
|
),
|
|
);
|
|
|
|
final count = await db.tasksDao.getCompletionCount(id);
|
|
expect(count, 0);
|
|
});
|
|
|
|
test('getCompletionCount returns correct count after completions', () async {
|
|
final id = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Abspuelen',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 13),
|
|
),
|
|
);
|
|
|
|
await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 13));
|
|
await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 14));
|
|
await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 15));
|
|
|
|
final count = await db.tasksDao.getCompletionCount(id);
|
|
expect(count, 3);
|
|
});
|
|
|
|
test('watchTasksInRoom excludes soft-deleted (isActive=false) tasks', () async {
|
|
final activeId = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Active Task',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 15),
|
|
),
|
|
);
|
|
final inactiveId = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Inactive Task',
|
|
intervalType: IntervalType.weekly,
|
|
effortLevel: EffortLevel.medium,
|
|
nextDueDate: DateTime(2026, 3, 10),
|
|
),
|
|
);
|
|
|
|
await db.tasksDao.softDeleteTask(inactiveId);
|
|
|
|
final tasks = await db.tasksDao.watchTasksInRoom(roomId).first;
|
|
expect(tasks.length, 1);
|
|
expect(tasks.first.id, activeId);
|
|
expect(tasks.first.name, 'Active Task');
|
|
});
|
|
|
|
test('getOverdueTaskCount excludes soft-deleted (isActive=false) tasks', () async {
|
|
// Active overdue task
|
|
await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Active Overdue',
|
|
intervalType: IntervalType.weekly,
|
|
effortLevel: EffortLevel.medium,
|
|
nextDueDate: DateTime(2026, 3, 10),
|
|
),
|
|
);
|
|
// Inactive overdue task
|
|
final inactiveId = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Inactive Overdue',
|
|
intervalType: IntervalType.weekly,
|
|
effortLevel: EffortLevel.medium,
|
|
nextDueDate: DateTime(2026, 3, 5),
|
|
),
|
|
);
|
|
|
|
await db.tasksDao.softDeleteTask(inactiveId);
|
|
|
|
final overdueCount = await db.tasksDao.getOverdueTaskCount(
|
|
roomId,
|
|
today: DateTime(2026, 3, 15),
|
|
);
|
|
expect(overdueCount, 1);
|
|
});
|
|
|
|
test('hard deleteTask still removes task and its completions', () async {
|
|
final id = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Abspuelen',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 13),
|
|
),
|
|
);
|
|
await db.tasksDao.completeTask(id, now: DateTime(2026, 3, 13));
|
|
|
|
await db.tasksDao.deleteTask(id);
|
|
|
|
final tasks = await (db.select(db.tasks)).get();
|
|
final completions = await (db.select(db.taskCompletions)).get();
|
|
expect(tasks, isEmpty);
|
|
expect(completions, isEmpty);
|
|
});
|
|
});
|
|
}
|