feat(exercice): modèle et migration pour l'auto-enchaînement des chronos d'étapes (ticket #75)

Étend le modèle Drift (migration schemaVersion 13→14, ADD COLUMN) et
la couche application (entités, use cases, repositories) pour
supporter l'auto-enchaînement des chronos d'étapes. flutter pub get
OK, build_runner OK, analyze propre (mêmes infos préexistantes),
141/141 tests verts, build APK debug validé. Premier ticket du
chantier "auto-enchaînement des chronos d'étapes" (#73).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 13:42:46 +02:00
parent f510053d55
commit e61365277b
8 changed files with 704 additions and 4 deletions

View File

@ -98,6 +98,100 @@ 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);
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 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 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);
final exercise = Exercise(
@ -453,13 +547,18 @@ EntityMetadata _metadata(String id, DateTime now, [int localRevision = 0]) {
ProgramExercise _programExercise(
String id,
DateTime now, {
String programId = 'program-1',
required int position,
bool autoStartNextTimedStepSnapshot = true,
bool? autoStartNextTimedStepOverride,
}) {
return ProgramExercise(
metadata: _metadata(id, now),
programId: 'program-1',
programId: programId,
position: position,
exerciseNameSnapshot: 'Exercise $position',
autoStartNextTimedStepSnapshot: autoStartNextTimedStepSnapshot,
autoStartNextTimedStepOverride: autoStartNextTimedStepOverride,
availableTimeSnapshot: false,
availableRepsSnapshot: true,
availableScoreSnapshot: false,