import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:ground_suite/core/observations/observed_event.dart'; import 'package:ground_suite/core/observations/vehicle_data_port.dart'; import 'package:ground_suite/core/observations/vehicle_data_update.dart'; import 'package:ground_suite/features/observability/presentation/observability_controller.dart'; void main() { test('coalesces rapid critical accessibility announcements', () async { final port = _ControlledDataPort(); final container = ProviderContainer( overrides: [vehicleDataPortProvider.overrideWithValue(port)], ); addTearDown(container.dispose); container.read(observabilityControllerProvider); await Future.delayed(Duration.zero); port.events.add( ObservationReceived( ErrorEvent( receivedAt: DateTime.utc(2026, 8, 10), path: 'kaptan_drivers::lidar_lite_v3::error::Error::TransferFault', ), ), ); await Future.delayed(Duration.zero); expect( container.read(observabilityControllerProvider).accessibilityAnnouncement, 'Error received: kaptan_drivers::lidar_lite_v3::error::Error::TransferFault', ); port.events.add( ObservationReceived( ShutdownEvent( receivedAt: DateTime.utc(2026, 8, 10, 0, 0, 1), reason: KaptanShutdownReason.safety, ), ), ); await Future.delayed(Duration.zero); final state = container.read(observabilityControllerProvider); expect(state.accessibilityAnnouncement, isNull); expect(state.coalescedAlertCount, 1); }); test( 'retains expected parser and semantic drops without ending observation', () async { final port = _ControlledDataPort(); final container = ProviderContainer( overrides: [vehicleDataPortProvider.overrideWithValue(port)], ); addTearDown(container.dispose); container.read(observabilityControllerProvider); await Future.delayed(Duration.zero); port.events.add( const ObservationDropped(kind: ObservationDropKind.parser, count: 2), ); port.events.add( const ObservationDropped(kind: ObservationDropKind.semantic), ); await Future.delayed(Duration.zero); final state = container.read(observabilityControllerProvider); expect(state.status, ObservationStatus.receiving); expect(state.parserDropCount, 2); expect(state.semanticDropCount, 1); }, ); test('treats a transport stream failure as a disconnected link', () async { final port = _ControlledDataPort(); final container = ProviderContainer( overrides: [vehicleDataPortProvider.overrideWithValue(port)], ); addTearDown(container.dispose); container.read(observabilityControllerProvider); await Future.delayed(Duration.zero); port.events.addError(StateError('serial endpoint closed')); await Future.delayed(Duration.zero); final state = container.read(observabilityControllerProvider); expect(state.status, ObservationStatus.disconnected); expect(state.parserDropCount, 0); expect(state.semanticDropCount, 0); }); } class _ControlledDataPort implements VehicleDataPort { final events = StreamController(); @override bool get isReplay => false; @override String get label => 'Test transport'; @override Future close() => events.close(); @override Stream observe() => events.stream; }