90 lines
3.3 KiB
Dart
90 lines
3.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:glp1_companion/features/medication/domain/entities/injection_site.dart';
|
|
import 'package:glp1_companion/features/medication/domain/value_objects/glp1_compound.dart';
|
|
|
|
void main() {
|
|
group('InjectionSite model', () {
|
|
test('exposes exactly twelve anatomical quadrants', () {
|
|
expect(InjectionSite.values, hasLength(12));
|
|
});
|
|
|
|
test('quadrants span three regions with four sites each', () {
|
|
for (final region in InjectionBodyRegion.values) {
|
|
expect(
|
|
InjectionSite.values.where((site) => site.region == region),
|
|
hasLength(4),
|
|
reason: 'region ${region.name} should own four quadrants',
|
|
);
|
|
}
|
|
});
|
|
|
|
test('tryParse round-trips the persistence key', () {
|
|
for (final site in InjectionSite.values) {
|
|
expect(InjectionSite.tryParse(site.persistenceKey), site);
|
|
expect(InjectionSite.tryParse(site.displayName), site);
|
|
}
|
|
expect(InjectionSite.tryParse(null), isNull);
|
|
expect(InjectionSite.tryParse(' '), isNull);
|
|
expect(InjectionSite.tryParse('unknown-quadrant'), isNull);
|
|
});
|
|
});
|
|
|
|
group('InjectionSite.leastRecentlyUsed', () {
|
|
final now = DateTime(2026, 10, 16);
|
|
|
|
test('never-used quadrants win over used ones', () {
|
|
final lastUsed = {
|
|
InjectionSite.abdomenRightUpper: now,
|
|
InjectionSite.thighLeftLateral: now.subtract(const Duration(days: 90)),
|
|
};
|
|
final suggestion = InjectionSite.leastRecentlyUsed(lastUsed);
|
|
expect(
|
|
lastUsed.containsKey(suggestion),
|
|
isFalse,
|
|
reason: 'suggested site must be one that was never used',
|
|
);
|
|
});
|
|
|
|
test('with full history the oldest site is chosen', () {
|
|
final lastUsed = {
|
|
for (var i = 0; i < InjectionSite.values.length; i++)
|
|
InjectionSite.values[i]: now.subtract(Duration(days: i)),
|
|
};
|
|
expect(
|
|
InjectionSite.leastRecentlyUsed(lastUsed),
|
|
InjectionSite.values.last,
|
|
);
|
|
});
|
|
|
|
test('ties resolve deterministically to declaration order', () {
|
|
final lastUsed = {
|
|
for (final site in InjectionSite.values) site: now,
|
|
};
|
|
expect(InjectionSite.leastRecentlyUsed(lastUsed), InjectionSite.values.first);
|
|
|
|
expect(InjectionSite.leastRecentlyUsed(const {}), InjectionSite.values.first);
|
|
});
|
|
});
|
|
|
|
group('Glp1Compound', () {
|
|
test('models the three supported agonists with published half-lives', () {
|
|
expect(Glp1Compound.semaglutide.halfLifeHours, 168);
|
|
expect(Glp1Compound.tirzepatide.halfLifeHours, 120);
|
|
expect(Glp1Compound.liraglutide.halfLifeHours, 13);
|
|
});
|
|
|
|
test('tryParse resolves generic names, brands, and free text', () {
|
|
expect(Glp1Compound.tryParse('semaglutide'), Glp1Compound.semaglutide);
|
|
expect(Glp1Compound.tryParse('OZEMPIC'), Glp1Compound.semaglutide);
|
|
expect(Glp1Compound.tryParse('Mounjaro'), Glp1Compound.tirzepatide);
|
|
expect(Glp1Compound.tryParse('Zepbound'), Glp1Compound.tirzepatide);
|
|
expect(Glp1Compound.tryParse('Saxenda'), Glp1Compound.liraglutide);
|
|
expect(
|
|
Glp1Compound.tryParse('Semaglutide (compounded)'),
|
|
Glp1Compound.semaglutide,
|
|
);
|
|
expect(Glp1Compound.tryParse('unknown'), isNull);
|
|
expect(Glp1Compound.tryParse(null), isNull);
|
|
});
|
|
});
|
|
} |