import 'package:flutter_test/flutter_test.dart'; import 'package:glp1_companion/features/medication/domain/entities/dose_entry.dart'; import 'package:glp1_companion/features/medication/domain/usecases/calculate_serum_level.dart'; DoseEntry _dose({ required String compound, required double mg, required DateTime administeredAt, }) => DoseEntry( compoundName: compound, doseMg: mg, administeredAt: administeredAt, ); void main() { const calculator = CalculateSerumLevel(); final now = DateTime(2026, 10, 16, 12); group('CalculateSerumLevel', () { test('empty history yields zero concentration', () { expect(calculator.evaluate(doses: const [], at: now), 0); }); test('a fresh dose contributes its full amount', () { final doses = [ _dose(compound: 'Semaglutide', mg: 1, administeredAt: now), ]; expect(calculator.evaluate(doses: doses, at: now), closeTo(1, 1e-9)); }); test('dose halves exactly after one half-life', () { final doses = [ _dose( compound: 'Semaglutide', mg: 2, administeredAt: now.subtract(const Duration(hours: 168)), ), ]; expect(calculator.evaluate(doses: doses, at: now), closeTo(1, 1e-9)); }); test('follows the published half-life per compound', () { // Tirzepatide: 2.5 mg after 120 h (one half-life) -> 1.25 mg. final tirzepatide = [ _dose( compound: 'Tirzepatide', mg: 2.5, administeredAt: now.subtract(const Duration(hours: 120)), ), ]; expect(calculator.evaluate(doses: tirzepatide, at: now), closeTo(1.25, 1e-9)); // Liraglutide: 1.8 mg after 13 h (one half-life) -> 0.9 mg. final liraglutide = [ _dose( compound: 'Liraglutide', mg: 1.8, administeredAt: now.subtract(const Duration(hours: 13)), ), ]; expect(calculator.evaluate(doses: liraglutide, at: now), closeTo(0.9, 1e-9)); }); test('accumulates contributions across multiple doses', () { final doses = [ _dose( compound: 'Semaglutide', mg: 1, administeredAt: now.subtract(const Duration(hours: 168)), ), _dose( compound: 'Semaglutide', mg: 1, administeredAt: now.subtract(const Duration(hours: 336)), ), ]; // 1 * 0.5 + 1 * 0.25 = 0.75 mg. expect(calculator.evaluate(doses: doses, at: now), closeTo(0.75, 1e-9)); }); test('ignores doses timestamped in the future', () { final doses = [ _dose(compound: 'Semaglutide', mg: 1, administeredAt: now), _dose( compound: 'Semaglutide', mg: 5, administeredAt: now.add(const Duration(hours: 24)), ), ]; expect(calculator.evaluate(doses: doses, at: now), closeTo(1, 1e-9)); }); test('unknown compounds fall back to the default half-life', () { final doses = [ _dose( compound: 'Compounded GLP-1', mg: 4, administeredAt: now.subtract(const Duration(hours: 168)), ), ]; expect(calculator.evaluate(doses: doses, at: now), closeTo(2, 1e-9)); }); test('brand names resolve to their compound half-life', () { // "Ozempic" is semaglutide (168 h): 4 mg after 168 h -> 2 mg. final doses = [ _dose( compound: 'Ozempic', mg: 4, administeredAt: now.subtract(const Duration(hours: 168)), ), ]; expect(calculator.evaluate(doses: doses, at: now), closeTo(2, 1e-9)); }); test('call() mirrors evaluate()', () { final doses = [ _dose(compound: 'Tirzepatide', mg: 3, administeredAt: now), ]; expect( calculator(doses: doses, at: now), calculator.evaluate(doses: doses, at: now), ); }); test('halfLifeHours override forces a single half-life', () { final doses = [ _dose(compound: 'Tirzepatide', mg: 2, administeredAt: now), ]; expect( calculator.evaluate( doses: doses, at: now.add(const Duration(hours: 20)), halfLifeHours: 20, ), closeTo(1, 1e-9), ); }); }); }