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

@ -479,6 +479,129 @@ void main() {
);
});
test('program exercise snapshots step chaining setting from exercise', () async {
final exerciseRepository = _FakeExerciseRepository()
..exercise = Exercise(
metadata: _metadata('exercise-1'),
name: 'Circuit',
hasTimeMeasure: false,
hasRepsMeasure: true,
hasScoreMeasure: false,
defaultTargetReps: 10,
autoStartNextTimedStep: false,
);
final programRepository = _FakeProgramRepository()
..programs.add(
Program(
metadata: _metadata('program-1'),
name: 'Programme',
defaultRestSeconds: 60,
),
);
final useCase = ProgramUseCases(
programRepository: programRepository,
exerciseRepository: exerciseRepository,
templateRepository: _FakeWorkoutTemplateRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
final snapshot = await useCase.addExercise(
programId: 'program-1',
exerciseId: 'exercise-1',
position: 0,
setsCount: 3,
enabledMeasures: const {WorkoutMeasure.reps},
);
expect(snapshot.autoStartNextTimedStepSnapshot, isFalse);
expect(snapshot.autoStartNextTimedStepOverride, isNull);
expect(
snapshot.toSnapshotJson()['autoStartNextTimedStepSnapshot'],
isFalse,
);
});
test('program saveConfigured preserves step chaining override', () async {
final programRepository = _FakeProgramRepository();
final useCase = ProgramUseCases(
programRepository: programRepository,
exerciseRepository: _FakeExerciseRepository(),
templateRepository: _FakeWorkoutTemplateRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
final program = await useCase.saveConfigured(
name: 'Programme',
defaultRestSeconds: 60,
exercises: const [
ProgramExerciseConfig(
exerciseNameSnapshot: 'Circuit',
availableTimeSnapshot: false,
availableRepsSnapshot: true,
availableScoreSnapshot: false,
setsCount: 3,
enabledMeasures: {WorkoutMeasure.reps},
targetReps: 10,
autoStartNextTimedStepSnapshot: true,
autoStartNextTimedStepOverride: false,
),
],
);
expect(program.exercises.single.autoStartNextTimedStepSnapshot, isTrue);
expect(program.exercises.single.autoStartNextTimedStepOverride, isFalse);
});
test('workout template override preserves step chaining override', () async {
final templateRepository = _FakeWorkoutTemplateRepository();
final useCase = WorkoutTemplateUseCases(
templateRepository: templateRepository,
programRepository: _FakeProgramRepository(),
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
ids: _FakeIds(),
originDeviceId: 'device-1',
);
final template = await useCase.saveConfigured(
name: 'Séance',
programs: [
WorkoutTemplateProgramConfig(
clientKey: 'program-1',
programNameSnapshot: 'Programme',
defaultRestSecondsSnapshot: 60,
programSnapshotJson: jsonEncode({
'exercises': [
{
'id': 'exercise-snapshot-1',
'exerciseNameSnapshot': 'Circuit',
'setsCount': 3,
'timeEnabled': false,
'repsEnabled': true,
'scoreEnabled': false,
},
],
}),
),
],
overrides: const [
WorkoutTemplateExerciseOverrideConfig(
workoutTemplateProgramClientKey: 'program-1',
snapshotProgramExerciseId: 'exercise-snapshot-1',
autoStartNextTimedStepOverride: false,
),
],
);
expect(
template.overrides.single.autoStartNextTimedStepOverride,
isFalse,
);
});
test('delete exercise keeps existing program snapshots unchanged', () async {
final exerciseRepository = _FakeExerciseRepository()
..exercise = Exercise(