feat(exercice): résolution effective et auto-advance des chronos d'étapes (ticket #76)

Ajoute la résolution effective de l'auto-enchaînement (application/use_cases.dart)
et son support dans l'adapter Drift (infrastructure/local/drift_repositories.dart).
flutter pub get OK, dart format appliqué, analyze propre (mêmes infos
préexistantes), tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 13:56:49 +02:00
parent bb90df6411
commit ea1e240c87
4 changed files with 596 additions and 169 deletions

View File

@ -98,99 +98,108 @@ void main() {
expect(restored!.defaultTargetScore, 0);
});
test('step chaining settings round-trip through drift repositories', () async {
final now = DateTime.utc(2026, 7, 17, 12);
final exercise = Exercise(
metadata: _metadata('exercise-chain-1', now),
name: 'Circuit',
hasTimeMeasure: false,
hasRepsMeasure: true,
hasScoreMeasure: false,
defaultTargetReps: 10,
autoStartNextTimedStep: false,
);
await exerciseRepository.save(exercise);
test(
'step chaining settings round-trip through drift repositories',
() async {
final now = DateTime.utc(2026, 7, 17, 12);
final exercise = Exercise(
metadata: _metadata('exercise-chain-1', now),
name: 'Circuit',
hasTimeMeasure: false,
hasRepsMeasure: true,
hasScoreMeasure: false,
defaultTargetReps: 10,
autoStartNextTimedStep: false,
);
await exerciseRepository.save(exercise);
final restoredExercise = await exerciseRepository.findById(
exercise.metadata.id,
);
expect(restoredExercise!.autoStartNextTimedStep, isFalse);
final restoredExercise = await exerciseRepository.findById(
exercise.metadata.id,
);
expect(restoredExercise!.autoStartNextTimedStep, isFalse);
final programExercise = _programExercise(
'program-exercise-chain-1',
now,
programId: 'program-chain-1',
position: 0,
autoStartNextTimedStepSnapshot: false,
autoStartNextTimedStepOverride: true,
);
await programRepository.save(
Program(
metadata: _metadata('program-chain-1', now),
name: 'Programme',
defaultRestSeconds: 60,
exercises: [programExercise],
),
);
final programExercise = _programExercise(
'program-exercise-chain-1',
now,
programId: 'program-chain-1',
position: 0,
autoStartNextTimedStepSnapshot: false,
autoStartNextTimedStepOverride: true,
);
await programRepository.save(
Program(
metadata: _metadata('program-chain-1', now),
name: 'Programme',
defaultRestSeconds: 60,
exercises: [programExercise],
),
);
final restoredProgram = await programRepository.findById('program-chain-1');
expect(
restoredProgram!.exercises.single.autoStartNextTimedStepSnapshot,
isFalse,
);
expect(
restoredProgram.exercises.single.autoStartNextTimedStepOverride,
isTrue,
);
final restoredProgram = await programRepository.findById(
'program-chain-1',
);
expect(
restoredProgram!.exercises.single.autoStartNextTimedStepSnapshot,
isFalse,
);
expect(
restoredProgram.exercises.single.autoStartNextTimedStepOverride,
isTrue,
);
final templateProgram = WorkoutTemplateProgram(
metadata: _metadata('template-program-chain-1', now),
workoutTemplateId: 'template-chain-1',
sourceProgramId: 'program-chain-1',
position: 0,
programNameSnapshot: 'Programme',
defaultRestSecondsSnapshot: 60,
programSnapshotJson: jsonEncode({
'exercises': [programExercise.toSnapshotJson()],
}),
);
await templateRepository.save(
WorkoutTemplate(
metadata: _metadata('template-chain-1', now),
name: 'Séance',
programs: [templateProgram],
overrides: [
WorkoutTemplateExerciseOverride(
metadata: _metadata('override-chain-1', now),
workoutTemplateProgramId: templateProgram.metadata.id,
snapshotProgramExerciseId: programExercise.metadata.id,
),
WorkoutTemplateExerciseOverride(
metadata: _metadata('override-chain-2', now),
workoutTemplateProgramId: templateProgram.metadata.id,
snapshotProgramExerciseId: 'program-exercise-chain-2',
autoStartNextTimedStepOverride: false,
),
],
),
);
final templateProgram = WorkoutTemplateProgram(
metadata: _metadata('template-program-chain-1', now),
workoutTemplateId: 'template-chain-1',
sourceProgramId: 'program-chain-1',
position: 0,
programNameSnapshot: 'Programme',
defaultRestSecondsSnapshot: 60,
programSnapshotJson: jsonEncode({
'exercises': [programExercise.toSnapshotJson()],
}),
);
await templateRepository.save(
WorkoutTemplate(
metadata: _metadata('template-chain-1', now),
name: 'Séance',
programs: [templateProgram],
overrides: [
WorkoutTemplateExerciseOverride(
metadata: _metadata('override-chain-1', now),
workoutTemplateProgramId: templateProgram.metadata.id,
snapshotProgramExerciseId: programExercise.metadata.id,
),
WorkoutTemplateExerciseOverride(
metadata: _metadata('override-chain-2', now),
workoutTemplateProgramId: templateProgram.metadata.id,
snapshotProgramExerciseId: 'program-exercise-chain-2',
autoStartNextTimedStepOverride: false,
),
],
),
);
final restoredTemplate = await templateRepository.findById(
'template-chain-1',
);
expect(
restoredTemplate!.overrides
.singleWhere((override) => override.metadata.id == 'override-chain-1')
.autoStartNextTimedStepOverride,
isNull,
);
expect(
restoredTemplate.overrides
.singleWhere((override) => override.metadata.id == 'override-chain-2')
.autoStartNextTimedStepOverride,
isFalse,
);
});
final restoredTemplate = await templateRepository.findById(
'template-chain-1',
);
expect(
restoredTemplate!.overrides
.singleWhere(
(override) => override.metadata.id == 'override-chain-1',
)
.autoStartNextTimedStepOverride,
isNull,
);
expect(
restoredTemplate.overrides
.singleWhere(
(override) => override.metadata.id == 'override-chain-2',
)
.autoStartNextTimedStepOverride,
isFalse,
);
},
);
test('exercise repository round-trips configured steps', () async {
final now = DateTime.utc(2026, 7, 17, 12);