From 54852a33d936f09b2034f5334909b8548d7046af Mon Sep 17 00:00:00 2001 From: addievo Date: Wed, 25 Oct 2023 14:37:02 +1100 Subject: [PATCH] feat: UI polish incl nodes connection labels, nodes connection in json format removes auth metadata, agent status strips key and cert, and adds some nice padding --- src/agent/CommandStatus.ts | 2 - src/nodes/CommandClaim.ts | 38 +++++---- src/nodes/CommandConnections.ts | 54 +++++++------ src/nodes/CommandFind.ts | 11 ++- src/nodes/CommandGetAll.ts | 11 ++- src/nodes/CommandPing.ts | 12 +-- src/notifications/CommandRead.ts | 11 ++- src/secrets/CommandGet.ts | 11 ++- src/secrets/CommandList.ts | 13 +-- src/secrets/CommandStat.ts | 14 ++-- src/types.ts | 9 +++ src/utils/utils.ts | 134 +++++++++++++++++++++++++------ src/vaults/CommandCreate.ts | 12 +-- src/vaults/CommandList.ts | 12 +-- src/vaults/CommandLog.ts | 12 +-- src/vaults/CommandPermissions.ts | 11 ++- src/vaults/CommandScan.ts | 11 ++- tests/agent/status.test.ts | 4 - tests/keys/cert.test.ts | 4 +- tests/nodes/connections.test.ts | 129 +++++++++++++++++++++++++++++ tests/utils.test.ts | 50 ++++++------ 21 files changed, 391 insertions(+), 174 deletions(-) create mode 100644 tests/nodes/connections.test.ts diff --git a/src/agent/CommandStatus.ts b/src/agent/CommandStatus.ts index c661da99..058f14ac 100644 --- a/src/agent/CommandStatus.ts +++ b/src/agent/CommandStatus.ts @@ -79,8 +79,6 @@ class CommandStatus extends CommandPolykey { clientPort: response.clientPort, agentHost: response.agentHost, agentPort: response.agentPort, - publicKeyJWK: response.publicKeyJwk, - certChainPEM: response.certChainPEM, }, }), ); diff --git a/src/nodes/CommandClaim.ts b/src/nodes/CommandClaim.ts index 3e17ec8d..4847fc36 100644 --- a/src/nodes/CommandClaim.ts +++ b/src/nodes/CommandClaim.ts @@ -65,27 +65,25 @@ class CommandClaim extends CommandPolykey { ); const claimed = response.success; if (claimed) { - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [ - `Successfully generated a cryptolink claim on Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`, - ], - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: [ + `Successfully generated a cryptolink claim on Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}`, + ], + }); + process.stdout.write(formattedOutput); } else { - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [ - `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( - nodeId, - )}`, - ], - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: [ + `Successfully sent Gestalt Invite notification to Keynode with ID ${nodesUtils.encodeNodeId( + nodeId, + )}`, + ], + }); + process.stdout.write(formattedOutput); } } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/nodes/CommandConnections.ts b/src/nodes/CommandConnections.ts index a8ee7452..7501167e 100644 --- a/src/nodes/CommandConnections.ts +++ b/src/nodes/CommandConnections.ts @@ -48,35 +48,41 @@ class CommandAdd extends CommandPolykey { }); const connectionEntries: Array = []; for await (const connection of connections) { - connectionEntries.push(connection); + connectionEntries.push({ + host: connection.host, + hostname: connection.hostname, + nodeIdEncoded: connection.nodeIdEncoded, + port: connection.port, + timeout: connection.timeout, + usageCount: connection.usageCount, + }); } return connectionEntries; }, auth); if (options.format === 'human') { - const output: Array = []; - for (const connection of connections) { - const hostnameString = - connection.hostname === '' ? '' : `(${connection.hostname})`; - const hostString = `${connection.nodeIdEncoded}@${connection.host}${hostnameString}:${connection.port}`; - const usageCount = connection.usageCount; - const timeout = - connection.timeout === -1 ? 'NA' : `${connection.timeout}`; - const outputLine = `${hostString}\t${usageCount}\t${timeout}`; - output.push(outputLine); - } - process.stdout.write( - binUtils.outputFormatter({ - type: 'list', - data: output, - }), - ); + // Wait for outputFormatter to complete and then write to stdout + const formattedOutput = await binUtils.outputFormatter({ + type: 'table', + data: connections, + options: { + headers: [ + 'host', + 'hostname', + 'nodeIdEncoded', + 'port', + 'timeout', + 'usageCount', + ], + }, + }); + process.stdout.write(formattedOutput); } else { - process.stdout.write( - binUtils.outputFormatter({ - type: 'json', - data: connections, - }), - ); + // Wait for outputFormatter to complete and then write to stdout + const formattedOutput = await binUtils.outputFormatter({ + type: 'json', + data: connections, + }); + process.stdout.write(formattedOutput); } } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/nodes/CommandFind.ts b/src/nodes/CommandFind.ts index 4528ade8..33d7230c 100644 --- a/src/nodes/CommandFind.ts +++ b/src/nodes/CommandFind.ts @@ -90,12 +90,11 @@ class CommandFind extends CommandPolykey { } let output: any = result; if (options.format === 'human') output = [result.message]; - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: output, + }); + process.stdout.write(formattedOutput); // Like ping it should error when failing to find node for automation reasons. if (!result.success) { throw new errors.ErrorPolykeyCLINodeFindFailed(result.message); diff --git a/src/nodes/CommandGetAll.ts b/src/nodes/CommandGetAll.ts index c0ded8bb..21b0da47 100644 --- a/src/nodes/CommandGetAll.ts +++ b/src/nodes/CommandGetAll.ts @@ -60,12 +60,11 @@ class CommandGetAll extends CommandPolykey { `NodeId ${value.nodeIdEncoded}, Address ${value.host}:${value.port}, bucketIndex ${value.bucketIndex}`, ); } - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: output, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/nodes/CommandPing.ts b/src/nodes/CommandPing.ts index aaff1ca0..49478e43 100644 --- a/src/nodes/CommandPing.ts +++ b/src/nodes/CommandPing.ts @@ -68,12 +68,12 @@ class CommandPing extends CommandPolykey { else status.message = error.message; const output: any = options.format === 'json' ? status : [status.message]; - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: output, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: output, + }); + process.stdout.write(formattedOutput); + if (error != null) throw error; } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/notifications/CommandRead.ts b/src/notifications/CommandRead.ts index 78153798..543f33e2 100644 --- a/src/notifications/CommandRead.ts +++ b/src/notifications/CommandRead.ts @@ -79,12 +79,11 @@ class CommandRead extends CommandPolykey { notifications.push(notification); } for (const notification of notifications) { - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'dict', - data: notification, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'dict', + data: notification, + }); + process.stdout.write(formattedOutput); } } finally { if (pkClient! != null) await pkClient.stop(); diff --git a/src/secrets/CommandGet.ts b/src/secrets/CommandGet.ts index 6ab24c60..6b56281f 100644 --- a/src/secrets/CommandGet.ts +++ b/src/secrets/CommandGet.ts @@ -59,12 +59,11 @@ class CommandGet extends CommandPolykey { meta, ); const secretContent = Buffer.from(response.secretContent, 'binary'); - process.stdout.write( - binUtils.outputFormatter({ - type: 'raw', - data: secretContent, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: 'raw', + data: secretContent, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/secrets/CommandList.ts b/src/secrets/CommandList.ts index a4b7b0d1..193c2061 100644 --- a/src/secrets/CommandList.ts +++ b/src/secrets/CommandList.ts @@ -56,12 +56,13 @@ class CommandList extends CommandPolykey { } return data; }, auth); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }); + + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/secrets/CommandStat.ts b/src/secrets/CommandStat.ts index fd383970..31880a86 100644 --- a/src/secrets/CommandStat.ts +++ b/src/secrets/CommandStat.ts @@ -65,13 +65,13 @@ class CommandStat extends CommandPolykey { data.push(`${key}: ${value}`); } - // Print out the result. - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data, - }), - ); + // Assuming the surrounding function is async + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data, + }); + + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/types.ts b/src/types.ts index 102e6bbc..f29f3189 100644 --- a/src/types.ts +++ b/src/types.ts @@ -40,8 +40,17 @@ type AgentChildProcessOutput = error: POJO; }; +type TableRow = Record; + +interface TableOptions { + headers?: string[]; + includeRowCount?: boolean; +} + export type { AgentStatusLiveData, AgentChildProcessInput, AgentChildProcessOutput, + TableRow, + TableOptions, }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 2e19b77b..1d1b79aa 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,4 +1,5 @@ import type { POJO } from 'polykey/dist/types'; +import type { TableRow, TableOptions } from '../types'; import process from 'process'; import { LogLevel } from '@matrixai/logger'; import ErrorPolykey from 'polykey/dist/ErrorPolykey'; @@ -34,6 +35,7 @@ type OutputObject = | { type: 'table'; data: Array; + options?: TableOptions; } | { type: 'dict'; @@ -62,7 +64,93 @@ function standardErrorReplacer(key: string, value: any) { return value; } -function outputFormatter(msg: OutputObject): string | Uint8Array { +async function* arrayToAsyncIterable(array: T[]): AsyncIterable { + for (const item of array) { + yield item; + } +} + +// Function to handle 'table' type output +const outputTableFormatter = async ( + rowStream: TableRow[] | AsyncIterable, + options?: TableOptions, +): Promise => { + let output = ''; + const maxColumnLengths: Record = {}; + let rowCount = 0; + + // Initialize maxColumnLengths with header lengths if headers are provided + if (options?.headers) { + for (const header of options.headers) { + maxColumnLengths[header] = header.length; + } + } + + let iterableStream = Array.isArray(rowStream) + ? arrayToAsyncIterable(rowStream) + : rowStream; + + const updateMaxColumnLengths = (row: TableRow) => { + for (const [key, value] of Object.entries(row)) { + const cellValue = + value === null || value === '' || value === undefined ? 'N/A' : value; + maxColumnLengths[key] = Math.max( + maxColumnLengths[key] || 0, + cellValue.toString().length, + ); + } + }; + + // Precompute max column lengths by iterating over the rows first + for await (const row of iterableStream) { + updateMaxColumnLengths(row); + } + + // Reset the iterableStream if it's an array so we can iterate over it again + if (Array.isArray(rowStream)) { + iterableStream = arrayToAsyncIterable(rowStream); + } + + // If headers are provided, add them to your output first + if (options?.headers) { + const headerRow = options.headers + .map((header) => header.padEnd(maxColumnLengths[header])) + .join('\t'); + output += headerRow + '\n'; + } + + // Function to format a single row + const formatRow = (row: TableRow) => { + let formattedRow = ''; + + if (options?.includeRowCount) { + formattedRow += `${++rowCount}\t`; + } + + const keysToUse = options?.headers ?? Object.keys(maxColumnLengths); + + for (const key of keysToUse) { + const cellValue = Object.prototype.hasOwnProperty.call(row, key) + ? row[key] + : 'N/A'; + formattedRow += `${cellValue + ?.toString() + .padEnd(maxColumnLengths[key] || 0)}\t`; + } + + return formattedRow.trimEnd(); + }; + + for await (const row of iterableStream) { + output += formatRow(row) + '\n'; + } + + return output; +}; + +function outputFormatter( + msg: OutputObject, +): string | Uint8Array | Promise { let output = ''; if (msg.type === 'raw') { return msg.data; @@ -75,40 +163,34 @@ function outputFormatter(msg: OutputObject): string | Uint8Array { output += `${msg.data[elem]}\n`; } } else if (msg.type === 'table') { - for (const key in msg.data[0]) { - output += `${key}\t`; - } - output = output.substring(0, output.length - 1) + `\n`; - for (const row of msg.data) { - for (const key in row) { - let value = row[key]; - // Empty string for null or undefined values - if (value == null) { - value = ''; - } - value = value.toString(); - // Remove the last line terminator if it exists - // This may exist if the value is multi-line string - value = value.replace(/(?:\r\n|\n)$/, ''); - output += `${value}\t`; + return outputTableFormatter(msg.data, msg.options); + } else if (msg.type === 'dict') { + let maxKeyLength = 0; + for (const key in msg.data) { + if (key.length > maxKeyLength) { + maxKeyLength = key.length; } - output = output.substring(0, output.length - 1) + `\n`; } - } else if (msg.type === 'dict') { + for (const key in msg.data) { let value = msg.data[key]; - // Empty string for null or undefined values if (value == null) { value = ''; } - value = JSON.stringify(value); - // Remove the last line terminator if it exists - // This may exist if the value is multi-line string + + // Only trim starting and ending quotes if value is a string + if (typeof value === 'string') { + value = JSON.stringify(value).replace(/^"|"$/g, ''); + } else { + value = JSON.stringify(value); + } + + // Re-introduce value.replace logic from old code value = value.replace(/(?:\r\n|\n)$/, ''); - // If the string has line terminators internally - // Then we must append `\t` separator after each line terminator value = value.replace(/(\r\n|\n)/g, '$1\t'); - output += `${key}\t${value}\n`; + + const padding = ' '.repeat(maxKeyLength - key.length); + output += `${key}${padding}\t${value}\n`; } } else if (msg.type === 'json') { output = JSON.stringify(msg.data, standardErrorReplacer); diff --git a/src/vaults/CommandCreate.ts b/src/vaults/CommandCreate.ts index 18912299..bf89c439 100644 --- a/src/vaults/CommandCreate.ts +++ b/src/vaults/CommandCreate.ts @@ -1,4 +1,5 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import process from 'process'; import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; @@ -53,12 +54,11 @@ class CommandCreate extends CommandPolykey { }), meta, ); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: [`Vault ${response.vaultIdEncoded} created successfully`], - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: [`Vault ${response.vaultIdEncoded} created successfully`], + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/vaults/CommandList.ts b/src/vaults/CommandList.ts index ab420ddb..b42d0d21 100644 --- a/src/vaults/CommandList.ts +++ b/src/vaults/CommandList.ts @@ -1,4 +1,5 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import process from 'process'; import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; @@ -55,12 +56,11 @@ class CommandList extends CommandPolykey { } return data; }, meta); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/vaults/CommandLog.ts b/src/vaults/CommandLog.ts index 21e8b2d4..460b82f0 100644 --- a/src/vaults/CommandLog.ts +++ b/src/vaults/CommandLog.ts @@ -1,4 +1,5 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import process from 'process'; import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; import * as binOptions from '../utils/options'; @@ -62,12 +63,11 @@ class CommandLog extends CommandPolykey { } return data; }, meta); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/vaults/CommandPermissions.ts b/src/vaults/CommandPermissions.ts index 58575381..6a45ec24 100644 --- a/src/vaults/CommandPermissions.ts +++ b/src/vaults/CommandPermissions.ts @@ -61,12 +61,11 @@ class CommandPermissions extends CommandPolykey { }, meta); if (data.length === 0) data.push('No permissions were found'); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/vaults/CommandScan.ts b/src/vaults/CommandScan.ts index 6ca0ae00..7a769210 100644 --- a/src/vaults/CommandScan.ts +++ b/src/vaults/CommandScan.ts @@ -58,12 +58,11 @@ class CommandScan extends CommandPolykey { } return data; }, meta); - process.stdout.write( - binUtils.outputFormatter({ - type: options.format === 'json' ? 'json' : 'list', - data: data, - }), - ); + const formattedOutput = await binUtils.outputFormatter({ + type: options.format === 'json' ? 'json' : 'list', + data: data, + }); + process.stdout.write(formattedOutput); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/tests/agent/status.test.ts b/tests/agent/status.test.ts index e66bdb5f..d57ed0dc 100644 --- a/tests/agent/status.test.ts +++ b/tests/agent/status.test.ts @@ -179,8 +179,6 @@ describe('status', () => { clientPort: statusInfo.data.clientPort, agentHost: statusInfo.data.agentHost, agentPort: statusInfo.data.agentPort, - publicKeyJWK: expect.any(Object), - certChainPEM: expect.any(String), }); }); testUtils.testIf( @@ -229,8 +227,6 @@ describe('status', () => { clientPort: statusInfo.data.clientPort, agentHost: statusInfo.data.agentHost, agentPort: statusInfo.data.agentPort, - publicKeyJWK: expect.any(Object), - certChainPEM: expect.any(String), }); }); }); diff --git a/tests/keys/cert.test.ts b/tests/keys/cert.test.ts index aa699971..47c01d53 100644 --- a/tests/keys/cert.test.ts +++ b/tests/keys/cert.test.ts @@ -33,7 +33,7 @@ describe('cert', () => { }); const certCommand = JSON.parse(stdout).cert; ({ exitCode, stdout } = await testUtils.pkExec( - ['agent', 'status', '--format', 'json'], + ['keys', 'cert', '--format', 'json'], { env: { PK_NODE_PATH: agentDir, @@ -44,7 +44,7 @@ describe('cert', () => { }, )); expect(exitCode).toBe(0); - const certStatus = JSON.parse(stdout).certChainPEM; + const certStatus = JSON.parse(stdout).cert; expect(certCommand).toBe(certStatus); }); }); diff --git a/tests/nodes/connections.test.ts b/tests/nodes/connections.test.ts new file mode 100644 index 00000000..d2a59ef9 --- /dev/null +++ b/tests/nodes/connections.test.ts @@ -0,0 +1,129 @@ +import type { NodeId, NodeIdEncoded } from 'polykey/dist/ids/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('connections', () => { + const logger = new Logger('connections test', LogLevel.WARN, [ + new StreamHandler(), + ]); + const password = 'helloworld'; + let dataDir: string; + let nodePath: string; + let pkAgent: PolykeyAgent; + let remoteNode: PolykeyAgent; + let localId: NodeId; + let remoteId: NodeId; + let remoteIdEncoded: NodeIdEncoded; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + nodePath = path.join(dataDir, 'keynode'); + pkAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + seedNodes: {}, // Explicitly no seed nodes on startup + nodePath, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + agentServicePort: 55555, + clientServicePort: 55554, + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger, + }); + localId = pkAgent.keyRing.getNodeId(); + // Setting up a remote keynode + remoteNode = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + seedNodes: {}, // Explicitly no seed nodes on startup + nodePath: path.join(dataDir, 'remoteNode'), + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + agentServicePort: 55553, + clientServicePort: 55552, + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger, + }); + remoteId = remoteNode.keyRing.getNodeId(); + remoteIdEncoded = nodesUtils.encodeNodeId(remoteId); + await testUtils.nodesConnect(pkAgent, remoteNode); + await pkAgent.acl.setNodePerm(remoteId, { + gestalt: { + notify: null, + claim: null, + }, + vaults: {}, + }); + await remoteNode.acl.setNodePerm(localId, { + gestalt: { + notify: null, + claim: null, + }, + vaults: {}, + }); + }); + afterEach(async () => { + await pkAgent.stop(); + await remoteNode.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test('Correctly list connection information, and not list auth data', async () => { + await remoteNode.notificationsManager.sendNotification(localId, { + type: 'GestaltInvite', + }); + const { exitCode } = await testUtils.pkStdio( + ['nodes', 'claim', remoteIdEncoded, '--force-invite'], + { + env: { + PK_NODE_PATH: nodePath, + PK_PASSWORD: password, + }, + cwd: dataDir, + }, + ); + const { stdout } = await testUtils.pkStdio( + ['nodes', 'connections', '--format', 'json'], + { + env: { + PK_NODE_PATH: nodePath, + PK_PASSWORD: password, + }, + cwd: dataDir, + }, + ); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + host: remoteNode.agentServiceHost, + hostname: '', + nodeIdEncoded: nodesUtils.encodeNodeId( + remoteNode.keyRing.getNodeId(), + ), + port: remoteNode.agentServicePort, + timeout: expect.any(Number), + usageCount: 0, + }), + ]), + ); + }); +}); diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 581a65ec..b2c590f4 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -28,28 +28,32 @@ describe('bin/utils', () => { ); testUtils.testIf(testUtils.isTestPlatformEmpty)( 'table in human and in json format', - () => { + async () => { + // Note the async here // Table - expect( - binUtils.outputFormatter({ - type: 'table', - data: [ - { key1: 'value1', key2: 'value2' }, - { key1: 'data1', key2: 'data2' }, - { key1: null, key2: undefined }, - ], - }), - ).toBe('key1\tkey2\nvalue1\tvalue2\ndata1\tdata2\n\t\n'); + const tableOutput = await binUtils.outputFormatter({ + // And the await here + type: 'table', + data: [ + { key1: 'value1', key2: 'value2' }, + { key1: 'data1', key2: 'data2' }, + { key1: null, key2: undefined }, + ], + }); + expect(tableOutput).toBe( + 'value1\tvalue2\ndata1 \tdata2\nundefined\tundefined\n', + ); + // JSON - expect( - binUtils.outputFormatter({ - type: 'json', - data: [ - { key1: 'value1', key2: 'value2' }, - { key1: 'data1', key2: 'data2' }, - ], - }), - ).toBe( + const jsonOutput = await binUtils.outputFormatter({ + // And the await here + type: 'json', + data: [ + { key1: 'value1', key2: 'value2' }, + { key1: 'data1', key2: 'data2' }, + ], + }); + expect(jsonOutput).toBe( '[{"key1":"value1","key2":"value2"},{"key1":"data1","key2":"data2"}]\n', ); }, @@ -63,19 +67,19 @@ describe('bin/utils', () => { type: 'dict', data: { key1: 'value1', key2: 'value2' }, }), - ).toBe('key1\t"value1"\nkey2\t"value2"\n'); + ).toBe('key1\tvalue1\nkey2\tvalue2\n'); expect( binUtils.outputFormatter({ type: 'dict', data: { key1: 'first\nsecond', key2: 'first\nsecond\n' }, }), - ).toBe('key1\t"first\\nsecond"\nkey2\t"first\\nsecond\\n"\n'); + ).toBe('key1\tfirst\\nsecond\nkey2\tfirst\\nsecond\\n\n'); expect( binUtils.outputFormatter({ type: 'dict', data: { key1: null, key2: undefined }, }), - ).toBe('key1\t""\nkey2\t""\n'); + ).toBe('key1\t\nkey2\t\n'); // JSON expect( binUtils.outputFormatter({