Refine backendless share intake

This commit is contained in:
Rijad Zuzo
2026-05-18 20:33:54 +02:00
parent f655adfbea
commit 42a59e959f
37 changed files with 1467 additions and 824 deletions
+92 -32
View File
@@ -17,15 +17,17 @@ void main() {
SharedPreferences.setMockInitialValues(<String, Object>{});
});
test('seeds local data on first load', () async {
test('starts with empty 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);
expect(state.people, isEmpty);
expect(state.moments, isEmpty);
expect(state.ideas, isEmpty);
expect(state.reminders, isEmpty);
expect(state.tasks, isEmpty);
});
test('adds and removes person with persistence state update', () async {
@@ -62,8 +64,8 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final String personId = await _addTestPerson(container);
final state = await container.read(localRepositoryProvider.future);
final String personId = state.people.first.id;
final int beforeCount = state.moments.length;
await container
@@ -86,10 +88,7 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final LocalDataState state = await container.read(
localRepositoryProvider.future,
);
final String personId = state.people.first.id;
final String personId = await _addTestPerson(container);
final PersonPreferenceSignal first = await container
.read(localRepositoryProvider.notifier)
@@ -161,7 +160,7 @@ void main() {
});
test(
'ingestSharedMessage auto-creates profile and reuses source link for follow-up messages',
'ingestSharedMessage queues unknown identity, then reuses source link after manual creation',
() async {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
@@ -184,22 +183,35 @@ void main() {
final LocalDataState afterFirst = await container.read(
localRepositoryProvider.future,
);
expect(first.createdProfile, isTrue);
expect(afterFirst.people.length, before.people.length + 1);
final SourceProfileLink createdLink = afterFirst.sourceLinks.firstWhere(
(SourceProfileLink link) => link.profileId == first.profileId,
expect(first.isQueuedForResolution, isTrue);
expect(first.createdProfile, isFalse);
expect(afterFirst.people.length, before.people.length);
expect(
afterFirst.sharedInbox.single.reason,
SharedInboxReason.missingIdentity,
);
final SharedMessageIngestResult created = await container
.read(localRepositoryProvider.notifier)
.resolveSharedInboxByCreatingProfile(
inboxEntryId: first.inboxEntryId!,
name: 'Taylor Quinn',
relationship: 'Friend',
);
final LocalDataState afterCreate = await container.read(
localRepositoryProvider.future,
);
expect(created.createdProfile, isTrue);
expect(afterCreate.people.length, before.people.length + 1);
final SourceProfileLink createdLink = afterCreate.sourceLinks.firstWhere(
(SourceProfileLink link) => link.profileId == created.profileId,
);
expect(createdLink.sourceFingerprint, 'whatsapp|u:wa:taylor quinn');
expect(
afterFirst.sourceLinks.any(
(SourceProfileLink link) => link.profileId == first.profileId,
),
isTrue,
);
expect(
afterFirst.moments.any(
afterCreate.moments.any(
(RelationshipMoment moment) =>
moment.personId == first.profileId && moment.type == 'whatsapp',
moment.personId == created.profileId && moment.type == 'whatsapp',
),
isTrue,
);
@@ -219,11 +231,11 @@ void main() {
localRepositoryProvider.future,
);
expect(second.createdProfile, isFalse);
expect(second.profileId, first.profileId);
expect(afterSecond.people.length, afterFirst.people.length);
expect(second.profileId, created.profileId);
expect(afterSecond.people.length, afterCreate.people.length);
expect(
afterSecond.sharedMessages.length,
afterFirst.sharedMessages.length + 1,
afterCreate.sharedMessages.length + 1,
);
},
);
@@ -234,7 +246,7 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
await container.read(localRepositoryProvider.future);
await _addTestPerson(container, name: 'Nora Diaz');
final SharedMessageIngestResult result = await container
.read(localRepositoryProvider.notifier)
@@ -285,10 +297,7 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final LocalDataState state = await container.read(
localRepositoryProvider.future,
);
final String personId = state.people.first.id;
final String personId = await _addTestPerson(container);
final SharedPayload payload = SharePayloadParser.parse(
rawText: 'She loves vegan sushi and rooftop dinners.',
sourceApp: 'share_sheet',
@@ -378,6 +387,35 @@ void main() {
},
);
test(
'captureSharedPayloadByCreatingProfile requires an explicit person name',
() async {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
await container.read(localRepositoryProvider.future);
final SharedPayload payload = SharePayloadParser.parse(
rawText: 'Alex: Kaffee klingt gut',
sourceApp: 'whatsapp',
platform: 'ios',
);
expect(payload.sourceDisplayName, 'Alex');
expect(
() => container
.read(localRepositoryProvider.notifier)
.captureSharedPayloadByCreatingProfile(payload: payload),
throwsArgumentError,
);
final LocalDataState after = await container.read(
localRepositoryProvider.future,
);
expect(after.people, isEmpty);
expect(after.sharedMessages, isEmpty);
},
);
test(
'queues near-match conflict and then uses fingerprint mapping for follow-up shares',
() async {
@@ -689,10 +727,10 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final String personId = await _addTestPerson(container);
final LocalDataState state = await container.read(
localRepositoryProvider.future,
);
final String personId = state.people.first.id;
final int beforeCount = state.ideas.length;
await container
@@ -740,10 +778,10 @@ void main() {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final String personId = await _addTestPerson(container);
final LocalDataState state = await container.read(
localRepositoryProvider.future,
);
final String personId = state.people.first.id;
final int beforeCount = state.reminders.length;
await container
@@ -852,6 +890,28 @@ ProviderContainer _createContainer({ReminderScheduler? scheduler}) {
);
}
Future<String> _addTestPerson(
ProviderContainer container, {
String name = 'Test Person',
String relationship = 'Friend',
}) async {
await container.read(localRepositoryProvider.future);
await container
.read(localRepositoryProvider.notifier)
.addPerson(
name: name,
relationship: relationship,
notes: '',
tags: const <String>[],
);
final LocalDataState state = await container.read(
localRepositoryProvider.future,
);
return state.people
.firstWhere((PersonProfile person) => person.name == name)
.id;
}
class RecordingReminderScheduler implements ReminderScheduler {
final List<List<ReminderRule>> snapshots = <List<ReminderRule>>[];
@@ -19,7 +19,7 @@ void main() {
addTearDown(container.dispose);
final initial = await container.read(localRepositoryProvider.future);
expect(initial.people, isNotEmpty);
expect(initial.people, isEmpty);
await container
.read(localRepositoryProvider.notifier)