141 lines
4.2 KiB
Dart
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),
|
|
);
|
|
});
|
|
});
|
|
} |