glp-companion/test/features/medication/domain/calculate_serum_level_test....

141 lines
4.2 KiB
Dart

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),
);
});
});
}