MotorSignal-App/test/field_test_logger_test.dart

147 lines
4.3 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:drift/native.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:motorcycle_dash/database/database.dart' hide TelemetrySample;
import 'package:motorcycle_dash/models/field_test_event.dart';
import 'package:motorcycle_dash/services/field_test/field_test_logger.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late AppDatabase db;
late FieldTestLogger logger;
setUp(() async {
SharedPreferences.setMockInitialValues({});
db = AppDatabase.forTesting(NativeDatabase.memory());
logger = FieldTestLogger(db);
});
tearDown(() async {
logger.dispose();
await db.close();
});
group('Field Test Logger API Tests', () {
test('Session lifecycle starts and stops cleanly', () async {
logger.startSession(10);
expect(logger.isSessionActive, isTrue);
expect(logger.activeRideId, 10);
expect(
logger.timelineEvents.any(
(e) => e.eventType == FieldTestEventType.rideStarted,
),
isTrue,
);
final summary = await logger.stopSession();
expect(logger.isSessionActive, isFalse);
expect(summary.rideId, 10);
});
test(
'Queue drops lowest-priority events when buffer capacity is reached',
() {
logger.startSession(11);
// Fill queue with 1,000 info events
for (int i = 0; i < 1005; i++) {
logger.log(
eventType: FieldTestEventType.databaseFlush,
severity: EventSeverity.info,
message: 'Flush event $i',
rideId: 11,
);
}
// Add a critical event
logger.log(
eventType: FieldTestEventType.databaseRecovery,
severity: EventSeverity.critical,
message: 'Critical DB Recovery',
rideId: 11,
);
expect(logger.droppedEventsCount, greaterThanOrEqualTo(5));
expect(
logger.timelineEvents.any(
(e) => e.severity == EventSeverity.critical,
),
isTrue,
);
},
);
test(
'Dropped diagnostic events alone do not inflate databaseSamplesLost',
() async {
logger.startSession(12);
// Saturate diagnostic event queue so diagnostic events are dropped
for (int i = 0; i < 1010; i++) {
logger.log(
eventType: FieldTestEventType.databaseFlush,
severity: EventSeverity.info,
message: 'Flush info event $i',
rideId: 12,
);
}
expect(logger.droppedEventsCount, greaterThanOrEqualTo(10));
// Zero telemetry lost recorded
expect(logger.telemetrySamplesLost, 0);
final summary = await logger.stopSession();
// databaseSamplesLost MUST be 0 despite droppedEventsCount > 0
expect(summary.databaseSamplesLost, 0);
},
);
test(
'Recorded telemetry loss is preserved across diagnostic event drops and reflected in summary',
() async {
logger.startSession(13);
// Record 101 telemetry drops
logger.recordTelemetryLoss(101);
expect(logger.telemetrySamplesLost, 101);
// Saturate diagnostic event queue with warning burst
for (int i = 0; i < 1020; i++) {
logger.log(
eventType: FieldTestEventType.databaseQueueOverflow,
severity: EventSeverity.warning,
message: 'Overflow warning $i',
rideId: 13,
);
}
expect(logger.droppedEventsCount, greaterThanOrEqualTo(20));
// Telemetry loss tally must NOT be corrupted by diagnostic event drops
expect(logger.telemetrySamplesLost, 101);
final summary = await logger.stopSession();
expect(summary.databaseSamplesLost, 101);
},
);
test('Starting a new session resets telemetry loss tally', () async {
logger.startSession(14);
logger.recordTelemetryLoss(55);
expect(logger.telemetrySamplesLost, 55);
final summary1 = await logger.stopSession();
expect(summary1.databaseSamplesLost, 55);
// Start new session #15
logger.startSession(15);
expect(logger.telemetrySamplesLost, 0);
final summary2 = await logger.stopSession();
expect(summary2.databaseSamplesLost, 0);
});
});
}