276 lines
9.6 KiB
Dart
276 lines
9.6 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:christian_period_tracker/models/scripture.dart';
|
|
import 'package:christian_period_tracker/models/cycle_entry.dart';
|
|
import 'package:christian_period_tracker/models/user_profile.dart';
|
|
import 'package:christian_period_tracker/providers/scripture_provider.dart';
|
|
import 'package:christian_period_tracker/providers/user_provider.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
|
|
// Fake ScriptureDatabase implementation for testing
|
|
class FakeScriptureDatabase implements ScriptureDatabase {
|
|
final int Function(String phase) getScriptureCountForPhaseFn;
|
|
final Scripture? Function(String phase, int index)
|
|
getScriptureForPhaseByIndexFn;
|
|
final Scripture? Function(String phase)? getRandomScriptureForPhaseFn;
|
|
|
|
FakeScriptureDatabase({
|
|
required this.getScriptureCountForPhaseFn,
|
|
required this.getScriptureForPhaseByIndexFn,
|
|
this.getRandomScriptureForPhaseFn,
|
|
});
|
|
|
|
@override
|
|
int getScriptureCountForPhase(String phase) =>
|
|
getScriptureCountForPhaseFn(phase);
|
|
|
|
@override
|
|
Scripture? getScriptureForPhaseByIndex(String phase, int index) =>
|
|
getScriptureForPhaseByIndexFn(phase, index);
|
|
|
|
@override
|
|
Scripture? getRandomScriptureForPhase(String phase) =>
|
|
getRandomScriptureForPhaseFn != null
|
|
? getRandomScriptureForPhaseFn!(phase)
|
|
: null;
|
|
|
|
// Unimplemented methods (not used by ScriptureNotifier)
|
|
@override
|
|
List<Scripture> getAllScriptures() => throw UnimplementedError();
|
|
|
|
@override
|
|
Scripture getHusbandScripture() => throw UnimplementedError();
|
|
|
|
@override
|
|
Future<void> loadScriptures() =>
|
|
Future.value(); // Can be mocked to do nothing
|
|
|
|
@override
|
|
Scripture? getRecommendedScripture(CycleEntry entry) =>
|
|
throw UnimplementedError();
|
|
|
|
@override
|
|
Scripture getScriptureForPhase(String phase) => throw UnimplementedError();
|
|
}
|
|
|
|
void main() {
|
|
group('ScriptureNotifier', () {
|
|
late ProviderContainer container;
|
|
late String testPath;
|
|
|
|
setUpAll(() async {
|
|
testPath = '${Directory.current.path}/test_hive_temp_scripture_provider';
|
|
final Directory tempDir = Directory(testPath);
|
|
if (!await tempDir.exists()) {
|
|
await tempDir.create(recursive: true);
|
|
}
|
|
Hive.init(testPath);
|
|
Hive.registerAdapter(UserProfileAdapter());
|
|
Hive.registerAdapter(RelationshipStatusAdapter());
|
|
Hive.registerAdapter(FertilityGoalAdapter());
|
|
Hive.registerAdapter(BibleTranslationAdapter());
|
|
Hive.registerAdapter(UserRoleAdapter());
|
|
await Hive.openBox<UserProfile>('user_profile');
|
|
});
|
|
|
|
tearDownAll(() async {
|
|
await Hive.close();
|
|
await Directory(testPath).delete(recursive: true);
|
|
});
|
|
|
|
final testScripture1 = Scripture(
|
|
verses: {BibleTranslation.esv: "Verse 1"},
|
|
reference: "Ref 1",
|
|
applicablePhases: ['menstrual'],
|
|
);
|
|
final testScripture2 = Scripture(
|
|
verses: {BibleTranslation.esv: "Verse 2"},
|
|
reference: "Ref 2",
|
|
applicablePhases: ['menstrual'],
|
|
);
|
|
final testScripture3 = Scripture(
|
|
verses: {BibleTranslation.esv: "Verse 3"},
|
|
reference: "Ref 3",
|
|
applicablePhases: ['menstrual'],
|
|
);
|
|
|
|
tearDown(() {
|
|
container.dispose();
|
|
});
|
|
|
|
test('initializes with correct scripture for phase', () async {
|
|
final fakeDb = FakeScriptureDatabase(
|
|
getScriptureCountForPhaseFn: (phase) => 3,
|
|
getScriptureForPhaseByIndexFn: (phase, index) => testScripture1,
|
|
);
|
|
|
|
container = ProviderContainer(
|
|
overrides: [
|
|
scriptureDatabaseProvider.overrideWithValue(fakeDb),
|
|
userProfileProvider.overrideWith(
|
|
(ref) => UserProfileNotifier(),
|
|
),
|
|
],
|
|
);
|
|
|
|
final notifier = container.read(scriptureProvider.notifier);
|
|
notifier.initializeScripture(CyclePhase.menstrual);
|
|
|
|
final state = container.read(scriptureProvider);
|
|
expect(state.currentScripture, testScripture1);
|
|
expect(state.currentPhase, CyclePhase.menstrual);
|
|
expect(state.maxIndex, 3);
|
|
// currentIndex will depend on dayOfYear % 3, which is hard to predict
|
|
// So we'll just check it's within bounds.
|
|
expect(state.currentIndex, isNonNegative);
|
|
expect(state.currentIndex, lessThan(3));
|
|
});
|
|
|
|
test('getNextScripture cycles correctly', () async {
|
|
final scriptures = [testScripture1, testScripture2, testScripture3];
|
|
final fakeDb = FakeScriptureDatabase(
|
|
getScriptureCountForPhaseFn: (phase) => scriptures.length,
|
|
getScriptureForPhaseByIndexFn: (phase, index) => scriptures[index],
|
|
);
|
|
|
|
container = ProviderContainer(
|
|
overrides: [
|
|
scriptureDatabaseProvider.overrideWithValue(fakeDb),
|
|
userProfileProvider.overrideWith(
|
|
(ref) => UserProfileNotifier(),
|
|
),
|
|
],
|
|
);
|
|
|
|
final notifier = container.read(scriptureProvider.notifier);
|
|
// Force initial state to 0 for predictable cycling
|
|
notifier.initializeScripture(CyclePhase.menstrual);
|
|
// Override currentIndex to 0 for predictable test
|
|
container.read(scriptureProvider.notifier).state =
|
|
container.read(scriptureProvider).copyWith(currentIndex: 0);
|
|
|
|
// First next
|
|
notifier.getNextScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture2);
|
|
expect(container.read(scriptureProvider).currentIndex, 1);
|
|
|
|
// Second next
|
|
notifier.getNextScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture3);
|
|
expect(container.read(scriptureProvider).currentIndex, 2);
|
|
|
|
// Wrap around
|
|
notifier.getNextScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture1);
|
|
expect(container.read(scriptureProvider).currentIndex, 0);
|
|
});
|
|
|
|
test('getPreviousScripture cycles correctly', () async {
|
|
final scriptures = [testScripture1, testScripture2, testScripture3];
|
|
final fakeDb = FakeScriptureDatabase(
|
|
getScriptureCountForPhaseFn: (phase) => scriptures.length,
|
|
getScriptureForPhaseByIndexFn: (phase, index) => scriptures[index],
|
|
);
|
|
|
|
container = ProviderContainer(
|
|
overrides: [
|
|
scriptureDatabaseProvider.overrideWithValue(fakeDb),
|
|
userProfileProvider.overrideWith(
|
|
(ref) => UserProfileNotifier(),
|
|
),
|
|
],
|
|
);
|
|
|
|
final notifier = container.read(scriptureProvider.notifier);
|
|
// Force initial state to 0 for predictable cycling
|
|
notifier.initializeScripture(CyclePhase.menstrual);
|
|
// Override currentIndex to 0 for predictable test
|
|
container.read(scriptureProvider.notifier).state =
|
|
container.read(scriptureProvider).copyWith(currentIndex: 0);
|
|
|
|
// First previous (wraps around)
|
|
notifier.getPreviousScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture3);
|
|
expect(container.read(scriptureProvider).currentIndex, 2);
|
|
|
|
// Second previous
|
|
notifier.getPreviousScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture2);
|
|
expect(container.read(scriptureProvider).currentIndex, 1);
|
|
|
|
// Third previous
|
|
notifier.getPreviousScripture();
|
|
expect(
|
|
container.read(scriptureProvider).currentScripture, testScripture1);
|
|
expect(container.read(scriptureProvider).currentIndex, 0);
|
|
});
|
|
|
|
test('getRandomScripture updates to a valid scripture', () async {
|
|
final scriptures = [testScripture1, testScripture2, testScripture3];
|
|
final fakeDb = FakeScriptureDatabase(
|
|
getScriptureCountForPhaseFn: (phase) => scriptures.length,
|
|
getScriptureForPhaseByIndexFn: (phase, index) => scriptures[
|
|
index % scriptures.length], // Ensure it always returns a valid one
|
|
);
|
|
|
|
container = ProviderContainer(
|
|
overrides: [
|
|
scriptureDatabaseProvider.overrideWithValue(fakeDb),
|
|
userProfileProvider.overrideWith(
|
|
(ref) => UserProfileNotifier(),
|
|
),
|
|
],
|
|
);
|
|
|
|
final notifier = container.read(scriptureProvider.notifier);
|
|
notifier.initializeScripture(CyclePhase.menstrual);
|
|
|
|
// Perform a random selection
|
|
notifier.getRandomScripture();
|
|
final state = container.read(scriptureProvider);
|
|
|
|
expect(state.currentScripture, isNotNull);
|
|
expect(state.currentScripture,
|
|
isIn(scriptures)); // Ensure it's one of the valid scriptures
|
|
expect(state.currentIndex, isNonNegative);
|
|
expect(state.currentIndex, lessThan(scriptures.length));
|
|
});
|
|
|
|
test('does not change state if maxIndex is 0', () async {
|
|
final fakeDb = FakeScriptureDatabase(
|
|
getScriptureCountForPhaseFn: (phase) => 0,
|
|
getScriptureForPhaseByIndexFn: (phase, index) => null,
|
|
);
|
|
|
|
container = ProviderContainer(
|
|
overrides: [
|
|
scriptureDatabaseProvider.overrideWithValue(fakeDb),
|
|
userProfileProvider.overrideWith(
|
|
(ref) => UserProfileNotifier(),
|
|
),
|
|
],
|
|
);
|
|
|
|
final notifier = container.read(scriptureProvider.notifier);
|
|
notifier.initializeScripture(CyclePhase.menstrual);
|
|
|
|
final initialState = container.read(scriptureProvider);
|
|
expect(initialState.currentScripture, isNull);
|
|
expect(initialState.maxIndex, 0);
|
|
|
|
notifier.getNextScripture();
|
|
notifier.getPreviousScripture();
|
|
notifier.getRandomScripture();
|
|
|
|
// State should remain unchanged
|
|
expect(container.read(scriptureProvider), initialState);
|
|
});
|
|
});
|
|
}
|