import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:glp1_companion/core/database/app_database.dart'; import 'package:glp1_companion/features/nutrition/data/repositories/nutrition_repository.dart'; import 'package:glp1_companion/features/nutrition/domain/entities/food_log_entry.dart'; import 'package:glp1_companion/features/nutrition/domain/entities/hydration_log_entry.dart'; import 'package:glp1_companion/features/nutrition/domain/entities/meal_type.dart'; void main() { late AppDatabase database; late NutritionRepository repository; final morning = DateTime(2026, 10, 16, 8); final noon = DateTime(2026, 10, 16, 12); final previousDay = DateTime(2026, 10, 15, 9); setUp(() { database = AppDatabase.forTesting(NativeDatabase.memory()); repository = NutritionRepository(database); }); tearDown(() => database.close()); FoodLogEntry makeFood({ int? id, DateTime? timestamp, MealType mealType = MealType.breakfast, String description = 'Greek yogurt', double proteinGrams = 20, int calories = 150, double? carbsGrams, double? fatGrams, }) => FoodLogEntry( id: id, timestamp: timestamp ?? morning, mealType: mealType, description: description, proteinGrams: proteinGrams, calories: calories, carbsGrams: carbsGrams, fatGrams: fatGrams, ); group('NutritionRepository food diary', () { test('saveFoodLog assigns an id and persists all fields', () async { final saved = await repository.saveFoodLog( makeFood( mealType: MealType.lunch, description: 'Chicken breast bowl', proteinGrams: 42.5, calories: 520, carbsGrams: 45, fatGrams: 12, ), ); expect(saved.id, isNotNull); final fetched = await repository.getFoodLogs(); expect(fetched, hasLength(1)); expect(fetched.single.id, saved.id); expect(fetched.single.mealType, MealType.lunch); expect(fetched.single.description, 'Chicken breast bowl'); expect(fetched.single.proteinGrams, 42.5); expect(fetched.single.calories, 520); expect(fetched.single.carbsGrams, 45); expect(fetched.single.fatGrams, 12); }); test('getFoodLogs returns entries newest first', () async { await repository.saveFoodLog(makeFood(timestamp: previousDay)); await repository.saveFoodLog(makeFood(timestamp: noon)); final fetched = await repository.getFoodLogs(); expect(fetched, hasLength(2)); expect(fetched.first.timestamp, noon); expect(fetched.last.timestamp, previousDay); }); test('getFoodLogs filters by the half-open [from, to) range', () async { await repository.saveFoodLog(makeFood(timestamp: previousDay)); await repository.saveFoodLog(makeFood(timestamp: morning)); await repository.saveFoodLog(makeFood(timestamp: noon)); final dayStart = DateTime(2026, 10, 16); final dayEnd = DateTime(2026, 10, 17); final todays = await repository.getFoodLogs(from: dayStart, to: dayEnd); expect(todays, hasLength(2)); expect( todays.every((entry) => entry.timestamp.isAfter(previousDay)), isTrue, ); }); test('deleteFoodLog removes only the targeted row', () async { final first = await repository.saveFoodLog(makeFood()); await repository.saveFoodLog( makeFood(mealType: MealType.dinner, description: 'Salmon'), ); await repository.deleteFoodLog(first.id!); final remaining = await repository.getFoodLogs(); expect(remaining, hasLength(1)); expect(remaining.single.description, 'Salmon'); }); test('watchFoodLogs emits on every write', () async { final events = >[]; final subscription = repository.watchFoodLogs().listen(events.add); await repository.saveFoodLog(makeFood()); await Future.delayed(const Duration(milliseconds: 50)); expect(events, isNotEmpty); expect(events.last, hasLength(1)); await subscription.cancel(); }); }); group('NutritionRepository hydration', () { test('hydration entries persist and filter by day', () async { await repository.saveHydrationLog( HydrationLogEntry(timestamp: morning, volumeOz: 8), ); await repository.saveHydrationLog( HydrationLogEntry(timestamp: noon, volumeOz: 16), ); await repository.saveHydrationLog( HydrationLogEntry(timestamp: previousDay, volumeOz: 24), ); final dayStart = DateTime(2026, 10, 16); final dayEnd = DateTime(2026, 10, 17); final todays = await repository.getHydrationLogs(from: dayStart, to: dayEnd); expect(todays, hasLength(2)); final totalOz = todays.fold(0, (sum, e) => sum + e.volumeOz); expect(totalOz, 24); await repository.deleteHydrationLog(todays.first.id!); final afterDelete = await repository.getHydrationLogs( from: dayStart, to: dayEnd, ); expect(afterDelete, hasLength(1)); }); }); group('MealType persistence keys', () { test('tryParse round-trips every value', () { for (final type in MealType.values) { expect(MealType.tryParse(type.persistenceKey), type); } expect(MealType.tryParse('brunch'), isNull); expect(MealType.tryParse(null), isNull); }); }); }