fix(watch): finalise correctif sync workoutHistory/exercise et distance live montre (#157)
This commit is contained in:
@ -2611,6 +2611,68 @@ void main() {
|
||||
expect(inboxRepository.items.single.status, ShareInboxStatus.accepted);
|
||||
});
|
||||
|
||||
test(
|
||||
'ShareUseCases acceptShare imports all returned resources locally',
|
||||
() async {
|
||||
final remoteShareApi = _FakeRemoteShareApi()
|
||||
..acceptResults = [
|
||||
_remoteSharedProgramItem(),
|
||||
_remoteSharedTemplateItem(),
|
||||
];
|
||||
final localChanges = _FakeLocalSyncChangeRepository();
|
||||
final inboxRepository = _FakeShareInboxRepository()
|
||||
..items.add(_shareInboxItem(status: ShareInboxStatus.pending));
|
||||
|
||||
await _shareUseCase(
|
||||
remoteShareApi: remoteShareApi,
|
||||
localChanges: localChanges,
|
||||
inboxRepository: inboxRepository,
|
||||
).acceptShare('share-1');
|
||||
|
||||
expect(localChanges.appliedItems.map((item) => item.resourceType), [
|
||||
SyncResourceType.program,
|
||||
SyncResourceType.workoutTemplate,
|
||||
]);
|
||||
expect(inboxRepository.items.single.status, ShareInboxStatus.accepted);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'ShareUseCases acceptShare imports cached pack payload after remote ack',
|
||||
() async {
|
||||
final remoteShareApi = _FakeRemoteShareApi();
|
||||
final templateRepository = _FakeWorkoutTemplateRepository();
|
||||
final inboxRepository = _FakeShareInboxRepository()
|
||||
..items.add(
|
||||
_shareInboxItem(
|
||||
status: ShareInboxStatus.pending,
|
||||
resourceType: ShareResourceType.pack,
|
||||
payloadJson: jsonEncode({
|
||||
'name': 'Pack reprise',
|
||||
'workouts': [
|
||||
_sharedTemplatePayload(id: 'template-a', name: 'Séance A'),
|
||||
_sharedTemplatePayload(id: 'template-b', name: 'Séance B'),
|
||||
],
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
await _shareUseCase(
|
||||
remoteShareApi: remoteShareApi,
|
||||
inboxRepository: inboxRepository,
|
||||
templateRepository: templateRepository,
|
||||
).acceptShare('share-1');
|
||||
|
||||
expect(remoteShareApi.acceptCalls, 1);
|
||||
expect(templateRepository.templates, hasLength(2));
|
||||
expect(
|
||||
templateRepository.templates.map((template) => template.name),
|
||||
containsAll(['Séance A', 'Séance B']),
|
||||
);
|
||||
expect(inboxRepository.items.single.status, ShareInboxStatus.accepted);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'ShareUseCases acceptShare sans token importe une copie locale indépendante',
|
||||
() async {
|
||||
@ -4559,6 +4621,7 @@ ShareUseCases _shareUseCase({
|
||||
final class _FakeRemoteShareApi implements RemoteShareApi {
|
||||
Exception? exception;
|
||||
RemoteSyncedItem? acceptResult;
|
||||
List<RemoteSyncedItem>? acceptResults;
|
||||
List<ShareInboxItem> inboxItems = const [];
|
||||
var sendCalls = 0;
|
||||
var acceptCalls = 0;
|
||||
@ -4568,13 +4631,16 @@ final class _FakeRemoteShareApi implements RemoteShareApi {
|
||||
Map<String, Object?>? lastPayload;
|
||||
|
||||
@override
|
||||
Future<RemoteSyncedItem> acceptShare(String shareId, String token) async {
|
||||
Future<List<RemoteSyncedItem>> acceptShare(
|
||||
String shareId,
|
||||
String token,
|
||||
) async {
|
||||
acceptCalls += 1;
|
||||
final error = exception;
|
||||
if (error != null) {
|
||||
throw error;
|
||||
}
|
||||
return acceptResult ?? _remoteSharedProgramItem();
|
||||
return acceptResults ?? [acceptResult ?? _remoteSharedProgramItem()];
|
||||
}
|
||||
|
||||
@override
|
||||
@ -4804,6 +4870,19 @@ RemoteSyncedItem _remoteSharedProgramItem() {
|
||||
);
|
||||
}
|
||||
|
||||
RemoteSyncedItem _remoteSharedTemplateItem() {
|
||||
return RemoteSyncedItem(
|
||||
resourceType: SyncResourceType.workoutTemplate,
|
||||
clientId: 'shared-template-1',
|
||||
serverId: 'server-template-1',
|
||||
schemaVersion: 1,
|
||||
clientUpdatedAt: DateTime.utc(2026, 7, 17, 12, 2),
|
||||
serverUpdatedAt: DateTime.utc(2026, 7, 17, 12, 3),
|
||||
deletedAt: null,
|
||||
payload: const {'id': 'shared-template-1', 'name': 'Séance partagée'},
|
||||
);
|
||||
}
|
||||
|
||||
ExerciseUseCases _exerciseUseCase(
|
||||
_FakeExerciseRepository repository, {
|
||||
_FakeProgramRepository? programRepository,
|
||||
|
||||
@ -377,6 +377,43 @@ void main() {
|
||||
expect(env.repository.stepResults.last.actualScore, 0);
|
||||
});
|
||||
|
||||
test('projects reps target for independent step manual score', () async {
|
||||
final session = _session(
|
||||
steps: [
|
||||
_step(
|
||||
type: ExerciseStepType.reps,
|
||||
defaultTargetValue: 10,
|
||||
hasScore: true,
|
||||
defaultTargetScore: 8,
|
||||
),
|
||||
],
|
||||
);
|
||||
final repository = _FakeActiveSessionRepository()..session = session;
|
||||
repository.stepProgressStates['step-state'] = _stepState(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
final projectionUseCases = WatchCompanionProjectionUseCases(
|
||||
sessionRepository: repository,
|
||||
clock: _FakeClock(_now),
|
||||
ids: _FakeIds(),
|
||||
originDeviceId: 'device-1',
|
||||
);
|
||||
|
||||
final projection = await projectionUseCases.emitCurrentProjection();
|
||||
|
||||
expect(projection.hasManualScore, isTrue);
|
||||
expect(projection.manualScoreScope, WatchManualScoreScope.step);
|
||||
expect(projection.manualScoreRepsTargetValue, 10);
|
||||
expect(projection.manualScoreTargetValue, 8);
|
||||
expect(projection.manualScoreTargetLabel, 'Cible');
|
||||
expect(
|
||||
projection.expiresAtEpochMs - projection.projectedAtEpochMs,
|
||||
const Duration(seconds: 12).inMilliseconds,
|
||||
);
|
||||
|
||||
await projectionUseCases.dispose();
|
||||
});
|
||||
|
||||
test('decrementScore at zero is accepted no-op', () async {
|
||||
final env = _env(
|
||||
session: _session(scoreEnabled: true),
|
||||
@ -740,6 +777,7 @@ final class _FakeProjectionSource implements WatchProjectionSource {
|
||||
deviceSessionId: projection.deviceSessionId,
|
||||
revision: projection.revision + 1,
|
||||
projectedAtEpochMs: projection.projectedAtEpochMs,
|
||||
expiresAtEpochMs: projection.expiresAtEpochMs,
|
||||
phase: projection.phase,
|
||||
phoneReachable: projection.phoneReachable,
|
||||
seriesIndex: projection.seriesIndex,
|
||||
@ -752,6 +790,9 @@ final class _FakeProjectionSource implements WatchProjectionSource {
|
||||
hasManualScore: projection.hasManualScore,
|
||||
currentManualScoreValue: projection.currentManualScoreValue,
|
||||
canDecrementScore: projection.canDecrementScore,
|
||||
manualScoreTargetValue: projection.manualScoreTargetValue,
|
||||
manualScoreTargetLabel: projection.manualScoreTargetLabel,
|
||||
manualScoreRepsTargetValue: projection.manualScoreRepsTargetValue,
|
||||
manualScoreScope: projection.manualScoreScope,
|
||||
);
|
||||
return projection;
|
||||
|
||||
Reference in New Issue
Block a user