Skip to content

Commit 35bdbe4

Browse files
committed
wip: test for two sets of servers and clients sharing sockets
* Related #14 [ci skip]
1 parent dfb8cc9 commit 35bdbe4

File tree

1 file changed

+211
-2
lines changed

1 file changed

+211
-2
lines changed

tests/concurrency.test.ts

Lines changed: 211 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type * as events from '@/events';
22
import type QUICStream from '@/QUICStream';
33
import type { Crypto, Host, Port } from '@';
4+
import type { TlsConfig } from '@/config';
45
import { fc, testProp } from '@fast-check/jest';
56
import Logger, { formatting, LogLevel, StreamHandler } from '@matrixai/logger';
67
import QUICServer from '@/QUICServer';
@@ -133,7 +134,7 @@ describe('Concurrency tests', () => {
133134
fc.array(streamArb, { size: 'small', minLength }).noShrink();
134135
const connectionArb = fc
135136
.record({
136-
streams: streamsArb(),
137+
streams: streamsArb(1),
137138
startDelay: fc.integer({ min: 0, max: 50 }),
138139
endDelay: fc.integer({ min: 0, max: 50 }),
139140
})
@@ -257,7 +258,7 @@ describe('Concurrency tests', () => {
257258
},
258259
{ numRuns: 3 },
259260
);
260-
testProp.only(
261+
testProp(
261262
'Multiple clients sharing a socket',
262263
[tlsConfigWithCaArb, connectionsArb, streamsArb(3)],
263264
async (tlsConfigProm, clientDatas, serverStreams) => {
@@ -376,8 +377,216 @@ describe('Concurrency tests', () => {
376377
)}`,
377378
);
378379
}
380+
await socket.stop();
379381
logger.info('TEST FULLY DONE!');
380382
},
381383
{ numRuns: 3 },
382384
);
385+
const spawnServer = async ({
386+
socket,
387+
port,
388+
cleanUpHoldProm,
389+
tlsConfig,
390+
serverStreams,
391+
}: {
392+
socket: QUICSocket | undefined;
393+
port: Port | undefined;
394+
cleanUpHoldProm: Promise<void>;
395+
tlsConfig: TlsConfig;
396+
serverStreams: Array<StreamData>;
397+
}) => {
398+
const server = new QUICServer({
399+
crypto,
400+
socket,
401+
logger: logger.getChild(QUICServer.name),
402+
config: {
403+
tlsConfig: tlsConfig,
404+
verifyPeer: false,
405+
},
406+
});
407+
const connProms: Array<Promise<void>> = [];
408+
server.addEventListener(
409+
'connection',
410+
async (e: events.QUICServerConnectionEvent) => {
411+
const conn = e.detail;
412+
const connProm = (async () => {
413+
const serverStreamProms: Array<Promise<void>> = [];
414+
conn.addEventListener(
415+
'stream',
416+
(streamEvent: events.QUICConnectionStreamEvent) => {
417+
const stream = streamEvent.detail;
418+
const streamData =
419+
serverStreams[serverStreamProms.length % serverStreams.length];
420+
serverStreamProms.push(handleStreamProm(stream, streamData));
421+
},
422+
);
423+
try {
424+
await cleanUpHoldProm;
425+
await Promise.race([
426+
Promise.all(serverStreamProms),
427+
sleep(1000).then(() => {
428+
throw Error('Streams hanging');
429+
}),
430+
]);
431+
} finally {
432+
await conn.destroy({ force: true });
433+
logger.info(
434+
`server conn result ${JSON.stringify(
435+
await Promise.allSettled(serverStreamProms),
436+
)}`,
437+
);
438+
}
439+
})();
440+
connProms.push(connProm);
441+
},
442+
);
443+
await sleep(100);
444+
await server.start({
445+
host: '127.0.0.1' as Host,
446+
port,
447+
});
448+
try {
449+
await cleanUpHoldProm;
450+
await Promise.all(connProms);
451+
} finally {
452+
await server.stop({ force: true });
453+
logger.info(
454+
`server result ${JSON.stringify(await Promise.allSettled(connProms))}`,
455+
);
456+
}
457+
};
458+
jest.setTimeout(100000);
459+
testProp.only(
460+
'Multiple clients sharing a socket with a server',
461+
[
462+
tlsConfigWithCaArb,
463+
tlsConfigWithCaArb,
464+
connectionsArb,
465+
connectionsArb,
466+
streamsArb(3),
467+
streamsArb(3),
468+
],
469+
async (
470+
tlsConfigProm1,
471+
tlsConfigProm2,
472+
clientDatas1,
473+
clientDatas2,
474+
serverStreams1,
475+
serverStreams2,
476+
) => {
477+
const tlsConfig1 = await tlsConfigProm1;
478+
const tlsConfig2 = await tlsConfigProm2;
479+
const cleanUpHoldProm = promise<void>();
480+
// Creating socket
481+
const socket1 = new QUICSocket({
482+
crypto,
483+
logger: logger.getChild('socket'),
484+
});
485+
const socket2 = new QUICSocket({
486+
crypto,
487+
logger: logger.getChild('socket'),
488+
});
489+
await socket1.start({
490+
host: '127.0.0.1' as Host,
491+
});
492+
await socket2.start({
493+
host: '127.0.0.1' as Host,
494+
});
495+
496+
const serverProm1 = spawnServer({
497+
cleanUpHoldProm: cleanUpHoldProm.p,
498+
port: undefined,
499+
serverStreams: serverStreams1,
500+
socket: socket1,
501+
tlsConfig: tlsConfig1.tlsConfig,
502+
});
503+
const serverProm2 = spawnServer({
504+
cleanUpHoldProm: cleanUpHoldProm.p,
505+
port: undefined,
506+
serverStreams: serverStreams2,
507+
socket: socket2,
508+
tlsConfig: tlsConfig2.tlsConfig,
509+
});
510+
511+
// Creating client activity
512+
logger.info('STARTING CLIENTS');
513+
const clientProms1: Array<Promise<void>> = [];
514+
const clientProms2: Array<Promise<void>> = [];
515+
for (const clientData of clientDatas1) {
516+
const clientProm = sleep(clientData.startDelay)
517+
.then(() => {
518+
logger.info('STARTING CLIENT');
519+
return QUICClient.createQUICClient({
520+
host: '127.0.0.1' as Host,
521+
port: socket2.port,
522+
socket: socket1,
523+
crypto,
524+
logger: logger.getChild(QUICClient.name),
525+
config: {
526+
verifyPeer: false,
527+
},
528+
});
529+
})
530+
.then((client) => {
531+
return handleClientProm(client, clientData);
532+
});
533+
clientProms1.push(clientProm);
534+
}
535+
for (const clientData of clientDatas2) {
536+
const clientProm = sleep(clientData.startDelay)
537+
.then(() => {
538+
logger.info('STARTING CLIENT');
539+
return QUICClient.createQUICClient({
540+
host: '127.0.0.1' as Host,
541+
port: socket1.port,
542+
socket: socket2,
543+
crypto,
544+
logger: logger.getChild(QUICClient.name),
545+
config: {
546+
verifyPeer: false,
547+
},
548+
});
549+
})
550+
.then((client) => {
551+
return handleClientProm(client, clientData);
552+
});
553+
clientProms2.push(clientProm);
554+
}
555+
// Wait for running activity to finish, should complete without error
556+
logger.info('STARTING TEST');
557+
try {
558+
await (async () => {
559+
await Promise.all([
560+
Promise.all(clientProms1),
561+
Promise.all(clientProms2),
562+
]);
563+
// Allow for streams to be negotiated
564+
await sleep(200);
565+
cleanUpHoldProm.resolveP();
566+
await serverProm1;
567+
await serverProm2;
568+
})();
569+
} catch (e) {
570+
logger.error(`test failed with ${e.message}`);
571+
throw e;
572+
} finally {
573+
logger.info('STARTING TEST FINALLY');
574+
cleanUpHoldProm.resolveP();
575+
logger.info(
576+
`test result ${JSON.stringify(
577+
await Promise.allSettled([
578+
...clientProms1,
579+
...clientProms2,
580+
serverProm1,
581+
serverProm2,
582+
]),
583+
)}`,
584+
);
585+
}
586+
await socket1.stop();
587+
await socket2.stop();
588+
logger.info('TEST FULLY DONE!');
589+
},
590+
{ numRuns: 1 },
591+
);
383592
});

0 commit comments

Comments
 (0)