import 'dart:io'; import 'package:cookit/src/database/database.dart'; import 'package:cookit/src/planning/models/meal.dart'; import 'package:cookit/src/planning/plan_repository.dart'; import 'package:cookit/src/recipes/photo_store.dart'; import 'package:cookit/src/recipes/recipe_repository.dart'; import 'package:flutter_test/flutter_test.dart'; import '../support/fixtures.dart'; import '../support/test_database.dart'; void main() { late AppDatabase db; late PhotoStore photos; late PlanRepository plan; late RecipeRepository recipes; final yesterday = DateTime.utc(2026, 10, 22); final today = DateTime.utc(2026, 10, 23); final tomorrow = DateTime.utc(2026, 10, 24); setUp(() async { db = testDatabase(); photos = await tempPhotoStore(); plan = PlanRepository(db); recipes = RecipeRepository(db, photos); }); tearDown(() async { await db.close(); await photos.directory.delete(recursive: true); }); Future meal(String name) async => (await plan.allMeals()).firstWhere((m) => m.name == name); test('a fresh database plans Mittag and Abend', () async { expect((await plan.allMeals()).map((m) => m.name), defaultMeals); }); test('entries are added in order, moved to the end of another Meal and removed', () async { await recipes.saveRecipe(recipe()); final abend = await meal('Abend'); final mittag = await meal('Mittag'); await plan.addEntry(today, abend.id, recipeId: 'r1'); await plan.addEntry(today, abend.id, note: 'Salat'); await plan.addEntry(tomorrow, mittag.id, note: 'Reste'); var entries = await plan.allEntries(); expect( [for (final e in entries) (e.day, e.recipeId ?? e.note, e.position)], [(today, 'r1', 0), (today, 'Salat', 1), (tomorrow, 'Reste', 0)], ); await plan.moveEntry(entries.first.id, tomorrow, mittag.id); await plan.removeEntry(entries[1].id); entries = await plan.allEntries(); expect( [for (final e in entries) (e.day, e.mealId, e.recipeId ?? e.note, e.position)], [(tomorrow, mittag.id, 'Reste', 0), (tomorrow, mittag.id, 'r1', 1)], ); }); test('deleting a Meal removes its upcoming entries and keeps it retired for the past', () async { final abend = await meal('Abend'); await plan.addEntry(yesterday, abend.id, note: 'Pizza'); await plan.addEntry(today, abend.id, note: 'Suppe'); await plan.addEntry(tomorrow, abend.id, note: 'Curry'); expect(await plan.upcomingEntryCount(abend.id, today), 2); await plan.deleteMeal(abend.id, today); expect((await plan.allEntries()).map((e) => e.note), ['Pizza']); expect((await meal('Abend')).retired, isTrue); // Added again under the same name, the old Meal comes back with its history. await plan.addMeal('abend'); final back = await meal('abend'); expect(back.id, abend.id); expect(back.retired, isFalse); }); test('deleting an unused Meal removes it entirely', () async { final mittag = await meal('Mittag'); await plan.deleteMeal(mittag.id, today); expect((await plan.allMeals()).map((m) => m.name), ['Abend']); }); test('a deleted Recipe stays in the plan as a note with its name', () async { await recipes.saveRecipe(recipe()); final abend = await meal('Abend'); await plan.addEntry(yesterday, abend.id, recipeId: 'r1'); await recipes.deleteRecipe(recipe()); final entry = (await plan.allEntries()).single; expect(entry.recipeId, isNull); expect(entry.note, 'Focaccia'); }); test('"Tauschen" plans another Recipe in the same entry', () async { await recipes.saveRecipe(recipe()); await recipes.saveRecipe(recipe(id: 'r2', name: 'Pizza')); final abend = await meal('Abend'); await plan.addEntry(today, abend.id, recipeId: 'r1'); await plan.replaceRecipe((await plan.allEntries()).single.id, 'r2'); expect((await plan.allEntries()).single.recipeId, 'r2'); }); test('an existing version 2 database gets the Week Plan tables on upgrade', () async { await db.close(); final dir = await Directory.systemTemp.createTemp('cookit_db_'); final file = File('${dir.path}/cookit.sqlite'); var fileDb = testDatabase(file: file); await RecipeRepository(fileDb, photos).saveRecipe(recipe()); await fileDb.customStatement('DROP TABLE plan_entries'); await fileDb.customStatement('DROP TABLE meals'); await fileDb.customStatement('PRAGMA user_version = 2'); await fileDb.close(); fileDb = testDatabase(file: file); expect((await PlanRepository(fileDb).allMeals()).map((m) => m.name), defaultMeals); expect((await RecipeRepository(fileDb, photos).getRecipe('r1'))!.name, 'Focaccia'); await fileDb.close(); await dir.delete(recursive: true); db = testDatabase(); // for tearDown }); }