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'; } }