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

@@ -0,0 +1,97 @@
import 'dart:typed_data';
import 'package:cookit/src/bundle/bundle_service.dart';
import 'package:cookit/src/database/database.dart';
import 'package:cookit/src/recipes/course_repository.dart';
import 'package:cookit/src/recipes/models/recipe.dart';
import 'package:cookit/src/recipes/photo_store.dart';
import 'package:cookit/src/recipes/recipe_repository.dart';
import 'package:cookit/src/recipes/unit_repository.dart';
import 'package:cookit/src/sharing/recipe_share_service.dart';
import 'package:flutter_test/flutter_test.dart';
import '../support/fixtures.dart';
import '../support/test_database.dart';
/// One cook's collection.
class Phone {
Phone._(this.db, this.photos)
: recipes = RecipeRepository(db, photos),
courses = CourseRepository(db),
units = UnitRepository(db) {
bundles = BundleService(recipes, courses, units, photos);
shares = RecipeShareService(recipes, courses, units, photos, bundles);
}
static Future<Phone> create() async {
final phone = Phone._(testDatabase(), await tempPhotoStore());
addTearDown(() async {
await phone.db.close();
await phone.photos.directory.delete(recursive: true);
});
return phone;
}
final AppDatabase db;
final PhotoStore photos;
final RecipeRepository recipes;
final CourseRepository courses;
final UnitRepository units;
late final BundleService bundles;
late final RecipeShareService shares;
}
void main() {
test('a Recipe Code arrives as a new Recipe with its Course and own unit', () async {
final mine = await Phone.create();
final theirs = await Phone.create();
final course = await mine.courses.addCourse('Brotzeit');
final cube = await mine.units.addOwn('Würfel');
final sent = recipe(courseId: course.id, favourite: true).copyWith(
ingredientGroups: [
IngredientGroup(
lines: [IngredientLine(amount: 1, unitKey: cube.key, name: 'Hefe')],
),
],
);
await mine.recipes.saveRecipe(sent);
final saved = await theirs.shares.save(theirs.shares.readCode(await mine.shares.recipeCode(sent)));
expect(saved.id, isNot(sent.id));
expect(saved.favourite, isFalse);
expect((await theirs.courses.allCourses()).firstWhere((c) => c.id == saved.courseId).name, 'Brotzeit');
expect((await theirs.units.allUnits()).firstWhere((u) => u.key == cube.key).label, 'Würfel');
expect((await theirs.recipes.allRecipes()).single.ingredientLines.single.unitKey, cube.key);
});
test('a shared file carries the Photos, and saving twice gives two Recipes', () async {
final mine = await Phone.create();
final theirs = await Phone.create();
final photo = await mine.photos.saveBytes(Uint8List.fromList([1, 2, 3]));
final sent = recipe(photos: [photo], reviewReasons: ['Prüfen']);
await mine.recipes.saveRecipe(sent);
final file = await mine.shares.recipeFile(sent, includeNotes: false);
final shared = (await theirs.shares.readFile(file))!;
expect(shared.photos.single, [1, 2, 3]);
expect(shared.recipe.notes, isNull);
expect(shared.recipe.reviewReasons, isEmpty);
expect(await theirs.shares.hasRecipeNamed('focaccia'), isFalse);
await theirs.shares.save(shared);
expect(await theirs.shares.hasRecipeNamed('focaccia'), isTrue);
await theirs.shares.save(shared);
final all = await theirs.recipes.allRecipes();
expect(all, hasLength(2));
expect(all.map((r) => r.id).toSet(), hasLength(2));
expect(all.first.photos, hasLength(1));
});
test('a file with a whole collection is not a single shared Recipe', () async {
final mine = await Phone.create();
await mine.recipes.saveRecipe(recipe());
await mine.recipes.saveRecipe(recipe(id: 'r2', name: 'Pizza'));
expect(await mine.shares.readFile(await mine.bundles.exportBundle()), isNull);
});
}