f655adfbea
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.
140 lines
4.0 KiB
Dart
140 lines
4.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:relationship_saver/app/state/local_data_state.dart';
|
|
import 'package:relationship_saver/features/ideas/domain/idea_models.dart';
|
|
import 'package:relationship_saver/features/people/domain/person_models.dart';
|
|
import 'package:relationship_saver/features/reminders/domain/reminder_models.dart';
|
|
import 'package:relationship_saver/features/share_intake/domain/share_models.dart';
|
|
import 'package:relationship_saver/features/signals/domain/signals_feed_synthesizer.dart';
|
|
|
|
void main() {
|
|
test(
|
|
'creates a check-in signal when interaction is stale and no reminder exists',
|
|
() {
|
|
final LocalDataState state = LocalDataState(
|
|
people: <PersonProfile>[
|
|
PersonProfile(
|
|
id: 'p-1',
|
|
name: 'Alex',
|
|
relationship: 'Friend',
|
|
affinityScore: 80,
|
|
nextMoment: DateTime(2026, 4, 30),
|
|
tags: const <String>[],
|
|
notes: '',
|
|
lastInteractedAt: DateTime(2026, 4, 1),
|
|
),
|
|
],
|
|
moments: const [],
|
|
ideas: const [],
|
|
reminders: const [],
|
|
tasks: const [],
|
|
);
|
|
|
|
final signals = SignalsFeedSynthesizer.synthesize(
|
|
state: state,
|
|
now: DateTime(2026, 4, 17),
|
|
);
|
|
|
|
expect(signals, isNotEmpty);
|
|
expect(signals.first.title, 'Check in with Alex');
|
|
expect(
|
|
signals.first.metadata?[signalMetadataActionKey],
|
|
createReminderSignalAction,
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'suppresses stale check-in signal when a nearby reminder already exists',
|
|
() {
|
|
final LocalDataState state = LocalDataState(
|
|
people: <PersonProfile>[
|
|
PersonProfile(
|
|
id: 'p-1',
|
|
name: 'Alex',
|
|
relationship: 'Friend',
|
|
affinityScore: 80,
|
|
nextMoment: DateTime(2026, 4, 30),
|
|
tags: const <String>[],
|
|
notes: '',
|
|
lastInteractedAt: DateTime(2026, 4, 1),
|
|
),
|
|
],
|
|
moments: const [],
|
|
ideas: const [],
|
|
reminders: <ReminderRule>[
|
|
ReminderRule(
|
|
id: 'r-1',
|
|
personId: 'p-1',
|
|
title: 'Weekly check in',
|
|
cadence: ReminderCadence.weekly,
|
|
nextAt: DateTime(2026, 4, 18),
|
|
),
|
|
],
|
|
tasks: const [],
|
|
);
|
|
|
|
final signals = SignalsFeedSynthesizer.synthesize(
|
|
state: state,
|
|
now: DateTime(2026, 4, 17),
|
|
);
|
|
|
|
expect(
|
|
signals.where((signal) => signal.title.contains('Check in')),
|
|
isEmpty,
|
|
);
|
|
},
|
|
);
|
|
|
|
test('creates an upcoming date signal with idea count context', () {
|
|
final DateTime now = DateTime(2026, 4, 17);
|
|
final LocalDataState state = LocalDataState(
|
|
people: <PersonProfile>[
|
|
PersonProfile(
|
|
id: 'p-1',
|
|
name: 'Mila',
|
|
relationship: 'Sister',
|
|
affinityScore: 88,
|
|
nextMoment: DateTime(2026, 4, 25),
|
|
tags: const <String>[],
|
|
notes: '',
|
|
lastInteractedAt: DateTime(2026, 4, 16),
|
|
),
|
|
],
|
|
moments: const [],
|
|
ideas: <RelationshipIdea>[
|
|
RelationshipIdea(
|
|
id: 'i-1',
|
|
personId: 'p-1',
|
|
type: IdeaType.gift,
|
|
title: 'Plant stand',
|
|
details: 'Oak finish',
|
|
createdAt: DateTime(2026, 4, 10),
|
|
),
|
|
],
|
|
reminders: const [],
|
|
tasks: const [],
|
|
importantDates: <PersonImportantDate>[
|
|
PersonImportantDate(
|
|
id: 'd-1',
|
|
personId: 'p-1',
|
|
label: 'Birthday',
|
|
date: now.add(const Duration(days: 3)),
|
|
classification: 'birthday',
|
|
sourceKind: CaptureSourceKind.manual,
|
|
createdAt: now.subtract(const Duration(days: 1)),
|
|
updatedAt: now,
|
|
),
|
|
],
|
|
);
|
|
|
|
final signals = SignalsFeedSynthesizer.synthesize(state: state, now: now);
|
|
|
|
expect(
|
|
signals.any(
|
|
(signal) => signal.title.contains('Birthday for Mila is in 3 days'),
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
}
|