Files
maps-saas/apps/siro_maps/test/telemetry_tracking_test.dart
T

211 lines
7.2 KiB
Dart

import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:siro_maps/core/services/device_fingerprint_service.dart';
import 'package:siro_maps/core/services/telemetry_tracking_service.dart';
import 'package:siro_maps/data/repositories/map_saas_repository.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late TelemetryTrackingService telemetryService;
setUp(() {
SharedPreferences.setMockInitialValues({});
telemetryService = TelemetryTrackingService.instance;
telemetryService.resetForTesting();
DeviceFingerprintService.instance.setMockState(
fingerprint: 'siro_test_hw_unit_test_device_123',
);
});
group('TelemetryTrackingService Sampling & Stationary Filter Tests', () {
test('Samples position only if at least 3 seconds have passed', () {
final baseTime = DateTime(2026, 9, 15, 12, 0, 0);
// First point at T=0
final p1 = telemetryService.recordPosition(
latitude: 31.9539,
longitude: 35.9106,
speedKmH: 45.0,
heading: 90.0,
now: baseTime,
);
expect(p1, isTrue);
expect(telemetryService.bufferedPointCount, equals(1));
// Attempt second point after only 1 second (T=1s) -> Should be rejected
final p2 = telemetryService.recordPosition(
latitude: 31.9540,
longitude: 35.9107,
speedKmH: 46.0,
heading: 90.0,
now: baseTime.add(const Duration(seconds: 1)),
);
expect(p2, isFalse);
expect(telemetryService.bufferedPointCount, equals(1));
// Attempt third point after 3 seconds (T=3s) -> Should be accepted
final p3 = telemetryService.recordPosition(
latitude: 31.9545,
longitude: 35.9110,
speedKmH: 48.0,
heading: 92.0,
now: baseTime.add(const Duration(seconds: 3)),
);
expect(p3, isTrue);
expect(telemetryService.bufferedPointCount, equals(2));
});
test('Smart stationary filter suppresses redundant points when vehicle is stopped', () {
final baseTime = DateTime(2026, 9, 15, 12, 0, 0);
// Stop at intersection: initial point (speed = 0)
final initial = telemetryService.recordPosition(
latitude: 31.9500,
longitude: 35.9100,
speedKmH: 0.0,
heading: 0.0,
now: baseTime,
);
expect(initial, isTrue);
expect(telemetryService.bufferedPointCount, equals(1));
// After 5 seconds: still at same spot (speed = 0, distance = 0) -> filter out
final duplicateAfter5s = telemetryService.recordPosition(
latitude: 31.9500,
longitude: 35.9100,
speedKmH: 0.0,
heading: 0.0,
now: baseTime.add(const Duration(seconds: 5)),
);
expect(duplicateAfter5s, isFalse);
expect(telemetryService.bufferedPointCount, equals(1));
// After 35 seconds: still at same spot -> 30s heartbeat triggers!
final heartbeatAfter35s = telemetryService.recordPosition(
latitude: 31.9500,
longitude: 35.9100,
speedKmH: 0.0,
heading: 0.0,
now: baseTime.add(const Duration(seconds: 35)),
);
expect(heartbeatAfter35s, isTrue);
expect(telemetryService.bufferedPointCount, equals(2));
});
});
group('Telemetry Batch Upload & Offline Buffer Tests', () {
test('Flushes batched telemetry to server in a single request', () async {
List<dynamic> receivedPayloadPoints = [];
final mockClient = MockClient((request) async {
if (request.url.path.contains('/api/telemetry/batch')) {
final body = jsonDecode(request.body) as Map<String, dynamic>;
receivedPayloadPoints = body['points'] as List<dynamic>;
return http.Response(jsonEncode({'success': true, 'count': receivedPayloadPoints.length}), 201);
}
return http.Response('Not Found', 404);
});
final mockRepo = MapSaasRepository(client: mockClient);
// Record 3 points spaced by 3 seconds
final baseTime = DateTime(2026, 9, 15, 12, 0, 0);
telemetryService.recordPosition(
latitude: 31.951,
longitude: 35.911,
speedKmH: 30,
heading: 45,
now: baseTime,
);
telemetryService.recordPosition(
latitude: 31.952,
longitude: 35.912,
speedKmH: 35,
heading: 45,
now: baseTime.add(const Duration(seconds: 3)),
);
telemetryService.recordPosition(
latitude: 31.953,
longitude: 35.913,
speedKmH: 40,
heading: 50,
now: baseTime.add(const Duration(seconds: 6)),
);
expect(telemetryService.bufferedPointCount, equals(3));
// Flush buffer to backend
final success = await telemetryService.flush(repository: mockRepo);
expect(success, isTrue);
expect(receivedPayloadPoints.length, equals(3));
// Buffer should be empty after successful flush
expect(telemetryService.bufferedPointCount, equals(0));
});
test('Retains points in offline buffer if batch upload encounters error', () async {
final offlineClient = MockClient((request) async {
return http.Response('Gateway Timeout', 504);
});
final mockRepo = MapSaasRepository(client: offlineClient);
telemetryService.recordPosition(
latitude: 31.951,
longitude: 35.911,
speedKmH: 20,
heading: 10,
now: DateTime(2026, 9, 15, 12, 0, 0),
);
expect(telemetryService.bufferedPointCount, equals(1));
final success = await telemetryService.flush(repository: mockRepo);
expect(success, isFalse);
// Points must still be retained for next attempt
expect(telemetryService.bufferedPointCount, equals(1));
});
});
group('10-Day Periodic Updates & Maintenance Tests', () {
test('Skips update if less than 10 days since last check', () async {
final now = DateTime(2026, 9, 15, 12, 0, 0);
final lastCheck = now.subtract(const Duration(days: 4)); // Only 4 days ago
SharedPreferences.setMockInitialValues({
TelemetryTrackingService.prefKeyLastPeriodicCheck: lastCheck.millisecondsSinceEpoch,
});
final prefs = await SharedPreferences.getInstance();
final executed = await telemetryService.checkPeriodicUpdates(
now: now,
mockPrefs: prefs,
);
expect(executed, isFalse);
});
test('Executes maintenance cycle if 10 or more days elapsed', () async {
final now = DateTime(2026, 9, 15, 12, 0, 0);
final lastCheck = now.subtract(const Duration(days: 12)); // 12 days ago
SharedPreferences.setMockInitialValues({
TelemetryTrackingService.prefKeyLastPeriodicCheck: lastCheck.millisecondsSinceEpoch,
});
final prefs = await SharedPreferences.getInstance();
final executed = await telemetryService.checkPeriodicUpdates(
now: now,
mockPrefs: prefs,
);
expect(executed, isTrue);
final updatedTimestamp = prefs.getInt(TelemetryTrackingService.prefKeyLastPeriodicCheck);
expect(updatedTimestamp, equals(now.millisecondsSinceEpoch));
});
});
}