Initial commit: CookIT recipe collection and week planner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CedricandClaude Opus 5.5 committed 2026-10-28 22:55:09 +01:00
commit 7d7a0bc60b
173 files changed
+22803

No files matched your search

+126
View File
@@ -0,0 +1,126 @@
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> 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
});
}