Files
kaptan/ground-suite/test/features/observability/observability_controller_test.dart
T

113 lines
3.5 KiB
Dart

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<void>.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<void>.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<void>.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<void>.delayed(Duration.zero);
port.events.add(
const ObservationDropped(kind: ObservationDropKind.parser, count: 2),
);
port.events.add(
const ObservationDropped(kind: ObservationDropKind.semantic),
);
await Future<void>.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<void>.delayed(Duration.zero);
port.events.addError(StateError('serial endpoint closed'));
await Future<void>.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<VehicleDataUpdate>();
@override
bool get isReplay => false;
@override
String get label => 'Test transport';
@override
Future<void> close() => events.close();
@override
Stream<VehicleDataUpdate> observe() => events.stream;
}