Files
rely/lib/integrations/backend/backend_gateway_fake.dart
T
2026-05-18 20:33:54 +02:00

149 lines
4.1 KiB
Dart

import 'package:clock/clock.dart';
import 'package:relationship_saver/integrations/backend/backend_gateway.dart';
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
import 'package:uuid/uuid.dart';
/// Deterministic fake backend for local development and tests.
class BackendGatewayFake implements BackendGateway {
BackendGatewayFake({Clock? clock, Uuid? uuid})
: _clock = clock ?? const Clock(),
_uuid = uuid ?? const Uuid();
final Clock _clock;
final Uuid _uuid;
static final DateTime _seedTime = DateTime.utc(2026, 1, 1, 8);
@override
Future<void> acknowledgeSignal({
required String signalId,
required SignalAction action,
DateTime? at,
}) async {
return;
}
@override
Future<DownloadUrl> getDownloadUrl({required String fileId}) async {
return DownloadUrl(
downloadUrl: 'https://files.example.com/download/$fileId',
expiresAt: _seedTime.add(const Duration(hours: 1)),
);
}
@override
Future<SignalsFeed> getSignalsFeed({
String? cursor,
DateTime? since,
int limit = 50,
Set<String>? personIds,
}) async {
final List<SignalItem> seeded = <SignalItem>[
SignalItem(
id: 'sig-1',
type: 'deal',
title: 'Weekend coffee promo nearby',
description: 'Save this for your next date idea.',
personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-1',
createdAt: _seedTime,
metadata: const <String, dynamic>{'merchant': 'Cafe Bloom'},
),
SignalItem(
id: 'sig-2',
type: 'trend',
title: 'Books are trending in gift ideas',
description: 'Reading-themed gifts up 24% this week.',
personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-1',
createdAt: _seedTime.add(const Duration(hours: 3)),
),
SignalItem(
id: 'sig-3',
type: 'recommendation',
title: 'Try a sunset walk reminder',
description: 'Low-effort ritual with high emotional impact.',
personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-2',
createdAt: _seedTime.add(const Duration(hours: 6)),
),
];
return SignalsFeed(
cursor: 'fake-cursor-1',
items: seeded.take(limit).toList(),
);
}
@override
Future<UploadInitResult> initUpload({
required UploadPurpose purpose,
required String contentType,
required int sizeBytes,
}) async {
final String fileId = 'file-${_uuid.v4()}';
return UploadInitResult(
uploadUrl: 'https://uploads.example.com/$fileId',
fileId: fileId,
expiresAt: _seedTime.add(const Duration(minutes: 15)),
);
}
@override
Future<UserProfile> me() async {
return const UserProfile(
id: 'user-fake',
email: 'demo@example.com',
displayName: 'Demo User',
);
}
@override
Future<SyncPullResult> pullChanges({String? cursor, int limit = 200}) async {
return SyncPullResult(
cursor: cursor ?? 'fake-pull-cursor-empty',
changes: const <ChangeEnvelope>[],
);
}
@override
Future<SyncPushResult> pushChanges({
required List<ChangeEnvelope> changes,
String? cursor,
}) async {
final DateTime now = _clock.now().toUtc();
final List<MutationAck> accepted = changes
.map(
(ChangeEnvelope change) => MutationAck(
clientMutationId: change.clientMutationId,
serverMutationId: 'srv-${change.clientMutationId}',
acceptedAt: now,
),
)
.toList(growable: false);
return SyncPushResult(
cursor: 'fake-push-cursor-1',
accepted: accepted,
rejected: const <MutationRejection>[],
);
}
@override
Future<AuthSession> signIn(SignInRequest request) async {
final DateTime now = _clock.now().toUtc();
return AuthSession(
accessToken: 'fake-access-token',
refreshToken: 'fake-refresh-token',
expiresAt: now.add(const Duration(hours: 1)),
user: UserProfile(
id: 'user-fake',
email: request.email ?? 'demo@example.com',
displayName: 'Demo User',
),
);
}
@override
Future<void> signOut() async {
return;
}
}