import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:iop_client/src/integrations/nexo/nexo_notification_client.dart'; import 'package:iop_client/src/integrations/nexo/nexo_notification_host_integration.dart'; class _FakePushClient implements NexoNotificationClient { int initializeCalls = 0; int navigateChannelAssignments = 0; int navigateThreadAssignments = 0; final StreamController> _controller = StreamController>.broadcast(); void Function(String serverUrl, String channelId)? _navigateChannel; void Function(String serverUrl, String rootId)? _navigateThread; @override Stream> get onNotification => _controller.stream; void emit(Map data) => _controller.add(data); Future dispose() => _controller.close(); @override Future initialize() async { initializeCalls += 1; } @override Future getDeviceToken() async => null; @override Future setAuthToken( String serverUrl, String token, { String? identifier, }) async {} @override Future setSigningKey(String serverUrl, String signingKey) async {} @override set onDeviceTokenReady(Future Function(String token)? callback) {} @override set onNavigateToChannel( void Function(String serverUrl, String channelId)? callback, ) { navigateChannelAssignments += 1; _navigateChannel = callback; } @override set onNavigateToThread( void Function(String serverUrl, String rootId)? callback, ) { navigateThreadAssignments += 1; _navigateThread = callback; } void triggerNavigateChannel(String serverUrl, String channelId) { _navigateChannel?.call(serverUrl, channelId); } void triggerNavigateThread(String serverUrl, String rootId) { _navigateThread?.call(serverUrl, rootId); } } void main() { test('initialize calls push client init exactly once', () async { final push = _FakePushClient(); final host = NexoNotificationHostIntegration(pushClient: push); await host.initialize(); await host.initialize(); expect(push.initializeCalls, equals(1)); expect(host.isInitialized, isTrue); await push.dispose(); }); test( 'navigation callbacks are assigned exactly once during initialize', () async { final push = _FakePushClient(); final host = NexoNotificationHostIntegration(pushClient: push); String? capturedChannel; String? capturedThread; await host.initialize( onNavigateToChannel: (_, channelId) => capturedChannel = channelId, onNavigateToThread: (_, rootId) => capturedThread = rootId, ); expect(push.navigateChannelAssignments, equals(1)); expect(push.navigateThreadAssignments, equals(1)); push.triggerNavigateChannel('https://srv', 'channel-1'); push.triggerNavigateThread('https://srv', 'root-1'); expect(capturedChannel, equals('channel-1')); expect(capturedThread, equals('root-1')); await push.dispose(); }, ); test('notification stream remains consumable through integration', () async { final push = _FakePushClient(); final host = NexoNotificationHostIntegration(pushClient: push); await host.initialize(); final received = >[]; final sub = host.onNotification.listen(received.add); push.emit(const {'type': 'message', 'message': 'hi'}); await Future.delayed(Duration.zero); expect(received, hasLength(1)); expect(received.first['message'], equals('hi')); await sub.cancel(); await push.dispose(); }); }