From 75655eefac3f200332408767be373a5338ea754d Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Wed, 29 May 2024 13:49:51 +1000 Subject: [PATCH] wip: adding tests [ci skip] --- src/audit/utils.ts | 7 +- tests/audit/utils.test.ts | 2 - tests/client/handlers/audit.test.ts | 298 +++++++++++++++++++++++++++- 3 files changed, 294 insertions(+), 13 deletions(-) diff --git a/src/audit/utils.ts b/src/audit/utils.ts index 8174918331..b24c9b59bd 100644 --- a/src/audit/utils.ts +++ b/src/audit/utils.ts @@ -188,7 +188,7 @@ const topicPaths = [ discoveryCheckRediscoveryTopicPath, ] as const; -// @ts-ignore +// @ts-ignore: recursive definition for defining a tree type TopicPathTreeNode = Record; function generateTopicPathTree() { @@ -261,7 +261,7 @@ function filterSubPaths(paths: Array): Array { * This takes N generators that yield data in a sorted order and combines their outputs in a fully sorted order. * This will only work on pre-sorted outputs from the generator. */ -async function* genSort( +async function* genSort( sortFn: (a: T, b: T) => number, ...gens: Array> ): AsyncGenerator { @@ -308,9 +308,6 @@ async function* genSort( // If the last head is done then we break if (heads.length === 0) return; } - } catch (e) { - console.error(e); - throw e; } finally { for (const { gen } of heads) { await gen.return(); diff --git a/tests/audit/utils.test.ts b/tests/audit/utils.test.ts index 29188ea719..4b19182fc0 100644 --- a/tests/audit/utils.test.ts +++ b/tests/audit/utils.test.ts @@ -58,7 +58,6 @@ describe('Audit Utils', () => { expect(filtered).not.toInclude('e.f'); expect(filtered).not.toInclude('e.g'); }); - test.prop([fc.array(orderedNumberArrayArb).noShrink()])( 'can combine strictly ordered iterators', async (generatorData) => { @@ -89,7 +88,6 @@ describe('Audit Utils', () => { expect(acc).toMatchObject(expectedDataArray); }, ); - test('isAuditPath', async () => { for (const topicPath of auditUtils.topicPaths) { expect(auditUtils.isTopicPath(topicPath)).toBeTrue(); diff --git a/tests/client/handlers/audit.test.ts b/tests/client/handlers/audit.test.ts index 63d8102e95..0539b1d3be 100644 --- a/tests/client/handlers/audit.test.ts +++ b/tests/client/handlers/audit.test.ts @@ -45,7 +45,6 @@ describe('auditEventGet', () => { let rpcClient: OverrideRPClientType< RPCClient<{ auditEventsGet: typeof auditEventsGet; - auditEventsMultiPathGet: typeof auditEventsMultiPathGet; }> >; let tlsConfig: TLSConfig; @@ -98,9 +97,6 @@ describe('auditEventGet', () => { auditEventsGet: new AuditEventsGet({ audit, }), - auditEventsMultiPathGet: new AuditEventsMultiPathGet({ - audit, - }), }, host: localhost, }); @@ -251,8 +247,287 @@ describe('auditEventGet', () => { } expect(results).toHaveLength(6); }); +}); + +describe('auditEventMultiPathGet', () => { + const logger = new Logger('auditEventsGet test', LogLevel.WARN, [ + new StreamHandler( + formatting.format`${formatting.level}:${formatting.keys}:${formatting.msg}`, + ), + ]); + const password = 'password'; + const localhost = '127.0.0.1'; + let audit: Audit; + let dataDir: string; + let db: DB; + let keyRing: KeyRing; + let clientService: ClientService; + let webSocketClient: WebSocketClient; + let rpcClient: OverrideRPClientType< + RPCClient<{ + auditEventsMultiPathGet: typeof auditEventsMultiPathGet; + }> + >; + let tlsConfig: TLSConfig; + let nodeConnectionManager: NodeConnectionManager; // Event target pretending to be discovery + let discovery: Discovery; // Event target pretending to be discovery + + const handleEvent = async (evt) => { + // @ts-ignore: kidnap protected handlerMap so we can send events in the foreground + const handlerMap = audit.eventHandlerMap; + await handlerMap.get(evt.constructor)?.handler(evt); + }; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + const keysPath = path.join(dataDir, 'keys'); + const dbPath = path.join(dataDir, 'db'); + db = await DB.createDB({ + dbPath, + logger, + }); + keyRing = await KeyRing.createKeyRing({ + password, + keysPath, + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + logger, + }); + tlsConfig = await testsUtils.createTLSConfig(keyRing.keyPair); + clientService = new ClientService({ + tlsConfig, + logger: logger.getChild(ClientService.name), + }); + nodeConnectionManager = new EventTarget() as any; + discovery = new EventTarget() as any; + audit = await Audit.createAudit({ + db, + nodeConnectionManager, + discovery, + logger: logger.getChild(Audit.name), + }); + clientService = new ClientService({ + tlsConfig, + logger: logger.getChild(ClientService.name), + }); + await clientService.start({ + manifest: { + auditEventsGet: new AuditEventsGet({ + audit, + }), + auditEventsMultiPathGet: new AuditEventsMultiPathGet({ + audit, + }), + }, + host: localhost, + }); + webSocketClient = await WebSocketClient.createWebSocketClient({ + config: { + verifyPeer: false, + }, + host: localhost, + logger: logger.getChild(WebSocketClient.name), + port: clientService.port, + }); + rpcClient = new RPCClient({ + manifest: { + auditEventsMultiPathGet, + }, + streamFactory: () => webSocketClient.connection.newStream(), + toError: networkUtils.toError, + logger: logger.getChild(RPCClient.name), + }) as any; + }); + afterEach(async () => { + await clientService.stop({ force: true }); + await webSocketClient.destroy({ force: true }); + await keyRing.stop(); + await audit.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('cancels', async () => { + let callerInterface = await rpcClient.methods.auditEventsMultiPathGet({ + paths: [], + }); + let reader = callerInterface.getReader(); + await reader.cancel(); + await expect(reader.closed).toResolve(); + callerInterface = await rpcClient.methods.auditEventsMultiPathGet({ + paths: [], + awaitFutureEvents: true, + }); + reader = callerInterface.getReader(); + await reader.cancel(); + await expect(reader.closed).toResolve(); + }); + test('gets connection events', async () => { + const nodeId = testNodesUtils.generateRandomNodeId(); + const eventDetail: ConnectionData = { + remoteHost: '::' as Host, + remoteNodeId: nodeId, + remotePort: 0 as Port, + }; + const auditEventData = { + ...eventDetail, + remoteNodeId: nodesUtils.encodeNodeId(eventDetail.remoteNodeId), + }; + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionReverse({ + detail: eventDetail, + }), + ); + let callerInterface: any = await rpcClient.methods.auditEventsMultiPathGet({ + paths: ['node.connection.reverse'], + }); + let reader = callerInterface.getReader(); + await expect(reader.read().then((e) => e.value!.data)).resolves.toEqual({ + ...auditEventData, + type: 'reverse', + }); + callerInterface = await rpcClient.methods.auditEventsMultiPathGet({ + paths: ['node.connection'], + awaitFutureEvents: true, + }); + reader = callerInterface.getReader(); + await expect(reader.read().then((e) => e.value!.data)).resolves.toEqual({ + ...auditEventData, + type: 'reverse', + }); + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionForward({ + detail: eventDetail, + }), + ); + await expect(reader.read().then((e) => e.value!.data)).resolves.toEqual({ + ...auditEventData, + type: 'forward', + }); + }); + test('gets discovery events', async () => { + // Set up some events + await handleEvent( + new discoveryEvents.EventDiscoveryVertexQueued({ + detail: { + vertex: 'vertex1' as GestaltIdEncoded, + }, + }), + ); + await handleEvent( + new discoveryEvents.EventDiscoveryVertexQueued({ + detail: { + vertex: 'vertex2' as GestaltIdEncoded, + parent: 'vertex1' as GestaltIdEncoded, + }, + }), + ); + await handleEvent( + new discoveryEvents.EventDiscoveryVertexQueued({ + detail: { + vertex: 'vertex3' as GestaltIdEncoded, + parent: 'vertex1' as GestaltIdEncoded, + }, + }), + ); + await handleEvent( + new discoveryEvents.EventDiscoveryVertexProcessed({ + detail: { + vertex: 'vertex1' as GestaltIdEncoded, + }, + }), + ); + await handleEvent( + new discoveryEvents.EventDiscoveryVertexFailed({ + detail: { + vertex: 'vertex2' as GestaltIdEncoded, + parent: 'vertex1' as GestaltIdEncoded, + code: 255, + message: 'some message', + }, + }), + ); + await handleEvent( + new discoveryEvents.EventDiscoveryVertexCancelled({ + detail: { + vertex: 'vertex3' as GestaltIdEncoded, + parent: 'vertex1' as GestaltIdEncoded, + }, + }), + ); - test('testo', async () => { + const readableStream = await rpcClient.methods.auditEventsMultiPathGet({ + paths: ['discovery.vertex'], + }); + const results: Array = []; + for await (const result of readableStream) { + results.push(result); + } + expect(results).toHaveLength(6); + }); + test('can get multiple paths in ascending order', async () => { + const nodeId = testNodesUtils.generateRandomNodeId(); + const eventDetail: ConnectionData = { + remoteHost: '::' as Host, + remoteNodeId: nodeId, + remotePort: 0 as Port, + }; + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionReverse({ + detail: { + ...eventDetail, + remotePort: 1 as Port, + }, + }), + ); + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionForward({ + detail: { + ...eventDetail, + remotePort: 2 as Port, + }, + }), + ); + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionReverse({ + detail: { + ...eventDetail, + remotePort: 3 as Port, + }, + }), + ); + await handleEvent( + new nodesEvents.EventNodeConnectionManagerConnectionForward({ + detail: { + ...eventDetail, + remotePort: 4 as Port, + }, + }), + ); + const callerInterface: any = + await rpcClient.methods.auditEventsMultiPathGet({ + paths: ['node.connection', 'node.connection.forward'], + order: 'asc', + }); + const order: Array = []; + const pathSet: Set = new Set(); + for await (const result of callerInterface) { + order.push(result.data.remotePort); + pathSet.add(result.path.join('.')); + } + expect(order).toMatchObject([1, 2, 3, 4]); + expect([...pathSet]).toIncludeAllMembers([ + 'node.connection.reverse', + 'node.connection.forward', + ]); + expect(pathSet.size).toBe(2); + }); + test('can get multiple paths in descending order', async () => { const nodeId = testNodesUtils.generateRandomNodeId(); const eventDetail: ConnectionData = { remoteHost: '::' as Host, @@ -296,7 +571,18 @@ describe('auditEventGet', () => { paths: ['node.connection', 'node.connection.forward'], order: 'desc', }); - for await (const result of callerInterface) console.log(result); + const order: Array = []; + const pathSet: Set = new Set(); + for await (const result of callerInterface) { + order.push(result.data.remotePort); + pathSet.add(result.path.join('.')); + } + expect(order).toMatchObject([4, 3, 2, 1]); + expect([...pathSet]).toIncludeAllMembers([ + 'node.connection.reverse', + 'node.connection.forward', + ]); + expect(pathSet.size).toBe(2); }); });