feat(core): consolide le chainage type metier d'exercice -> strategie Health Services -> payload montre -> consommation Kotlin
Finalise #183 : modele/persistance des types metier d'exercice, mapping vers la strategie Health Services, propagation du payload dans le contrat watch bridge et consommation cote Kotlin montre. Perimetre complet QA vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -54,6 +54,29 @@ void main() {
|
||||
expect(projection.dominantTimer, isNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'projects Health Services exercise type strategy from snapshot',
|
||||
() async {
|
||||
final repository = _FakeActiveSessionRepository()
|
||||
..session = _session(
|
||||
healthServicesExerciseTypeStrategy: const [
|
||||
'RUNNING',
|
||||
'HIGH_INTENSITY_INTERVAL_TRAINING',
|
||||
'WORKOUT',
|
||||
],
|
||||
);
|
||||
final projector = _projector(repository, _clock());
|
||||
|
||||
final projection = await projector.project(revision: 1);
|
||||
|
||||
expect(projection.healthServicesExerciseTypeStrategy, [
|
||||
'RUNNING',
|
||||
'HIGH_INTENSITY_INTERVAL_TRAINING',
|
||||
'WORKOUT',
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test('projects running with step timer before stopwatch score', () async {
|
||||
final now = DateTime.utc(2026, 7, 25, 12);
|
||||
final session = _session(
|
||||
@ -587,6 +610,7 @@ ActiveWorkoutSession _session({
|
||||
bool? autoStartNextTimedStepSnapshot = true,
|
||||
List<ExerciseStep> steps = const [],
|
||||
String? secondExerciseName,
|
||||
List<String> healthServicesExerciseTypeStrategy = const [],
|
||||
}) {
|
||||
final exerciseSnapshot = {
|
||||
'id': 'exercise-snapshot-1',
|
||||
@ -603,6 +627,7 @@ ActiveWorkoutSession _session({
|
||||
.map((step) => step.toSnapshotJson())
|
||||
.toList(),
|
||||
'autoStartNextTimedStepSnapshot': ?autoStartNextTimedStepSnapshot,
|
||||
'healthServicesExerciseTypeStrategy': healthServicesExerciseTypeStrategy,
|
||||
};
|
||||
final secondExerciseSnapshot = secondExerciseName == null
|
||||
? null
|
||||
|
||||
@ -125,6 +125,10 @@ void main() {
|
||||
|
||||
expect(await columnNames('exercises'), contains('tags_json'));
|
||||
expect(await columnNames('exercises'), contains('business_types_json'));
|
||||
expect(
|
||||
await columnNames('program_exercises'),
|
||||
contains('health_services_exercise_type_strategy_snapshot_json'),
|
||||
);
|
||||
expect(await columnNames('programs'), contains('tags_json'));
|
||||
expect(await columnNames('workout_templates'), contains('tags_json'));
|
||||
expect(
|
||||
@ -175,6 +179,48 @@ void main() {
|
||||
BusinessExerciseType.dribble,
|
||||
BusinessExerciseType.highIntensity,
|
||||
]);
|
||||
expect(restoredExercise?.healthServicesExerciseTypeStrategy, [
|
||||
HealthServicesExerciseType.running,
|
||||
HealthServicesExerciseType.highIntensityIntervalTraining,
|
||||
HealthServicesExerciseType.workout,
|
||||
]);
|
||||
|
||||
final program = Program(
|
||||
metadata: _metadata('program-business-types', now),
|
||||
name: 'Programme types',
|
||||
defaultRestSeconds: 30,
|
||||
exercises: [
|
||||
ProgramExercise.snapshotFromExercise(
|
||||
metadata: _metadata('program-exercise-business-types', now),
|
||||
programId: 'program-business-types',
|
||||
exercise: restoredExercise!,
|
||||
position: 0,
|
||||
setsCount: 1,
|
||||
enabledMeasures: const {WorkoutMeasure.time},
|
||||
),
|
||||
],
|
||||
);
|
||||
await programRepository.save(program);
|
||||
|
||||
final restoredProgram = await programRepository.findById(
|
||||
program.metadata.id,
|
||||
);
|
||||
expect(
|
||||
restoredProgram
|
||||
?.exercises
|
||||
.single
|
||||
.healthServicesExerciseTypeStrategySnapshot,
|
||||
[
|
||||
HealthServicesExerciseType.running,
|
||||
HealthServicesExerciseType.highIntensityIntervalTraining,
|
||||
HealthServicesExerciseType.workout,
|
||||
],
|
||||
);
|
||||
expect(
|
||||
restoredProgram?.exercises.single
|
||||
.toSnapshotJson()['healthServicesExerciseTypeStrategy'],
|
||||
['RUNNING', 'HIGH_INTENSITY_INTERVAL_TRAINING', 'WORKOUT'],
|
||||
);
|
||||
|
||||
final legacyExercise = Exercise(
|
||||
metadata: _metadata('exercise-business-types-legacy', now),
|
||||
@ -193,6 +239,12 @@ void main() {
|
||||
expect(restoredLegacy?.effectiveBusinessTypes, [
|
||||
BusinessExerciseType.shoot,
|
||||
]);
|
||||
expect(restoredLegacy?.healthServicesExerciseTypeStrategy, [
|
||||
HealthServicesExerciseType.highIntensityIntervalTraining,
|
||||
HealthServicesExerciseType.running,
|
||||
HealthServicesExerciseType.walking,
|
||||
HealthServicesExerciseType.workout,
|
||||
]);
|
||||
});
|
||||
|
||||
test(
|
||||
@ -595,6 +647,23 @@ CREATE TABLE pending_share_actions (
|
||||
],
|
||||
);
|
||||
await exerciseRepository.save(updatedExercise);
|
||||
await programRepository.save(
|
||||
Program(
|
||||
metadata: _metadata('program-sync-business-types', now),
|
||||
name: 'Program business types',
|
||||
defaultRestSeconds: 45,
|
||||
exercises: [
|
||||
ProgramExercise.snapshotFromExercise(
|
||||
metadata: _metadata('program-exercise-sync-business-types', now),
|
||||
programId: 'program-sync-business-types',
|
||||
exercise: updatedExercise,
|
||||
position: 0,
|
||||
setsCount: 1,
|
||||
enabledMeasures: const {WorkoutMeasure.time},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final changes = await syncChangeRepository.listPendingChanges();
|
||||
final payloadsById = {
|
||||
@ -605,6 +674,13 @@ CREATE TABLE pending_share_actions (
|
||||
'dribble',
|
||||
'finishing',
|
||||
]);
|
||||
final programExercises =
|
||||
payloadsById['program-sync-business-types']!['exercises'] as List;
|
||||
expect(programExercises.single['healthServicesExerciseTypeStrategy'], [
|
||||
'RUNNING',
|
||||
'HIGH_INTENSITY_INTERVAL_TRAINING',
|
||||
'WORKOUT',
|
||||
]);
|
||||
});
|
||||
|
||||
test('local sync payload includes full workout history aggregate', () async {
|
||||
|
||||
Reference in New Issue
Block a user