feat: add local-first private AI digest workflow
Migrate app code into canonical feature slices, add phone-only AI digest scheduling and review, wire local notification/background task support, and cover the flow with tests.
This commit is contained in:
@@ -9,45 +9,48 @@ import 'package:relationship_saver/features/sync/sync_background_runner.dart';
|
||||
import 'package:relationship_saver/features/sync/sync_coordinator.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('triggers sync when reachability transitions from offline to online', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final _TestReachability reachability = _TestReachability(initial: false);
|
||||
addTearDown(reachability.dispose);
|
||||
testWidgets(
|
||||
'triggers sync when reachability transitions from offline to online',
|
||||
(WidgetTester tester) async {
|
||||
final _TestReachability reachability = _TestReachability(initial: false);
|
||||
addTearDown(reachability.dispose);
|
||||
|
||||
int syncCalls = 0;
|
||||
int syncCalls = 0;
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
networkReachabilityProvider.overrideWithValue(reachability),
|
||||
syncCoordinatorProvider.overrideWith(
|
||||
(Ref ref) =>
|
||||
_TestSyncCoordinator(ref, onSyncNow: () async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
networkReachabilityProvider.overrideWithValue(reachability),
|
||||
syncCoordinatorProvider.overrideWith(
|
||||
(Ref ref) => _TestSyncCoordinator(
|
||||
ref,
|
||||
onSyncNow: () async {
|
||||
syncCalls += 1;
|
||||
return _okResult();
|
||||
}),
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SyncBackgroundRunner(child: SizedBox()),
|
||||
),
|
||||
],
|
||||
child: const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SyncBackgroundRunner(child: SizedBox()),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||
|
||||
// Startup trigger always runs once when background sync is enabled.
|
||||
expect(syncCalls, 1);
|
||||
// Startup trigger always runs once when background sync is enabled.
|
||||
expect(syncCalls, 1);
|
||||
|
||||
reachability.emit(false);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 40));
|
||||
expect(syncCalls, 1);
|
||||
reachability.emit(false);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 40));
|
||||
expect(syncCalls, 1);
|
||||
|
||||
reachability.emit(true);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||
expect(syncCalls, 2);
|
||||
});
|
||||
reachability.emit(true);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 60));
|
||||
expect(syncCalls, 2);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('does not trigger extra sync while connectivity stays online', (
|
||||
WidgetTester tester,
|
||||
@@ -62,11 +65,13 @@ void main() {
|
||||
overrides: [
|
||||
networkReachabilityProvider.overrideWithValue(reachability),
|
||||
syncCoordinatorProvider.overrideWith(
|
||||
(Ref ref) =>
|
||||
_TestSyncCoordinator(ref, onSyncNow: () async {
|
||||
syncCalls += 1;
|
||||
return _okResult();
|
||||
}),
|
||||
(Ref ref) => _TestSyncCoordinator(
|
||||
ref,
|
||||
onSyncNow: () async {
|
||||
syncCalls += 1;
|
||||
return _okResult();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const Directionality(
|
||||
@@ -102,7 +107,9 @@ class _TestReachability implements NetworkReachability {
|
||||
bool _current;
|
||||
|
||||
@override
|
||||
Future<bool> isReachable({Duration timeout = const Duration(seconds: 2)}) async {
|
||||
Future<bool> isReachable({
|
||||
Duration timeout = const Duration(seconds: 2),
|
||||
}) async {
|
||||
return _current;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user