test(mobile): cover auth disconnect deduplication

Assert that a stream closure during NIP-42 authentication produces exactly one disconnect callback.

Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e
2026-07-14 11:55:10 -07:00
parent 024476edc4
commit 473986fe64
@@ -27,6 +27,30 @@ void main() {
expect(channel.closeCount, 1);
});
test('auth-phase stream closure reports one disconnection', () async {
final channel = _ReadyWebSocketChannel();
final disconnections = <Object?>[];
final socket = RelaySocket(
wsUrl: 'wss://relay.example',
nsec: null,
onMessage: (_) {},
onConnected: () => fail('socket must not connect'),
onDisconnected: disconnections.add,
channelFactory: (_) => channel,
);
final connecting = socket.connect();
await Future<void>.delayed(Duration.zero);
expect(socket.state, SocketState.authenticating);
await channel.closeStream();
await connecting;
expect(disconnections, hasLength(1));
expect(disconnections.single, isA<Exception>());
expect(socket.state, SocketState.disconnected);
});
test('hung handshake times out, closes, and reports disconnection', () async {
final channel = _HungWebSocketChannel();
final disconnected = Completer<Object?>();
@@ -67,6 +91,8 @@ class _ReadyWebSocketChannel implements WebSocketChannel {
@override
Stream<dynamic> get stream => _controller.stream;
Future<void> closeStream() => _controller.close();
@override
late final WebSocketSink sink = _RecordingWebSocketSink(
_controller.sink,