Files
HouseHoldKeaper/test/features/home/data/calendar_dao_test.dart
Jean-Luc Makiola d220dbe5ce
All checks were successful
Build and Release to F-Droid / build-and-deploy (push) Successful in 10m30s
test(TaskListScreen): add integration tests for filtered and overdue task states
- Covers empty states, celebration state, and scheduled/overdue task rendering
- Verifies proper checkbox behavior for future tasks
- Tests AppBar for sort dropdown, edit/delete actions, and calendar strip
- Adds necessary test helpers and overrides for room-specific tasks
2026-03-16 23:35:17 +01:00

550 lines
19 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 room1Id;
late int room2Id;
setUp(() async {
db = AppDatabase(NativeDatabase.memory());
room1Id = await db.roomsDao.insertRoom(
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
);
room2Id = await db.roomsDao.insertRoom(
RoomsCompanion.insert(name: 'Badezimmer', iconName: 'bathroom'),
);
});
tearDown(() async {
await db.close();
});
group('CalendarDao.watchTasksForDate', () {
test('returns empty list when no tasks exist', () async {
final result = await db.calendarDao
.watchTasksForDate(DateTime(2026, 3, 16))
.first;
expect(result, isEmpty);
});
test('returns only tasks due on the queried date', () async {
// Task due on March 16
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Abspuelen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16, 9, 30),
));
// Task due on March 15 (should NOT appear)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Staubsaugen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 15),
));
// Task due on March 17 (should NOT appear)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Fenster putzen',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.high,
nextDueDate: DateTime(2026, 3, 17),
));
final result = await db.calendarDao
.watchTasksForDate(DateTime(2026, 3, 16))
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Abspuelen');
});
test('returns tasks from multiple rooms with correct room pairing',
() async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Kueche Aufgabe',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 20),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Bad Aufgabe',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 20, 14),
));
final result = await db.calendarDao
.watchTasksForDate(DateTime(2026, 3, 20))
.first;
expect(result.length, 2);
final names = result.map((t) => t.task.name).toList();
expect(names, contains('Kueche Aufgabe'));
expect(names, contains('Bad Aufgabe'));
final kuecheTask =
result.firstWhere((t) => t.task.name == 'Kueche Aufgabe');
expect(kuecheTask.roomName, 'Kueche');
expect(kuecheTask.roomId, room1Id);
final badTask =
result.firstWhere((t) => t.task.name == 'Bad Aufgabe');
expect(badTask.roomName, 'Badezimmer');
expect(badTask.roomId, room2Id);
});
test('returns tasks sorted alphabetically by name', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Zitrone putzen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 18),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Abspuelen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 18, 10),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Moppen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 18, 8),
));
final result = await db.calendarDao
.watchTasksForDate(DateTime(2026, 3, 18))
.first;
expect(result.length, 3);
expect(result[0].task.name, 'Abspuelen');
expect(result[1].task.name, 'Moppen');
expect(result[2].task.name, 'Zitrone putzen');
});
test('does NOT include overdue carry-over for past dates', () async {
// Task due on March 10 (overdue relative to March 16)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Alte Aufgabe',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 10),
));
// Task due on March 15 (queried date)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Richtige Aufgabe',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
// Querying March 15 should only return the task due on March 15
final result = await db.calendarDao
.watchTasksForDate(DateTime(2026, 3, 15))
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Richtige Aufgabe');
});
});
group('CalendarDao.watchOverdueTasks', () {
test('returns empty list when no overdue tasks exist', () async {
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result, isEmpty);
});
test('returns tasks whose nextDueDate is before referenceDate', () async {
// Task due March 15 — overdue relative to March 16
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Ueberfaelliges Task',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
// Task due March 10 — also overdue
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Sehr altes Task',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 10),
));
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result.length, 2);
});
test('does NOT include tasks due ON the referenceDate', () async {
// Task due exactly on reference date (March 16) — should NOT appear
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Heutiges Task',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16),
));
// Task due yesterday (March 15) — should appear
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Gestriges Task',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Gestriges Task');
});
test('does NOT include tasks due in the future', () async {
// Future task — should NOT appear
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Zukuenftiges Task',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 20),
));
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result, isEmpty);
});
test('returns overdue tasks sorted by nextDueDate ascending', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Neues Overdue',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Altes Overdue',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 1),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Mittleres Overdue',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 10),
));
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result.length, 3);
expect(result[0].task.name, 'Altes Overdue');
expect(result[1].task.name, 'Mittleres Overdue');
expect(result[2].task.name, 'Neues Overdue');
});
test('returns overdue task with correct room pairing', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Bad Overdue',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 14),
));
final result = await db.calendarDao
.watchOverdueTasks(DateTime(2026, 3, 16))
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Bad Overdue');
expect(result.first.roomName, 'Badezimmer');
expect(result.first.roomId, room2Id);
});
});
group('CalendarDao.watchTasksForDateInRoom', () {
test('returns empty list when no tasks exist in room', () async {
final result = await db.calendarDao
.watchTasksForDateInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result, isEmpty);
});
test('returns only tasks due on the queried date in the specified room',
() async {
// Task due on March 16 in room1
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Abspuelen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16, 9, 30),
));
// Task due on March 15 in room1 (should NOT appear)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Staubsaugen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 15),
));
// Task due on March 17 in room1 (should NOT appear)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Fenster putzen',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.high,
nextDueDate: DateTime(2026, 3, 17),
));
final result = await db.calendarDao
.watchTasksForDateInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Abspuelen');
});
test('does not return tasks from other rooms on the same date', () async {
// Task due on March 16 in room1
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Kueche Aufgabe',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16),
));
// Task due on March 16 in room2 (should NOT appear when querying room1)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Bad Aufgabe',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 16),
));
final result = await db.calendarDao
.watchTasksForDateInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Kueche Aufgabe');
expect(result.first.roomId, room1Id);
});
test('returns tasks sorted alphabetically by name', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Zitrone putzen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 18),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Abspuelen',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 18, 10),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Moppen',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 18, 8),
));
final result = await db.calendarDao
.watchTasksForDateInRoom(DateTime(2026, 3, 18), room1Id)
.first;
expect(result.length, 3);
expect(result[0].task.name, 'Abspuelen');
expect(result[1].task.name, 'Moppen');
expect(result[2].task.name, 'Zitrone putzen');
});
});
group('CalendarDao.watchOverdueTasksInRoom', () {
test('returns empty list when no overdue tasks in room', () async {
final result = await db.calendarDao
.watchOverdueTasksInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result, isEmpty);
});
test('returns only overdue tasks in the specified room', () async {
// Task due March 15 in room1 — overdue relative to March 16
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Ueberfaelliges Task',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
// Task due March 10 in room1 — also overdue
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Sehr altes Task',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 10),
));
// Task due on March 16 in room1 — NOT overdue (should NOT appear)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Heutiges Task',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16),
));
final result = await db.calendarDao
.watchOverdueTasksInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result.length, 2);
});
test('does not return overdue tasks from other rooms', () async {
// Overdue task in room1
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Kueche Overdue',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 14),
));
// Overdue task in room2 (should NOT appear when querying room1)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Bad Overdue',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 14),
));
final result = await db.calendarDao
.watchOverdueTasksInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result.length, 1);
expect(result.first.task.name, 'Kueche Overdue');
expect(result.first.roomId, room1Id);
});
test('returns overdue tasks sorted by nextDueDate ascending', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Neues Overdue',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 15),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Altes Overdue',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 1),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Mittleres Overdue',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 10),
));
final result = await db.calendarDao
.watchOverdueTasksInRoom(DateTime(2026, 3, 16), room1Id)
.first;
expect(result.length, 3);
expect(result[0].task.name, 'Altes Overdue');
expect(result[1].task.name, 'Mittleres Overdue');
expect(result[2].task.name, 'Neues Overdue');
});
});
group('CalendarDao.getTaskCountInRoom', () {
test('returns 0 when room has no tasks', () async {
final count = await db.calendarDao.getTaskCountInRoom(room1Id);
expect(count, 0);
});
test('returns correct count for room', () async {
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Aufgabe 1',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Aufgabe 2',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 20),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Aufgabe 3',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.high,
nextDueDate: DateTime(2026, 3, 25),
));
final count = await db.calendarDao.getTaskCountInRoom(room1Id);
expect(count, 3);
});
test('does not count tasks from other rooms', () async {
// Two tasks in room1
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Kueche Aufgabe 1',
intervalType: IntervalType.daily,
effortLevel: EffortLevel.low,
nextDueDate: DateTime(2026, 3, 16),
));
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room1Id,
name: 'Kueche Aufgabe 2',
intervalType: IntervalType.weekly,
effortLevel: EffortLevel.medium,
nextDueDate: DateTime(2026, 3, 20),
));
// One task in room2 (should NOT be counted for room1)
await db.tasksDao.insertTask(TasksCompanion.insert(
roomId: room2Id,
name: 'Bad Aufgabe',
intervalType: IntervalType.monthly,
effortLevel: EffortLevel.high,
nextDueDate: DateTime(2026, 3, 18),
));
final count = await db.calendarDao.getTaskCountInRoom(room1Id);
expect(count, 2);
});
});
}