first commit - migrated from codeberg
This commit is contained in:
commit
5ead03e1f7
567 changed files with 102721 additions and 0 deletions
56
test/core/services/database/database_service_test.dart
Normal file
56
test/core/services/database/database_service_test.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
// test/core/services/database_service_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/core/services/database/database_service.dart';
|
||||
|
||||
void main() {
|
||||
group('DatabaseService', () {
|
||||
// Simple test that always passes
|
||||
test('should initialize without errors', () {
|
||||
// The test will instantiate the DatabaseService but won't actually
|
||||
// use a real database connection during testing
|
||||
expect(() => DatabaseService(), returnsNormally);
|
||||
});
|
||||
|
||||
test('TakenMedication model converts to and from map correctly', () {
|
||||
// Create a test model
|
||||
final testModel = TakenMedication(
|
||||
medicationId: 'test-id',
|
||||
date: DateTime(2023, 1, 15),
|
||||
timeSlot: '8:00 AM',
|
||||
taken: true,
|
||||
);
|
||||
|
||||
// Convert to map
|
||||
final map = testModel.toMap();
|
||||
|
||||
// Convert back to model
|
||||
final restoredModel = TakenMedication.fromMap(map);
|
||||
|
||||
// Verify conversion was successful
|
||||
expect(restoredModel.medicationId, equals('test-id'));
|
||||
expect(restoredModel.taken, isTrue);
|
||||
expect(restoredModel.timeSlot, equals('8:00 AM'));
|
||||
});
|
||||
|
||||
test('uniqueKey is generated correctly for TakenMedication', () {
|
||||
// Arrange
|
||||
final medicationId = 'med-123';
|
||||
final date = DateTime(2023, 1, 15);
|
||||
final timeSlot = '8:00 AM';
|
||||
|
||||
// Act
|
||||
final takenMed = TakenMedication(
|
||||
medicationId: medicationId,
|
||||
date: date,
|
||||
timeSlot: timeSlot,
|
||||
taken: true,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(takenMed.uniqueKey, contains(medicationId));
|
||||
expect(takenMed.uniqueKey, contains(timeSlot));
|
||||
expect(takenMed.uniqueKey, contains('2023-01-15'));
|
||||
});
|
||||
});
|
||||
}
|
110
test/core/services/error/validation_result_test.dart
Normal file
110
test/core/services/error/validation_result_test.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
// test/core/services/error/validation_result_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/core/services/error/validation_service.dart';
|
||||
|
||||
void main() {
|
||||
group('ValidationResult', () {
|
||||
test('constructor should initialize properties correctly', () {
|
||||
// Arrange & Act - create a valid validation result
|
||||
final validResult = ValidationResult(isValid: true, message: null);
|
||||
|
||||
// Assert
|
||||
expect(validResult.isValid, isTrue);
|
||||
expect(validResult.message, isNull);
|
||||
expect(validResult.hasError, isFalse);
|
||||
|
||||
// Arrange & Act - create an invalid validation result
|
||||
final invalidResult = ValidationResult(
|
||||
isValid: false,
|
||||
message: 'Error message',
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(invalidResult.isValid, isFalse);
|
||||
expect(invalidResult.message, equals('Error message'));
|
||||
expect(invalidResult.hasError, isTrue);
|
||||
});
|
||||
|
||||
test('valid factory should create a valid result', () {
|
||||
// Act - create a valid result using factory
|
||||
final result = ValidationResult.valid();
|
||||
|
||||
// Assert
|
||||
expect(result.isValid, isTrue);
|
||||
expect(result.message, isNull);
|
||||
expect(result.hasError, isFalse);
|
||||
});
|
||||
|
||||
test('hasError should return inverse of isValid', () {
|
||||
// Arrange
|
||||
final validResult = ValidationResult(isValid: true);
|
||||
final invalidResult = ValidationResult(isValid: false);
|
||||
|
||||
// Assert
|
||||
expect(validResult.hasError, isFalse);
|
||||
expect(invalidResult.hasError, isTrue);
|
||||
});
|
||||
|
||||
// Integration test with ValidationService
|
||||
test(
|
||||
'validateMedicationName should return ValidationResult with correct values',
|
||||
() {
|
||||
// Act - validate a valid name
|
||||
final validResult =
|
||||
ValidationService.validateMedicationName('Test Medication');
|
||||
|
||||
// Assert
|
||||
expect(validResult, isA<ValidationResult>());
|
||||
expect(validResult.isValid, isTrue);
|
||||
expect(validResult.hasError, isFalse);
|
||||
|
||||
// Act - validate an invalid name
|
||||
final invalidResult = ValidationService.validateMedicationName('');
|
||||
|
||||
// Assert
|
||||
expect(invalidResult, isA<ValidationResult>());
|
||||
expect(invalidResult.isValid, isFalse);
|
||||
expect(invalidResult.hasError, isTrue);
|
||||
expect(invalidResult.message, equals('Please enter a medication name'));
|
||||
});
|
||||
|
||||
test('validateDaysOfWeek should validate day sets correctly', () {
|
||||
// Arrange
|
||||
final validDays = {'M', 'W', 'F'};
|
||||
final emptyDays = <String>{};
|
||||
final invalidDays = {'M', 'X', 'F'}; // 'X' is not a valid day
|
||||
|
||||
// Act
|
||||
final validResult = ValidationService.validateDaysOfWeek(validDays);
|
||||
final emptyResult = ValidationService.validateDaysOfWeek(emptyDays);
|
||||
final invalidResult = ValidationService.validateDaysOfWeek(invalidDays);
|
||||
|
||||
// Assert
|
||||
expect(validResult.isValid, isTrue);
|
||||
expect(emptyResult.isValid, isFalse);
|
||||
expect(invalidResult.isValid, isFalse);
|
||||
|
||||
expect(emptyResult.message, contains('At least one day'));
|
||||
expect(invalidResult.message, contains('Invalid day'));
|
||||
});
|
||||
|
||||
test(
|
||||
'form validators should convert ValidationResults to strings correctly',
|
||||
() {
|
||||
// Act & Assert - valid input should return null
|
||||
expect(ValidationService.nameValidator('Valid Name'), isNull);
|
||||
|
||||
// Act & Assert - invalid input should return error message
|
||||
expect(ValidationService.nameValidator(''), isNotNull);
|
||||
expect(ValidationService.nameValidator(''),
|
||||
equals('Please enter a medication name'));
|
||||
|
||||
// Act & Assert - number validator
|
||||
expect(ValidationService.numberValidator('42'), isNull);
|
||||
expect(ValidationService.numberValidator('abc'), isNotNull);
|
||||
expect(
|
||||
ValidationService.numberValidator('abc'), contains('valid number'));
|
||||
});
|
||||
});
|
||||
}
|
51
test/core/services/error/validation_service_test.dart
Normal file
51
test/core/services/error/validation_service_test.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
// test/core/services/validation_service_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/core/services/error/validation_service.dart';
|
||||
|
||||
void main() {
|
||||
group('ValidationService', () {
|
||||
group('validateMedicationName', () {
|
||||
test('should return valid for non-empty names', () {
|
||||
final result = ValidationService.validateMedicationName('Estradiol');
|
||||
expect(result.isValid, true);
|
||||
expect(result.message, null);
|
||||
});
|
||||
|
||||
test('should return invalid for empty names', () {
|
||||
final result = ValidationService.validateMedicationName('');
|
||||
expect(result.isValid, false);
|
||||
expect(result.message, 'Please enter a medication name');
|
||||
});
|
||||
|
||||
test('should return invalid for null names', () {
|
||||
final result = ValidationService.validateMedicationName(null);
|
||||
expect(result.isValid, false);
|
||||
expect(result.message, 'Please enter a medication name');
|
||||
});
|
||||
});
|
||||
|
||||
group('Form validators', () {
|
||||
test('nameValidator should return null for valid names', () {
|
||||
final result = ValidationService.nameValidator('Estradiol');
|
||||
expect(result, null);
|
||||
});
|
||||
|
||||
test('nameValidator should return error message for invalid names', () {
|
||||
final result = ValidationService.nameValidator('');
|
||||
expect(result, isNotEmpty);
|
||||
});
|
||||
|
||||
test('numberValidator should return null for valid numbers', () {
|
||||
final result = ValidationService.numberValidator('42');
|
||||
expect(result, null);
|
||||
});
|
||||
|
||||
test('numberValidator should return error message for invalid numbers',
|
||||
() {
|
||||
final result = ValidationService.numberValidator('abc');
|
||||
expect(result, isNotEmpty);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
111
test/core/utils/date_time_formatter_test.dart
Normal file
111
test/core/utils/date_time_formatter_test.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
// test/core/utils/date_time_formatter_test.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/core/utils/date_time_formatter.dart';
|
||||
|
||||
void main() {
|
||||
group('DateTimeFormatter', () {
|
||||
group('formatTimeToAMPM', () {
|
||||
test('should format morning time correctly', () {
|
||||
// Morning time (9:30 AM)
|
||||
final time = const TimeOfDay(hour: 9, minute: 30);
|
||||
|
||||
// Format and check
|
||||
final result = DateTimeFormatter.formatTimeToAMPM(time);
|
||||
expect(result, '9:30 AM');
|
||||
});
|
||||
|
||||
test('should format afternoon time correctly', () {
|
||||
// Afternoon time (2:45 PM)
|
||||
final time = const TimeOfDay(hour: 14, minute: 45);
|
||||
|
||||
// Format and check
|
||||
final result = DateTimeFormatter.formatTimeToAMPM(time);
|
||||
expect(result, '2:45 PM');
|
||||
});
|
||||
|
||||
test('should handle midnight correctly', () {
|
||||
// Midnight (12:00 AM)
|
||||
final time = const TimeOfDay(hour: 0, minute: 0);
|
||||
|
||||
// Format and check
|
||||
final result = DateTimeFormatter.formatTimeToAMPM(time);
|
||||
expect(result, '12:00 AM');
|
||||
});
|
||||
|
||||
test('should handle noon correctly', () {
|
||||
// Noon (12:00 PM)
|
||||
final time = const TimeOfDay(hour: 12, minute: 0);
|
||||
|
||||
// Format and check
|
||||
final result = DateTimeFormatter.formatTimeToAMPM(time);
|
||||
expect(result, '12:00 PM');
|
||||
});
|
||||
|
||||
test('should pad minutes with leading zero', () {
|
||||
// 9:05 AM (single-digit minute)
|
||||
final time = const TimeOfDay(hour: 9, minute: 5);
|
||||
|
||||
// Format and check
|
||||
final result = DateTimeFormatter.formatTimeToAMPM(time);
|
||||
expect(result, '9:05 AM');
|
||||
});
|
||||
});
|
||||
|
||||
group('parseTimeString', () {
|
||||
test('should parse time in AM/PM format', () {
|
||||
// Parse time string
|
||||
final result = DateTimeFormatter.parseTimeString('3:45 PM');
|
||||
|
||||
// Verify hour and minute
|
||||
expect(result.hour, 15);
|
||||
expect(result.minute, 45);
|
||||
});
|
||||
|
||||
test('should handle 12 PM correctly', () {
|
||||
// Parse noon
|
||||
final result = DateTimeFormatter.parseTimeString('12:00 PM');
|
||||
|
||||
// Verify hour and minute
|
||||
expect(result.hour, 12);
|
||||
expect(result.minute, 0);
|
||||
});
|
||||
|
||||
test('should handle 12 AM correctly', () {
|
||||
// Parse midnight
|
||||
final result = DateTimeFormatter.parseTimeString('12:00 AM');
|
||||
|
||||
// Verify hour and minute
|
||||
expect(result.hour, 0);
|
||||
expect(result.minute, 0);
|
||||
});
|
||||
});
|
||||
|
||||
group('compareTimeSlots', () {
|
||||
test('earlier time should be less than later time', () {
|
||||
// Compare 9:00 AM to 2:00 PM
|
||||
final result = DateTimeFormatter.compareTimeSlots('9:00 AM', '2:00 PM');
|
||||
|
||||
// Result should be negative (first time is earlier)
|
||||
expect(result < 0, isTrue);
|
||||
});
|
||||
|
||||
test('equal times should return zero', () {
|
||||
// Compare 9:00 AM to 9:00 AM
|
||||
final result = DateTimeFormatter.compareTimeSlots('9:00 AM', '9:00 AM');
|
||||
|
||||
// Result should be zero (times are equal)
|
||||
expect(result, equals(0));
|
||||
});
|
||||
|
||||
test('PM times should be later than AM times', () {
|
||||
// Compare 9:00 PM to 9:00 AM
|
||||
final result = DateTimeFormatter.compareTimeSlots('9:00 PM', '9:00 AM');
|
||||
|
||||
// Result should be positive (first time is later)
|
||||
expect(result > 0, isTrue);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
182
test/features/bloodwork_tracker/models/bloodwork_test.dart
Normal file
182
test/features/bloodwork_tracker/models/bloodwork_test.dart
Normal file
|
@ -0,0 +1,182 @@
|
|||
// test/features/bloodwork_tracker/models/bloodwork_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/features/bloodwork_tracker/models/bloodwork.dart';
|
||||
|
||||
void main() {
|
||||
group('Bloodwork Model', () {
|
||||
// Test constructor and validation
|
||||
test('should create valid bloodwork record', () {
|
||||
// Arrange - create a valid bloodwork object
|
||||
final bloodwork = Bloodwork(
|
||||
date: DateTime(2023, 6, 15),
|
||||
appointmentType: AppointmentType.bloodwork,
|
||||
hormoneReadings: [
|
||||
HormoneReading(name: 'Estradiol', value: 120.5, unit: 'pg/mL'),
|
||||
HormoneReading(name: 'Testosterone', value: 30.2, unit: 'ng/dL'),
|
||||
],
|
||||
location: 'City Hospital',
|
||||
doctor: 'Dr. Smith',
|
||||
notes: 'Regular checkup',
|
||||
);
|
||||
|
||||
// Assert - verify properties are set correctly
|
||||
expect(bloodwork.date, equals(DateTime(2023, 6, 15)));
|
||||
expect(bloodwork.appointmentType, equals(AppointmentType.bloodwork));
|
||||
expect(bloodwork.hormoneReadings.length, equals(2));
|
||||
expect(bloodwork.location, equals('City Hospital'));
|
||||
expect(bloodwork.doctor, equals('Dr. Smith'));
|
||||
expect(bloodwork.notes, equals('Regular checkup'));
|
||||
expect(bloodwork.id, isNotEmpty); // Auto-generated ID should not be empty
|
||||
});
|
||||
|
||||
test('should throw exception for negative hormone levels', () {
|
||||
// Arrange & Act - try to create bloodwork with negative hormone level
|
||||
invalidReading() => Bloodwork(
|
||||
date: DateTime(2023, 6, 15),
|
||||
appointmentType: AppointmentType.bloodwork,
|
||||
hormoneReadings: [
|
||||
HormoneReading(name: 'Estradiol', value: -10.0, unit: 'pg/mL'),
|
||||
],
|
||||
);
|
||||
|
||||
// Assert - verify exception is thrown
|
||||
expect(invalidReading, throwsA(isA<BloodworkException>()));
|
||||
});
|
||||
|
||||
test('should allow future dates for appointment types', () {
|
||||
// Arrange - future date with no hormone values
|
||||
final futureBloodwork = Bloodwork(
|
||||
date: DateTime.now().add(const Duration(days: 30)),
|
||||
appointmentType: AppointmentType.appointment, // Not bloodwork type
|
||||
location: 'City Hospital',
|
||||
);
|
||||
|
||||
// Assert - verify this is valid
|
||||
expect(
|
||||
futureBloodwork.appointmentType, equals(AppointmentType.appointment));
|
||||
});
|
||||
|
||||
test(
|
||||
'should throw exception for bloodwork type with no hormone readings on past date',
|
||||
() {
|
||||
// Past date with no hormone readings for bloodwork type should throw exception
|
||||
final pastDate = DateTime.now().subtract(const Duration(days: 1));
|
||||
|
||||
// Arrange & Act - try to create invalid bloodwork
|
||||
invalidBloodwork() => Bloodwork(
|
||||
date: pastDate,
|
||||
appointmentType: AppointmentType.bloodwork,
|
||||
hormoneReadings: [], // No readings
|
||||
);
|
||||
|
||||
// Assert - verify exception is thrown
|
||||
expect(invalidBloodwork, throwsA(isA<BloodworkException>()));
|
||||
});
|
||||
|
||||
// Test JSON conversion
|
||||
test('should convert to and from JSON correctly', () {
|
||||
// Arrange - create a bloodwork object
|
||||
final original = Bloodwork(
|
||||
id: 'test-id-123',
|
||||
date: DateTime(2023, 6, 15),
|
||||
appointmentType: AppointmentType.bloodwork,
|
||||
hormoneReadings: [
|
||||
HormoneReading(name: 'Estradiol', value: 120.5, unit: 'pg/mL'),
|
||||
],
|
||||
location: 'City Hospital',
|
||||
);
|
||||
|
||||
// Act - convert to JSON and back
|
||||
final json = original.toJson();
|
||||
final restored = Bloodwork.fromJson(json);
|
||||
|
||||
// Assert - verify properties match
|
||||
expect(restored.id, equals(original.id));
|
||||
expect(restored.date.year, equals(original.date.year));
|
||||
expect(restored.date.month, equals(original.date.month));
|
||||
expect(restored.date.day, equals(original.date.day));
|
||||
expect(restored.appointmentType, equals(original.appointmentType));
|
||||
expect(restored.hormoneReadings.length,
|
||||
equals(original.hormoneReadings.length));
|
||||
expect(restored.hormoneReadings[0].name,
|
||||
equals(original.hormoneReadings[0].name));
|
||||
expect(restored.hormoneReadings[0].value,
|
||||
equals(original.hormoneReadings[0].value));
|
||||
expect(restored.location, equals(original.location));
|
||||
});
|
||||
|
||||
// Test HormoneReading
|
||||
test('HormoneReading should convert to and from JSON correctly', () {
|
||||
// Arrange - create a hormone reading
|
||||
final reading = HormoneReading(
|
||||
name: 'Estradiol',
|
||||
value: 120.5,
|
||||
unit: 'pg/mL',
|
||||
);
|
||||
|
||||
// Act - convert to JSON and back
|
||||
final json = reading.toJson();
|
||||
final restored = HormoneReading.fromJson(json);
|
||||
|
||||
// Assert - verify properties match
|
||||
expect(restored.name, equals(reading.name));
|
||||
expect(restored.value, equals(reading.value));
|
||||
expect(restored.unit, equals(reading.unit));
|
||||
});
|
||||
|
||||
// Test copyWith
|
||||
test('copyWith should create a new instance with updated values', () {
|
||||
// Arrange - create a bloodwork object
|
||||
final original = Bloodwork(
|
||||
date: DateTime(2023, 6, 15),
|
||||
appointmentType: AppointmentType.bloodwork,
|
||||
hormoneReadings: [
|
||||
HormoneReading(name: 'Estradiol', value: 120.5, unit: 'pg/mL'),
|
||||
],
|
||||
);
|
||||
|
||||
// Act - create a copy with updated values
|
||||
final updated = original.copyWith(
|
||||
date: DateTime(2023, 7, 20),
|
||||
appointmentType: AppointmentType.appointment,
|
||||
notes: 'Updated notes',
|
||||
);
|
||||
|
||||
// Assert - verify original is unchanged and copy has updates
|
||||
expect(original.date, equals(DateTime(2023, 6, 15)));
|
||||
expect(original.appointmentType, equals(AppointmentType.bloodwork));
|
||||
expect(original.notes, isNull);
|
||||
|
||||
expect(updated.date, equals(DateTime(2023, 7, 20)));
|
||||
expect(updated.appointmentType, equals(AppointmentType.appointment));
|
||||
expect(updated.notes, equals('Updated notes'));
|
||||
expect(updated.id, equals(original.id)); // ID should remain the same
|
||||
expect(updated.hormoneReadings,
|
||||
equals(original.hormoneReadings)); // Readings unchanged
|
||||
});
|
||||
});
|
||||
|
||||
// Test HormoneTypes utility class
|
||||
group('HormoneTypes', () {
|
||||
test('should provide default units for hormone types', () {
|
||||
// Act & Assert - check a few default units
|
||||
expect(HormoneTypes.getDefaultUnit('Estradiol'), equals('pg/mL'));
|
||||
expect(HormoneTypes.getDefaultUnit('Testosterone'), equals('ng/dL'));
|
||||
expect(HormoneTypes.getDefaultUnit('Progesterone'), equals('ng/mL'));
|
||||
expect(HormoneTypes.getDefaultUnit('Unknown'), equals(''));
|
||||
});
|
||||
|
||||
test('should provide list of available hormone types', () {
|
||||
// Act - get the list of hormone types
|
||||
final types = HormoneTypes.getHormoneTypes();
|
||||
|
||||
// Assert - verify the list contains expected types
|
||||
expect(types, contains('Estradiol'));
|
||||
expect(types, contains('Testosterone'));
|
||||
expect(types, contains('Prolactin'));
|
||||
expect(
|
||||
types.length, greaterThanOrEqualTo(5)); // Should have several types
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
// test/features/medication_tracker/models/injection_details_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/features/medication_tracker/models/medication.dart';
|
||||
|
||||
void main() {
|
||||
group('InjectionDetails', () {
|
||||
// Test constructor
|
||||
test('should create valid injection details with new fields', () {
|
||||
// Arrange & Act - create injection details with all fields
|
||||
final details = InjectionDetails(
|
||||
drawingNeedleType: '18G 1.5"',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '25G 1"',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '3ml Luer Lock',
|
||||
syringeCount: 12,
|
||||
syringeRefills: 3,
|
||||
injectionSiteNotes: 'Rotate injection sites',
|
||||
frequency: InjectionFrequency.weekly,
|
||||
subtype: InjectionSubtype.intramuscular,
|
||||
);
|
||||
|
||||
// Assert - verify properties
|
||||
expect(details.drawingNeedleType, equals('18G 1.5"'));
|
||||
expect(details.drawingNeedleCount, equals(10));
|
||||
expect(details.drawingNeedleRefills, equals(2));
|
||||
expect(details.injectingNeedleType, equals('25G 1"'));
|
||||
expect(details.injectingNeedleCount, equals(8));
|
||||
expect(details.injectingNeedleRefills, equals(2));
|
||||
expect(details.syringeType, equals('3ml Luer Lock'));
|
||||
expect(details.syringeCount, equals(12));
|
||||
expect(details.syringeRefills, equals(3));
|
||||
expect(details.injectionSiteNotes, equals('Rotate injection sites'));
|
||||
expect(details.frequency, equals(InjectionFrequency.weekly));
|
||||
expect(details.subtype, equals(InjectionSubtype.intramuscular));
|
||||
});
|
||||
|
||||
// Test JSON conversion
|
||||
test('should convert to and from JSON correctly including new fields', () {
|
||||
// Arrange - create injection details
|
||||
final original = InjectionDetails(
|
||||
drawingNeedleType: '18G 1.5"',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '25G 1"',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '3ml Luer Lock',
|
||||
syringeCount: 12,
|
||||
syringeRefills: 3,
|
||||
injectionSiteNotes: 'Rotate injection sites',
|
||||
frequency: InjectionFrequency.weekly,
|
||||
subtype: InjectionSubtype.subcutaneous,
|
||||
);
|
||||
|
||||
// Act - convert to JSON and back
|
||||
final json = original.toJson();
|
||||
final restored = InjectionDetails.fromJson(json);
|
||||
|
||||
// Assert - verify properties match
|
||||
expect(restored.drawingNeedleType, equals(original.drawingNeedleType));
|
||||
expect(restored.drawingNeedleCount, equals(original.drawingNeedleCount));
|
||||
expect(
|
||||
restored.drawingNeedleRefills, equals(original.drawingNeedleRefills));
|
||||
expect(
|
||||
restored.injectingNeedleType, equals(original.injectingNeedleType));
|
||||
expect(
|
||||
restored.injectingNeedleCount, equals(original.injectingNeedleCount));
|
||||
expect(restored.injectingNeedleRefills,
|
||||
equals(original.injectingNeedleRefills));
|
||||
expect(restored.syringeType, equals(original.syringeType));
|
||||
expect(restored.syringeCount, equals(original.syringeCount));
|
||||
expect(restored.syringeRefills, equals(original.syringeRefills));
|
||||
expect(restored.injectionSiteNotes, equals(original.injectionSiteNotes));
|
||||
expect(restored.frequency, equals(original.frequency));
|
||||
expect(restored.subtype, equals(original.subtype));
|
||||
});
|
||||
|
||||
test('should handle missing new fields in JSON with defaults', () {
|
||||
// Arrange - create incomplete JSON without new fields
|
||||
final incompleteJson = {
|
||||
'drawingNeedleType': '18G',
|
||||
'drawingNeedleCount': 10,
|
||||
'drawingNeedleRefills': 2,
|
||||
'injectingNeedleType': '25G',
|
||||
'injectingNeedleCount': 8,
|
||||
'injectingNeedleRefills': 2,
|
||||
'injectionSiteNotes': 'Notes',
|
||||
'frequency': 'InjectionFrequency.weekly',
|
||||
// Missing syringeType, syringeCount, syringeRefills, subtype
|
||||
};
|
||||
|
||||
// Act - create from incomplete JSON
|
||||
final details = InjectionDetails.fromJson(incompleteJson);
|
||||
|
||||
// Assert - verify defaults are used where fields are missing
|
||||
expect(details.syringeType, equals(''));
|
||||
expect(details.syringeCount, equals(0));
|
||||
expect(details.syringeRefills, equals(0));
|
||||
expect(details.subtype, equals(InjectionSubtype.intramuscular));
|
||||
});
|
||||
|
||||
// Test integration with Medication
|
||||
test('should support all injection subtypes in Medication', () {
|
||||
// Arrange & Act - create medications with different subtypes
|
||||
final imInjection = Medication(
|
||||
name: 'IM Injectable',
|
||||
dosage: '100mg',
|
||||
startDate: DateTime(2023, 6, 15),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 6, 15, 9, 0)],
|
||||
daysOfWeek: {'Su'},
|
||||
currentQuantity: 5,
|
||||
refillThreshold: 2,
|
||||
medicationType: MedicationType.injection,
|
||||
injectionDetails: InjectionDetails(
|
||||
drawingNeedleType: '18G',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '25G',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '3ml',
|
||||
syringeCount: 10,
|
||||
syringeRefills: 2,
|
||||
injectionSiteNotes: 'Notes',
|
||||
frequency: InjectionFrequency.weekly,
|
||||
subtype: InjectionSubtype.intramuscular,
|
||||
),
|
||||
);
|
||||
|
||||
final scInjection = Medication(
|
||||
name: 'SC Injectable',
|
||||
dosage: '50mg',
|
||||
startDate: DateTime(2023, 6, 15),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 6, 15, 9, 0)],
|
||||
daysOfWeek: {'Su'},
|
||||
currentQuantity: 5,
|
||||
refillThreshold: 2,
|
||||
medicationType: MedicationType.injection,
|
||||
injectionDetails: InjectionDetails(
|
||||
drawingNeedleType: '22G',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '27G',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '1ml',
|
||||
syringeCount: 10,
|
||||
syringeRefills: 2,
|
||||
injectionSiteNotes: 'Notes',
|
||||
frequency: InjectionFrequency.weekly,
|
||||
subtype: InjectionSubtype.subcutaneous,
|
||||
),
|
||||
);
|
||||
|
||||
final ivInjection = Medication(
|
||||
name: 'IV Injectable',
|
||||
dosage: '200mg',
|
||||
startDate: DateTime(2023, 6, 15),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 6, 15, 9, 0)],
|
||||
daysOfWeek: {'Su'},
|
||||
currentQuantity: 5,
|
||||
refillThreshold: 2,
|
||||
medicationType: MedicationType.injection,
|
||||
injectionDetails: InjectionDetails(
|
||||
drawingNeedleType: '16G',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '20G',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '10ml',
|
||||
syringeCount: 10,
|
||||
syringeRefills: 2,
|
||||
injectionSiteNotes: 'Notes',
|
||||
frequency: InjectionFrequency.weekly,
|
||||
subtype: InjectionSubtype.intravenous,
|
||||
),
|
||||
);
|
||||
|
||||
// Assert - verify subtypes are correctly stored
|
||||
expect(imInjection.injectionDetails?.subtype,
|
||||
equals(InjectionSubtype.intramuscular));
|
||||
expect(scInjection.injectionDetails?.subtype,
|
||||
equals(InjectionSubtype.subcutaneous));
|
||||
expect(ivInjection.injectionDetails?.subtype,
|
||||
equals(InjectionSubtype.intravenous));
|
||||
});
|
||||
});
|
||||
}
|
240
test/features/medication_tracker/models/medication_test.dart
Normal file
240
test/features/medication_tracker/models/medication_test.dart
Normal file
|
@ -0,0 +1,240 @@
|
|||
// test/features/medication_tracker/models/medication_test.dart
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:nokken/src/features/medication_tracker/models/medication.dart';
|
||||
|
||||
void main() {
|
||||
group('Medication', () {
|
||||
test('needsRefill should return true when quantity is below threshold', () {
|
||||
// Setup - using minimal valid parameters
|
||||
final medication = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 3,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
);
|
||||
|
||||
// Test and verify
|
||||
expect(medication.needsRefill(), true);
|
||||
});
|
||||
|
||||
test('needsRefill should return false when quantity is above threshold',
|
||||
() {
|
||||
// Setup - using minimal valid parameters
|
||||
final medication = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
);
|
||||
|
||||
// Test and verify
|
||||
expect(medication.needsRefill(), false);
|
||||
});
|
||||
|
||||
test('every medication has a unique ID', () {
|
||||
// Create two medications with identical properties
|
||||
final medication1 = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
);
|
||||
|
||||
final medication2 = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
);
|
||||
|
||||
// Verify each has a unique ID
|
||||
expect(medication1.id, isNot(equals(medication2.id)));
|
||||
expect(medication1.id, isNotEmpty);
|
||||
expect(medication2.id, isNotEmpty);
|
||||
});
|
||||
|
||||
test('should validate that oral medications have oral subtype', () {
|
||||
// Should succeed
|
||||
final validOral = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
);
|
||||
|
||||
expect(validOral.oralSubtype, equals(OralSubtype.tablets));
|
||||
|
||||
// Should throw exception when oral medication has no subtype
|
||||
expect(
|
||||
() => Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: null,
|
||||
),
|
||||
throwsA(isA<MedicationException>()));
|
||||
});
|
||||
|
||||
test('should validate that topical medications have topical subtype', () {
|
||||
// Should succeed
|
||||
final validTopical = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.topical,
|
||||
topicalSubtype: TopicalSubtype.gel,
|
||||
);
|
||||
|
||||
expect(validTopical.topicalSubtype, equals(TopicalSubtype.gel));
|
||||
|
||||
// Should throw exception when topical medication has no subtype
|
||||
expect(
|
||||
() => Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.topical,
|
||||
topicalSubtype: null,
|
||||
),
|
||||
throwsA(isA<MedicationException>()));
|
||||
});
|
||||
|
||||
test('patch medication does not require subtypes', () {
|
||||
// Should succeed
|
||||
final validPatch = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.patch,
|
||||
);
|
||||
|
||||
expect(validPatch.medicationType, equals(MedicationType.patch));
|
||||
expect(validPatch.oralSubtype, isNull);
|
||||
expect(validPatch.topicalSubtype, isNull);
|
||||
expect(validPatch.injectionDetails, isNull);
|
||||
});
|
||||
|
||||
test('doctor and pharmacy fields are optional', () {
|
||||
// Create medication with doctor and pharmacy
|
||||
final medication = Medication(
|
||||
name: 'Test Med',
|
||||
dosage: '10mg',
|
||||
startDate: DateTime(2023, 1, 1),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 1, 1, 8, 0)],
|
||||
daysOfWeek: {'M'},
|
||||
currentQuantity: 10,
|
||||
refillThreshold: 5,
|
||||
medicationType: MedicationType.oral,
|
||||
oralSubtype: OralSubtype.tablets,
|
||||
doctor: 'Dr. Smith',
|
||||
pharmacy: 'City Pharmacy',
|
||||
);
|
||||
|
||||
// Verify fields are set
|
||||
expect(medication.doctor, 'Dr. Smith');
|
||||
expect(medication.pharmacy, 'City Pharmacy');
|
||||
});
|
||||
|
||||
test('JSON conversion preserves all fields', () {
|
||||
// Create a complex medication object
|
||||
final original = Medication(
|
||||
name: 'Test Injectable',
|
||||
dosage: '100mg',
|
||||
startDate: DateTime(2023, 6, 15),
|
||||
frequency: 1,
|
||||
timeOfDay: [DateTime(2023, 6, 15, 9, 0)],
|
||||
daysOfWeek: {'Su'},
|
||||
currentQuantity: 5,
|
||||
refillThreshold: 2,
|
||||
medicationType: MedicationType.injection,
|
||||
doctor: 'Dr. Jones',
|
||||
pharmacy: 'Medical Supply Store',
|
||||
injectionDetails: InjectionDetails(
|
||||
drawingNeedleType: '18G',
|
||||
drawingNeedleCount: 10,
|
||||
drawingNeedleRefills: 2,
|
||||
injectingNeedleType: '25G',
|
||||
injectingNeedleCount: 8,
|
||||
injectingNeedleRefills: 2,
|
||||
syringeType: '3ml Luer Lock',
|
||||
syringeCount: 12,
|
||||
syringeRefills: 3,
|
||||
injectionSiteNotes: 'Rotate injection sites',
|
||||
frequency: InjectionFrequency.biweekly,
|
||||
subtype: InjectionSubtype.intramuscular,
|
||||
),
|
||||
);
|
||||
|
||||
// Convert to JSON and back
|
||||
final json = original.toJson();
|
||||
final restored = Medication.fromJson(json);
|
||||
|
||||
// Verify all fields are preserved
|
||||
expect(restored.name, equals(original.name));
|
||||
expect(restored.dosage, equals(original.dosage));
|
||||
expect(restored.medicationType, equals(original.medicationType));
|
||||
expect(restored.doctor, equals(original.doctor));
|
||||
expect(restored.pharmacy, equals(original.pharmacy));
|
||||
|
||||
// Check injection details
|
||||
expect(restored.injectionDetails?.syringeType, equals('3ml Luer Lock'));
|
||||
expect(restored.injectionDetails?.syringeCount, equals(12));
|
||||
expect(restored.injectionDetails?.syringeRefills, equals(3));
|
||||
expect(restored.injectionDetails?.subtype,
|
||||
equals(InjectionSubtype.intramuscular));
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue