Corrige les bugs identifiés lors de la QA finale sur use_cases.dart et drift_repositories.dart, ajoute la couverture de tests d'infrastructure manquante (test/infrastructure/drift_repositories_test.dart). flutter analyze propre, 21/21 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
197 lines
5.9 KiB
Dart
197 lines
5.9 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:gametime/application/application.dart';
|
|
import 'package:gametime/domain/domain.dart';
|
|
import 'package:gametime/infrastructure/local/local.dart' as local;
|
|
|
|
void main() {
|
|
late local.AppDatabase database;
|
|
late local.DriftActiveSessionRepository activeRepository;
|
|
late local.DriftWorkoutTemplateRepository templateRepository;
|
|
late local.DriftWorkoutHistoryRepository historyRepository;
|
|
|
|
setUp(() {
|
|
database = local.AppDatabase(NativeDatabase.memory());
|
|
activeRepository = local.DriftActiveSessionRepository(database);
|
|
templateRepository = local.DriftWorkoutTemplateRepository(database);
|
|
historyRepository = local.DriftWorkoutHistoryRepository(database);
|
|
});
|
|
|
|
tearDown(() async {
|
|
await database.close();
|
|
});
|
|
|
|
test(
|
|
'running session elapsed time survives repository reconstruction',
|
|
() async {
|
|
final persistedAt = DateTime.utc(2026, 7, 17, 12);
|
|
final session = ActiveWorkoutSession(
|
|
metadata: _metadata('session-1', persistedAt),
|
|
sourceWorkoutTemplateId: null,
|
|
status: ActiveWorkoutStatus.running,
|
|
startedAt: persistedAt.subtract(const Duration(seconds: 30)),
|
|
lastPersistedAt: persistedAt,
|
|
elapsedActiveMs: 30000,
|
|
currentProgramIndex: 0,
|
|
currentExerciseIndex: 0,
|
|
currentSetIndex: 0,
|
|
resolvedTemplateSnapshotJson: _resolvedSnapshot(),
|
|
);
|
|
|
|
await activeRepository.save(session);
|
|
|
|
final afterAppKillRepository = local.DriftActiveSessionRepository(
|
|
database,
|
|
);
|
|
final afterAppKillUseCases = ActiveWorkoutSessionUseCases(
|
|
sessionRepository: afterAppKillRepository,
|
|
templateRepository: templateRepository,
|
|
clock: _FakeClock(persistedAt.add(const Duration(seconds: 20))),
|
|
ids: _FakeIds(),
|
|
originDeviceId: 'device-1',
|
|
);
|
|
|
|
final restored = await afterAppKillRepository.findOpen();
|
|
|
|
expect(restored, isNotNull);
|
|
expect(afterAppKillUseCases.elapsedActiveMilliseconds(restored!), 50000);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'closing a session stores autonomous history rows with set snapshots',
|
|
() async {
|
|
final now = DateTime.utc(2026, 7, 17, 12);
|
|
final template = WorkoutTemplate(
|
|
metadata: _metadata('template-1', now),
|
|
name: 'Séance jambes',
|
|
);
|
|
await templateRepository.save(template);
|
|
|
|
final session = ActiveWorkoutSession(
|
|
metadata: _metadata('session-1', now),
|
|
sourceWorkoutTemplateId: template.metadata.id,
|
|
status: ActiveWorkoutStatus.running,
|
|
startedAt: now.subtract(const Duration(minutes: 10)),
|
|
lastPersistedAt: now,
|
|
elapsedActiveMs: 600000,
|
|
currentProgramIndex: 0,
|
|
currentExerciseIndex: 0,
|
|
currentSetIndex: 0,
|
|
resolvedTemplateSnapshotJson: _resolvedSnapshot(),
|
|
);
|
|
await activeRepository.save(session);
|
|
await activeRepository.saveSetResult(
|
|
ActiveSetResult(
|
|
metadata: _metadata('active-result-1', now),
|
|
activeWorkoutSessionId: session.metadata.id,
|
|
programSnapshotId: 'program-snapshot-1',
|
|
exerciseSnapshotId: 'exercise-snapshot-1',
|
|
programIndex: 0,
|
|
exerciseIndex: 0,
|
|
setIndex: 0,
|
|
actualTimeMs: 45000,
|
|
actualReps: 10,
|
|
actualScore: 80,
|
|
scoreLabelSnapshot: 'Charge',
|
|
scoreUnitSnapshot: 'kg',
|
|
completedAt: now,
|
|
),
|
|
);
|
|
|
|
final closeUseCase = CloseWorkoutSessionUseCase(
|
|
sessionRepository: activeRepository,
|
|
historyRepository: historyRepository,
|
|
clock: _FakeClock(now.add(const Duration(seconds: 5))),
|
|
ids: _FakeIds(),
|
|
originDeviceId: 'device-1',
|
|
);
|
|
|
|
final history = await closeUseCase.close(
|
|
sessionId: session.metadata.id,
|
|
nameSnapshot: 'Séance jambes',
|
|
completed: true,
|
|
);
|
|
await templateRepository.save(
|
|
WorkoutTemplate(
|
|
metadata: template.metadata.markDeleted(now),
|
|
name: template.name,
|
|
),
|
|
);
|
|
|
|
final restored = await historyRepository.findById(history.metadata.id);
|
|
|
|
expect(restored, isNotNull);
|
|
expect(restored!.nameSnapshot, 'Séance jambes');
|
|
expect(restored.results, hasLength(1));
|
|
expect(restored.results.single.programNameSnapshot, 'Programme jambes');
|
|
expect(restored.results.single.exerciseNameSnapshot, 'Squat');
|
|
expect(restored.results.single.actualTimeMs, 45000);
|
|
expect(restored.results.single.actualReps, 10);
|
|
expect(restored.results.single.actualScore, 80);
|
|
expect(restored.results.single.scoreUnitSnapshot, 'kg');
|
|
},
|
|
);
|
|
}
|
|
|
|
String _resolvedSnapshot() {
|
|
return jsonEncode({
|
|
'name': 'Séance jambes',
|
|
'programs': [
|
|
{
|
|
'id': 'program-snapshot-1',
|
|
'programNameSnapshot': 'Programme jambes',
|
|
'programSnapshotJson': jsonEncode({
|
|
'exercises': [
|
|
{
|
|
'id': 'exercise-snapshot-1',
|
|
'exerciseNameSnapshot': 'Squat',
|
|
'setsCount': 1,
|
|
'timeEnabled': true,
|
|
'repsEnabled': true,
|
|
'scoreEnabled': true,
|
|
'targetTimeSeconds': 45,
|
|
'targetReps': 10,
|
|
'targetScore': 80,
|
|
'scoreLabelSnapshot': 'Charge',
|
|
'scoreUnitSnapshot': 'kg',
|
|
'restSecondsOverride': 0,
|
|
},
|
|
],
|
|
}),
|
|
},
|
|
],
|
|
'overrides': const [],
|
|
});
|
|
}
|
|
|
|
EntityMetadata _metadata(String id, DateTime now) {
|
|
return EntityMetadata(
|
|
id: id,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
originDeviceId: 'device-1',
|
|
);
|
|
}
|
|
|
|
final class _FakeClock implements Clock {
|
|
const _FakeClock(this.value);
|
|
|
|
final DateTime value;
|
|
|
|
@override
|
|
DateTime now() => value;
|
|
}
|
|
|
|
final class _FakeIds implements IdGenerator {
|
|
var _next = 0;
|
|
|
|
@override
|
|
String newId() {
|
|
_next += 1;
|
|
return 'id-$_next';
|
|
}
|
|
}
|