nomadcode/apps/client/test/integrations/mattermost_push_host_integration_test.dart

174 lines
4.8 KiB
Dart

import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:nomadcode_app/src/integrations/mattermost/mattermost_auth_service.dart';
import 'package:nomadcode_app/src/integrations/mattermost/mattermost_push_client.dart';
import 'package:nomadcode_app/src/integrations/mattermost/mattermost_push_host_integration.dart';
class _FakePushClient implements MattermostPushClient {
int initializeCalls = 0;
int navigateChannelAssignments = 0;
int navigateThreadAssignments = 0;
final StreamController<Map<String, dynamic>> _controller =
StreamController<Map<String, dynamic>>.broadcast();
void Function(String serverUrl, String channelId)? _navigateChannel;
void Function(String serverUrl, String rootId)? _navigateThread;
@override
Stream<Map<String, dynamic>> get onNotification => _controller.stream;
void emit(Map<String, dynamic> data) => _controller.add(data);
Future<void> dispose() => _controller.close();
@override
Future<void> initialize() async {
initializeCalls += 1;
}
@override
Future<String?> getDeviceToken() async => null;
@override
Future<void> setAuthToken(
String serverUrl,
String token, {
String? identifier,
}) async {}
@override
Future<void> setSigningKey(String serverUrl, String signingKey) async {}
@override
set onDeviceTokenReady(Future<void> 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);
}
}
class _NoopAuthService implements MattermostAuthService {
int autoLoginCalls = 0;
final bool throwOnLogin;
_NoopAuthService({this.throwOnLogin = false});
@override
Future<void> autoLoginAndRegister() async {
autoLoginCalls += 1;
if (throwOnLogin) {
throw StateError('credentials missing');
}
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
void main() {
test('initialize calls push client init exactly once', () async {
final push = _FakePushClient();
final auth = _NoopAuthService();
final host = MattermostPushHostIntegration(
pushClient: push,
authServiceFactory: (_) => auth,
);
await host.initialize();
await host.initialize();
expect(push.initializeCalls, equals(1));
expect(auth.autoLoginCalls, equals(1));
expect(host.isInitialized, isTrue);
await push.dispose();
});
test('auto-login failure does not block initialize', () async {
final push = _FakePushClient();
final auth = _NoopAuthService(throwOnLogin: true);
final host = MattermostPushHostIntegration(
pushClient: push,
authServiceFactory: (_) => auth,
);
await host.initialize();
expect(host.isInitialized, isTrue);
expect(push.initializeCalls, equals(1));
await push.dispose();
});
test(
'navigation callbacks are assigned exactly once during initialize',
() async {
final push = _FakePushClient();
final host = MattermostPushHostIntegration(
pushClient: push,
authServiceFactory: (_) => _NoopAuthService(),
);
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 = MattermostPushHostIntegration(
pushClient: push,
authServiceFactory: (_) => _NoopAuthService(),
);
await host.initialize();
final received = <Map<String, dynamic>>[];
final sub = host.onNotification.listen(received.add);
push.emit(const {'type': 'message', 'message': 'hi'});
await Future<void>.delayed(Duration.zero);
expect(received, hasLength(1));
expect(received.first['message'], equals('hi'));
await sub.cancel();
await push.dispose();
});
}