Skip to content

Commit 80d3092

Browse files
committed
Add coverage improvements for web-socket-handler.
1 parent d46aea6 commit 80d3092

File tree

2 files changed

+113
-13
lines changed

2 files changed

+113
-13
lines changed

src/web-socket-handler.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,12 @@ export class WebSocketHandler implements WebSocketInterface {
137137
stdin: stream.Readable,
138138
streamNum: number = 0,
139139
retryCount: number = 3,
140+
// kind of hacky, but otherwise we can't wait for the writes to flush before testing.
141+
addFlushForTesting: boolean = false,
140142
): () => WebSocket.WebSocket | null {
141143
if (retryCount < 0) {
142144
throw new Error("retryCount can't be lower than 0.");
143145
}
144-
145146
let queue: Promise<void> = Promise.resolve();
146147
let ws: WebSocket.WebSocket | null = null;
147148

@@ -158,8 +159,14 @@ export class WebSocketHandler implements WebSocketInterface {
158159
});
159160
});
160161

162+
if (addFlushForTesting) {
163+
stdin.on('flush', async () => {
164+
await queue;
165+
});
166+
}
167+
161168
stdin.on('end', () => {
162-
if (ws) {
169+
if (ws !== null) {
163170
ws.close();
164171
}
165172
});

src/web-socket-handler_test.ts

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { deepStrictEqual, notStrictEqual, rejects, strictEqual, throws } from 'node:assert';
1+
import { deepStrictEqual, equal, notStrictEqual, rejects, strictEqual, throws } from 'node:assert';
22
import { Readable, Writable } from 'node:stream';
33
import { setImmediate as setImmediatePromise } from 'node:timers/promises';
44
import WebSocket from 'isomorphic-ws';
@@ -335,19 +335,30 @@ describe('V5 protocol support', () => {
335335
protocol: 'v5.channel.k8s.io',
336336
} as WebSocket.WebSocket;
337337
let uriOut = '';
338-
let endCalled = false;
338+
let stderrEndCalled = false;
339+
let stdoutEndCalled = false;
340+
let stdinPauseCalled = false;
339341
const handler = new WebSocketHandler(
340342
kc,
341343
(uri: string, protocols: string[], opts: WebSocket.ClientOptions): WebSocket.WebSocket => {
342344
uriOut = uri;
343345
return mockWs as WebSocket.WebSocket;
344346
},
345347
{
346-
stdin: process.stdin,
347-
stderr: process.stderr,
348+
stdin: {
349+
pause: () => {
350+
stdinPauseCalled = true;
351+
return {} as Readable;
352+
},
353+
} as Readable,
354+
stderr: {
355+
end: () => {
356+
stderrEndCalled = true;
357+
},
358+
} as Writable,
348359
stdout: {
349360
end: () => {
350-
endCalled = true;
361+
stdoutEndCalled = true;
351362
},
352363
} as Writable,
353364
},
@@ -364,17 +375,30 @@ describe('V5 protocol support', () => {
364375
type: 'open',
365376
};
366377
mockWs.onopen!(event);
367-
const closeBuff = Buffer.alloc(2);
368-
closeBuff.writeUint8(255, 0);
369-
closeBuff.writeUint8(WebSocketHandler.StdoutStream, 1);
370-
378+
// Close stdin/stdout with Buffers
379+
[WebSocketHandler.StdinStream, WebSocketHandler.StdoutStream].forEach((stream) => {
380+
const closeBuff = Buffer.alloc(2);
381+
closeBuff.writeUint8(255, 0);
382+
closeBuff.writeUint8(stream, 1);
383+
384+
mockWs.onmessage!({
385+
data: closeBuff,
386+
type: 'type',
387+
target: mockWs,
388+
});
389+
});
390+
// Close stderr with a string \xff is 'close' \x02 is the stderr stream number
391+
// so that both paths are tested.
392+
const closeMsg = '\xFF\x02';
371393
mockWs.onmessage!({
372-
data: closeBuff,
394+
data: closeMsg,
373395
type: 'type',
374396
target: mockWs,
375397
});
376398
await promise;
377-
strictEqual(endCalled, true);
399+
strictEqual(stdoutEndCalled, true);
400+
strictEqual(stderrEndCalled, true);
401+
strictEqual(stdinPauseCalled, true);
378402
});
379403
it('should handle closing stdin < v4 protocol', () => {
380404
const ws = {
@@ -436,4 +460,73 @@ describe('Restartable Handle Standard Input', () => {
436460
strictEqual(count, retryTimes);
437461
});
438462
});
463+
464+
it('should work correctly', async () => {
465+
let sent: Buffer | null = null;
466+
const ws = {
467+
protocol: 'v5.channel.k8s.io',
468+
send: (data) => {
469+
sent = data;
470+
},
471+
readyState: WebSocket.OPEN,
472+
close: () => {
473+
throw new Error('should not be called');
474+
},
475+
} as unknown as WebSocket;
476+
const p = new Promise<WebSocket>((resolve, reject) => resolve(ws));
477+
let dataCb: any;
478+
let endCb: any;
479+
let flushCb: any;
480+
481+
const r = {
482+
on: (verb, cb) => {
483+
if (verb === 'data') {
484+
dataCb = cb;
485+
}
486+
if (verb === 'end') {
487+
endCb = cb;
488+
}
489+
if (verb == 'flush') {
490+
flushCb = cb;
491+
}
492+
},
493+
} as Readable;
494+
495+
WebSocketHandler.restartableHandleStandardInput(() => p, r, 0, 4, true);
496+
497+
dataCb('some test data');
498+
endCb();
499+
await flushCb();
500+
501+
equal(sent!.toString(), '\x00some test data');
502+
});
503+
504+
it('should work if the web socket exists', () => {
505+
let sent: Buffer | null = null;
506+
const ws = {
507+
protocol: 'v5.channel.k8s.io',
508+
send: (data) => {
509+
sent = data;
510+
},
511+
readyState: WebSocket.OPEN,
512+
close: () => {
513+
throw new Error('should not be called');
514+
},
515+
} as unknown as WebSocket;
516+
let count = 0;
517+
WebSocketHandler.processData(
518+
'some test data',
519+
ws,
520+
(): Promise<WebSocket.WebSocket> => {
521+
return new Promise<WebSocket.WebSocket>((resolve) => {
522+
count++;
523+
resolve(ws as WebSocket.WebSocket);
524+
});
525+
},
526+
0,
527+
5,
528+
);
529+
equal(sent!.toString(), '\x00some test data');
530+
strictEqual(count, 0);
531+
});
439532
});

0 commit comments

Comments
 (0)