- Add Rooms, Tasks, TaskCompletions Drift tables with schema v2 migration - Create RoomsDao with CRUD, watchAll, watchWithStats, cascade delete, reorder - Create TasksDao with CRUD, watchInRoom (sorted by due), completeTask, overdue detection - Implement calculateNextDueDate and catchUpToPresent pure scheduling functions - Define IntervalType enum (8 types), EffortLevel enum, FrequencyInterval model - Add formatRelativeDate German formatter and curatedRoomIcons icon list - Enable PRAGMA foreign_keys in beforeOpen migration strategy - All 30 unit tests passing (17 scheduling + 6 rooms DAO + 7 tasks DAO) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
184 lines
5.7 KiB
Dart
184 lines
5.7 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;
|
|
|
|
setUp(() {
|
|
db = AppDatabase(NativeDatabase.memory());
|
|
});
|
|
|
|
tearDown(() async {
|
|
await db.close();
|
|
});
|
|
|
|
group('RoomsDao', () {
|
|
test('insertRoom returns a valid id', () async {
|
|
final id = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
|
|
);
|
|
expect(id, greaterThan(0));
|
|
});
|
|
|
|
test('watchAllRooms emits rooms ordered by sortOrder', () async {
|
|
// Insert rooms with different sort orders
|
|
await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(
|
|
name: 'Badezimmer',
|
|
iconName: 'bathtub',
|
|
sortOrder: Value(2),
|
|
),
|
|
);
|
|
await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(
|
|
name: 'Kueche',
|
|
iconName: 'kitchen',
|
|
sortOrder: Value(0),
|
|
),
|
|
);
|
|
await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(
|
|
name: 'Schlafzimmer',
|
|
iconName: 'bed',
|
|
sortOrder: Value(1),
|
|
),
|
|
);
|
|
|
|
final rooms = await db.roomsDao.watchAllRooms().first;
|
|
|
|
expect(rooms.length, 3);
|
|
expect(rooms[0].name, 'Kueche');
|
|
expect(rooms[1].name, 'Schlafzimmer');
|
|
expect(rooms[2].name, 'Badezimmer');
|
|
});
|
|
|
|
test('updateRoom changes name and iconName', () async {
|
|
final id = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
|
|
);
|
|
|
|
final room = await db.roomsDao.getRoomById(id);
|
|
final updated = room.copyWith(name: 'Wohnkueche', iconName: 'dining');
|
|
await db.roomsDao.updateRoom(updated);
|
|
|
|
final result = await db.roomsDao.getRoomById(id);
|
|
expect(result.name, 'Wohnkueche');
|
|
expect(result.iconName, 'dining');
|
|
});
|
|
|
|
test('deleteRoom cascades to associated tasks and completions', () async {
|
|
// Create room
|
|
final roomId = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
|
|
);
|
|
|
|
// Create task in the room
|
|
final taskId = await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Abspuelen',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 15),
|
|
),
|
|
);
|
|
|
|
// Complete the task to create a completion record
|
|
await db.tasksDao.completeTask(taskId, now: DateTime(2026, 3, 15));
|
|
|
|
// Verify task and completion exist
|
|
final tasksBefore = await db.tasksDao.watchTasksInRoom(roomId).first;
|
|
expect(tasksBefore.length, 1);
|
|
|
|
// Delete room
|
|
await db.roomsDao.deleteRoom(roomId);
|
|
|
|
// Verify room is gone
|
|
final rooms = await db.roomsDao.watchAllRooms().first;
|
|
expect(rooms, isEmpty);
|
|
|
|
// Verify tasks are gone
|
|
final tasksAfter = await db.tasksDao.watchTasksInRoom(roomId).first;
|
|
expect(tasksAfter, isEmpty);
|
|
});
|
|
|
|
test('reorderRooms updates sortOrder for all rooms', () async {
|
|
final id1 = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
|
|
);
|
|
final id2 = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Bad', iconName: 'bathtub'),
|
|
);
|
|
final id3 = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Schlafzimmer', iconName: 'bed'),
|
|
);
|
|
|
|
// Reorder: Bad first, then Schlafzimmer, then Kueche
|
|
await db.roomsDao.reorderRooms([id2, id3, id1]);
|
|
|
|
final rooms = await db.roomsDao.watchAllRooms().first;
|
|
expect(rooms[0].name, 'Bad');
|
|
expect(rooms[1].name, 'Schlafzimmer');
|
|
expect(rooms[2].name, 'Kueche');
|
|
});
|
|
|
|
test('watchRoomWithStats emits room with due task count and cleanliness ratio', () async {
|
|
final roomId = await db.roomsDao.insertRoom(
|
|
RoomsCompanion.insert(name: 'Kueche', iconName: 'kitchen'),
|
|
);
|
|
|
|
final today = DateTime(2026, 3, 15);
|
|
|
|
// Add a task due today (counts as "due")
|
|
await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Task Due Today',
|
|
intervalType: IntervalType.daily,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: today,
|
|
),
|
|
);
|
|
|
|
// Add an overdue task (yesterday)
|
|
await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Overdue Task',
|
|
intervalType: IntervalType.weekly,
|
|
effortLevel: EffortLevel.medium,
|
|
nextDueDate: DateTime(2026, 3, 14),
|
|
),
|
|
);
|
|
|
|
// Add a future task
|
|
await db.tasksDao.insertTask(
|
|
TasksCompanion.insert(
|
|
roomId: roomId,
|
|
name: 'Future Task',
|
|
intervalType: IntervalType.monthly,
|
|
effortLevel: EffortLevel.low,
|
|
nextDueDate: DateTime(2026, 3, 20),
|
|
),
|
|
);
|
|
|
|
final stats = await db.roomsDao.watchRoomWithStats(today: today).first;
|
|
|
|
expect(stats.length, 1);
|
|
final roomStats = stats.first;
|
|
expect(roomStats.room.name, 'Kueche');
|
|
expect(roomStats.totalTasks, 3);
|
|
// "Due" means on or before today: today (Mar 15) + overdue (Mar 14) = 2
|
|
expect(roomStats.dueTasks, 2);
|
|
// "Overdue" means strictly before today: Mar 14 = 1
|
|
expect(roomStats.overdueCount, 1);
|
|
// Cleanliness: (3 - 1) / 3 = 0.6667
|
|
expect(roomStats.cleanlinessRatio, closeTo(0.6667, 0.001));
|
|
});
|
|
});
|
|
}
|