243 lines
8.3 KiB
Dart
243 lines
8.3 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:relationship_saver/features/local/local_models.dart';
|
|
import 'package:relationship_saver/features/local/local_repository.dart';
|
|
import 'package:relationship_saver/features/local/storage/local_data_store_in_memory.dart';
|
|
import 'package:relationship_saver/features/local/storage/local_data_store_provider.dart';
|
|
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler.dart';
|
|
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_provider.dart';
|
|
import 'package:relationship_saver/features/sync/storage/sync_state_store_in_memory.dart';
|
|
import 'package:relationship_saver/features/sync/storage/sync_state_store_provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues(<String, Object>{});
|
|
});
|
|
|
|
test('seeds local data on first load', () async {
|
|
final ProviderContainer container = _createContainer();
|
|
addTearDown(container.dispose);
|
|
|
|
final state = await container.read(localRepositoryProvider.future);
|
|
|
|
expect(state.people, isNotEmpty);
|
|
expect(state.moments, isNotEmpty);
|
|
expect(state.tasks, isNotEmpty);
|
|
});
|
|
|
|
test('adds and removes person with persistence state update', () async {
|
|
final ProviderContainer container = _createContainer();
|
|
addTearDown(container.dispose);
|
|
|
|
final initial = await container.read(localRepositoryProvider.future);
|
|
final int initialCount = initial.people.length;
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.addPerson(
|
|
name: 'Test Person',
|
|
relationship: 'Friend',
|
|
notes: 'test',
|
|
tags: <String>['alpha'],
|
|
);
|
|
|
|
final afterAdd = await container.read(localRepositoryProvider.future);
|
|
expect(afterAdd.people.length, initialCount + 1);
|
|
|
|
final String insertedId = afterAdd.people.first.id;
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.deletePerson(insertedId);
|
|
|
|
final afterDelete = await container.read(localRepositoryProvider.future);
|
|
expect(afterDelete.people.length, initialCount);
|
|
});
|
|
|
|
test('adds and removes capture moments', () async {
|
|
final ProviderContainer container = _createContainer();
|
|
addTearDown(container.dispose);
|
|
|
|
final state = await container.read(localRepositoryProvider.future);
|
|
final String personId = state.people.first.id;
|
|
final int beforeCount = state.moments.length;
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.addMoment(personId: personId, summary: 'This is a new capture moment');
|
|
|
|
final withMoment = await container.read(localRepositoryProvider.future);
|
|
expect(withMoment.moments.length, beforeCount + 1);
|
|
|
|
final String momentId = withMoment.moments.first.id;
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.deleteMoment(momentId);
|
|
|
|
final afterDelete = await container.read(localRepositoryProvider.future);
|
|
expect(afterDelete.moments.length, beforeCount);
|
|
});
|
|
|
|
test('adds, archives, edits, and deletes ideas', () async {
|
|
final ProviderContainer container = _createContainer();
|
|
addTearDown(container.dispose);
|
|
|
|
final LocalDataState state = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
final String personId = state.people.first.id;
|
|
final int beforeCount = state.ideas.length;
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.addIdea(
|
|
type: IdeaType.gift,
|
|
title: 'Test idea',
|
|
details: 'some details',
|
|
personId: personId,
|
|
);
|
|
|
|
final LocalDataState afterAdd = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
expect(afterAdd.ideas.length, beforeCount + 1);
|
|
|
|
final RelationshipIdea inserted = afterAdd.ideas.first;
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.toggleIdeaArchived(inserted.id);
|
|
|
|
LocalDataState afterArchive = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
expect(afterArchive.ideas.first.isArchived, isTrue);
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.updateIdea(afterArchive.ideas.first.copyWith(title: 'Updated idea'));
|
|
|
|
afterArchive = await container.read(localRepositoryProvider.future);
|
|
expect(afterArchive.ideas.first.title, 'Updated idea');
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.deleteIdea(inserted.id);
|
|
|
|
final LocalDataState afterDelete = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
expect(afterDelete.ideas.length, beforeCount);
|
|
});
|
|
|
|
test('adds, updates, toggles, and deletes reminders', () async {
|
|
final ProviderContainer container = _createContainer();
|
|
addTearDown(container.dispose);
|
|
|
|
final LocalDataState state = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
final String personId = state.people.first.id;
|
|
final int beforeCount = state.reminders.length;
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.addReminder(
|
|
title: 'Test reminder',
|
|
cadence: ReminderCadence.weekly,
|
|
nextAt: DateTime.now().add(const Duration(days: 1)),
|
|
personId: personId,
|
|
);
|
|
|
|
LocalDataState afterAdd = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
expect(afterAdd.reminders.length, beforeCount + 1);
|
|
|
|
final ReminderRule inserted = afterAdd.reminders.first;
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.toggleReminderEnabled(inserted.id);
|
|
afterAdd = await container.read(localRepositoryProvider.future);
|
|
expect(afterAdd.reminders.first.enabled, isFalse);
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.updateReminder(
|
|
afterAdd.reminders.first.copyWith(title: 'Updated reminder'),
|
|
);
|
|
afterAdd = await container.read(localRepositoryProvider.future);
|
|
expect(afterAdd.reminders.first.title, 'Updated reminder');
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.deleteReminder(inserted.id);
|
|
|
|
final LocalDataState afterDelete = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
expect(afterDelete.reminders.length, beforeCount);
|
|
});
|
|
|
|
test('reconciles reminder schedule after state changes', () async {
|
|
final RecordingReminderScheduler scheduler = RecordingReminderScheduler();
|
|
final ProviderContainer container = _createContainer(scheduler: scheduler);
|
|
addTearDown(container.dispose);
|
|
|
|
final LocalDataState initial = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
final int beforeCount = initial.reminders.length;
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.addReminder(
|
|
title: 'Scheduled reminder',
|
|
cadence: ReminderCadence.daily,
|
|
nextAt: DateTime.now().add(const Duration(hours: 4)),
|
|
);
|
|
final LocalDataState afterAdd = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
|
|
await container
|
|
.read(localRepositoryProvider.notifier)
|
|
.deleteReminder(afterAdd.reminders.first.id);
|
|
final LocalDataState afterDelete = await container.read(
|
|
localRepositoryProvider.future,
|
|
);
|
|
|
|
expect(scheduler.snapshots, isNotEmpty);
|
|
expect(scheduler.snapshots.first.length, beforeCount);
|
|
expect(
|
|
scheduler.snapshots.any(
|
|
(List<ReminderRule> r) => r.length == beforeCount + 1,
|
|
),
|
|
isTrue,
|
|
);
|
|
expect(scheduler.snapshots.last.length, afterDelete.reminders.length);
|
|
});
|
|
}
|
|
|
|
ProviderContainer _createContainer({ReminderScheduler? scheduler}) {
|
|
return ProviderContainer(
|
|
overrides: [
|
|
localDataStoreProvider.overrideWithValue(InMemoryLocalDataStore()),
|
|
syncStateStoreProvider.overrideWithValue(InMemorySyncStateStore()),
|
|
if (scheduler != null)
|
|
reminderSchedulerProvider.overrideWithValue(scheduler),
|
|
],
|
|
);
|
|
}
|
|
|
|
class RecordingReminderScheduler implements ReminderScheduler {
|
|
final List<List<ReminderRule>> snapshots = <List<ReminderRule>>[];
|
|
|
|
@override
|
|
Future<void> clearAll() async {}
|
|
|
|
@override
|
|
Future<void> reconcile(List<ReminderRule> reminders) async {
|
|
snapshots.add(List<ReminderRule>.unmodifiable(reminders));
|
|
}
|
|
}
|