import 'package:flutter_test/flutter_test.dart'; import 'package:drift/native.dart'; import 'package:motorcycle_dash/models/telemetry_sample.dart'; import 'package:motorcycle_dash/services/validation/telemetry_validator.dart'; import 'package:motorcycle_dash/features/performance/performance_timer_service.dart'; import 'package:motorcycle_dash/database/database.dart' hide TelemetrySample; void main() { group('Phase 9 Forensic Validation: Performance Timer & Run Verification', () { late AppDatabase db; setUp(() { db = AppDatabase.forTesting(NativeDatabase.memory()); }); tearDown(() async { await db.close(); }); test('Performance timer ignores rejected samples during 0-60 sprint', () async { final samples = []; final stream = Stream.fromIterable(samples); final timer = PerformanceTimerService(db, stream); timer.setTarget(PerformanceTarget.sixty); expect(timer.state.state, PerformanceState.ready); }); test('Sample quality overall confidence weights GPS 40%, speed 30%, lean 20%, G-force 10%', () { final validator = TelemetryValidator(); final sample = TelemetrySample( timestamp: DateTime(2026, 8, 28, 12, 0, 0), latitude: 37.7749, longitude: -122.4194, speed: 50.0, gpsAccuracy: 3.0, gpsAgeMs: 50, leanAngle: 15.0, longitudinalG: 0.1, lateralG: 0.2, ); final quality = validator.validate(sample); expect(quality.gpsConfidence, 1.0); expect(quality.speedConfidence, 1.0); expect(quality.leanConfidence, 1.0); expect(quality.gForceConfidence, 1.0); expect(quality.overallConfidence, greaterThanOrEqualTo(0.95)); }); test('Sample quality correctly discounts degraded GPS freshness (1.5s age)', () { final validator = TelemetryValidator(); final sample = TelemetrySample( timestamp: DateTime(2026, 8, 28, 12, 0, 0), latitude: 37.7749, longitude: -122.4194, speed: 45.0, gpsAccuracy: 5.0, gpsAgeMs: 1500, // 1.5s old fix leanAngle: 0.0, ); final quality = validator.validate(sample); expect(quality.gpsConfidence, lessThanOrEqualTo(0.45)); expect(quality.isSuspect, isTrue); }); test('Sample quality flags reverse timestamp delta <= 0 as suspect', () { final validator = TelemetryValidator(); final baseTime = DateTime(2026, 8, 28, 12, 0, 0); validator.validate(TelemetrySample(timestamp: baseTime, speed: 30.0)); final reverseSample = TelemetrySample( timestamp: baseTime.subtract(const Duration(milliseconds: 50)), speed: 30.0, ); final quality = validator.validate(reverseSample); expect(quality.isSuspect, isTrue); expect(quality.reasons.any((r) => r.contains('Duplicate or reverse timestamp')), isTrue); }); test('Sample quality flags large telemetry gap > 2000ms', () { final validator = TelemetryValidator(); final baseTime = DateTime(2026, 8, 28, 12, 0, 0); validator.validate(TelemetrySample(timestamp: baseTime, speed: 30.0)); final gapSample = TelemetrySample( timestamp: baseTime.add(const Duration(milliseconds: 2500)), speed: 30.0, ); final quality = validator.validate(gapSample); expect(quality.isSuspect, isTrue); expect(validator.timestampGapCount, 1); expect(quality.reasons.any((r) => r.contains('Large telemetry gap')), isTrue); }); test('Teleportation filter handles legitimate highway distance in 1.0s without rejecting', () { final validator = TelemetryValidator(); final baseTime = DateTime(2026, 8, 28, 12, 0, 0); // Move 30 meters in 1.0s (67 MPH) validator.validate(TelemetrySample( timestamp: baseTime, latitude: 37.774900, longitude: -122.419400, speed: 67.0, gpsAccuracy: 3.0, )); final legitimateSample = TelemetrySample( timestamp: baseTime.add(const Duration(milliseconds: 1000)), latitude: 37.775170, // ~30 meters north longitude: -122.419400, speed: 67.0, gpsAccuracy: 3.0, ); final quality = validator.validate(legitimateSample); expect(quality.isValid, isTrue); expect(quality.isRejected, isFalse); }); test('Zero speed at standstill does not create false acceleration alerts', () { final validator = TelemetryValidator(); final sample = TelemetrySample( timestamp: DateTime(2026, 8, 28, 12, 0, 0), speed: 0.0, longitudinalG: 0.0, lateralG: 0.0, leanAngle: 0.0, ); final quality = validator.validate(sample); expect(quality.isStationary, isTrue); expect(quality.isAccelerating, isFalse); expect(quality.isBraking, isFalse); }); }); }