diff --git a/src/client.ts b/src/client.ts index 5dc48a96..e87d1ad2 100644 --- a/src/client.ts +++ b/src/client.ts @@ -518,8 +518,8 @@ export function createClient(options: ClientOptions): Client { ); releaserRef.current = () => { - if (!completed) { - // if not completed already, send complete message to server on release + if (!completed && socket.readyState === WebSocketImpl.OPEN) { + // if not completed already and socket is open, send complete message to server on release socket.send( stringifyMessage({ id: id, diff --git a/src/tests/client.ts b/src/tests/client.ts index 1a8a4b4d..3f235695 100644 --- a/src/tests/client.ts +++ b/src/tests/client.ts @@ -284,6 +284,47 @@ it('should close the socket if the `connectionParams` rejects or throws', async }); }); +it('should not send the complete message if the socket is not open', async () => { + const { + url, + clients, + waitForOperation, + waitForClientClose, + } = await startTServer(); + + class MockWebSocket extends WebSocket { + constructor(...args: unknown[]) { + // @ts-expect-error Args will fit + super(...args); + } + + public send(data: unknown) { + if (this.readyState !== WebSocket.OPEN) + fail("Shouldn't send anything through a non-OPEN socket"); + super.send(data); + } + } + + const client = createClient({ + webSocketImpl: MockWebSocket, + url, + retryAttempts: 0, + onNonLazyError: noop, + }); + const sub = tsubscribe(client, { query: 'subscription { ping }' }); + await waitForOperation(); + + // kick the client off + for (const client of clients) { + client.close(); + await waitForClientClose(); + } + + // dispose of the subscription which should complete the connection + sub.dispose(); + await sub.waitForComplete(); +}); + describe('query operation', () => { it('should execute the query, "next" the result and then complete', async () => { const { url } = await startTServer();