From b1926cea2352018143c8d8d12f3b4dde92d28aa9 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Tue, 11 Jun 2024 09:23:18 -0700 Subject: [PATCH] test: migrate run-tx-with-ws-ds, keychain-aws-sm tests from Tap to Jest There's still about a hundred test cases to be migrated so I'm combining a few at a time in the pull requests to reduce the CI resource consumption. They are fairly boilerplate changes that usually follow the exact same pattern so it's fairly easy to review with that in mind (hopefully) despite the slightly larger size. Signed-off-by: Peter Somogyvari --- .github/workflows/ci.yaml | 6 +- .taprc | 3 - jest.config.js | 3 - .../openapi/openapi-validation.test.ts | 261 ++++----- .../fabric-v2-2-x/deploy-lock-asset.test.ts | 531 +++++++++--------- .../run-transaction-with-ws-ids.test.ts | 211 +++---- 6 files changed, 467 insertions(+), 548 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ff13c33769..508c54fdda 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -818,7 +818,7 @@ jobs: FULL_BUILD_DISABLED: true JEST_TEST_PATTERN: packages/cactus-plugin-keychain-aws-sm/src/test/typescript/(unit|integration|benchmark)/.*/*.test.ts JEST_TEST_RUNNER_DISABLED: true - TAPE_TEST_PATTERN: '--files={./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts,./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-factory-keychain.test.ts,./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-factory-keychain.test.ts,./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts}' + TAPE_TEST_PATTERN: '--files={./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-factory-keychain.test.ts,./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-factory-keychain.test.ts,./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/plugin-keychain-aws-sm.test.ts}' TAPE_TEST_RUNNER_DISABLED: false needs: build-dev runs-on: ubuntu-22.04 @@ -1276,7 +1276,7 @@ jobs: restore-keys: | ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - run: npm run configure - - run: yarn ts-node ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts + - run: yarn jest ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts plc-fabric-4: continue-on-error: false @@ -1573,7 +1573,7 @@ jobs: restore-keys: | ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} - run: npm run configure - - run: yarn ts-node ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts + - run: yarn jest ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts cplc-go-ethereum-socketio: continue-on-error: false diff --git a/.taprc b/.taprc index e4dd236583..024e28e54d 100644 --- a/.taprc +++ b/.taprc @@ -5,9 +5,6 @@ jobs: 1 timeout: 3600 ts: true files: - - ./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts - - ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts - - ./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts - ./packages/cactus-plugin-ledger-connector-corda/src/test/typescript/integration/deploy-cordapp-jars-to-nodes-v4.8-express.test.ts - ./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts - ./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts diff --git a/jest.config.js b/jest.config.js index 591f13270c..4202250a3e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,9 +16,6 @@ module.exports = { ], // Ignore the tests that are still using tap/tape for as their test runner testPathIgnorePatterns: [ - `./packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts`, - `./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts`, - `./packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts`, `./packages/cactus-plugin-ledger-connector-corda/src/test/typescript/integration/deploy-cordapp-jars-to-nodes-v4.8-express.test.ts`, `./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v2.3.0-invoke-contract-json-object-endpoints.test.ts`, `./packages/cactus-plugin-ledger-connector-quorum/src/test/typescript/integration/plugin-ledger-connector-quorum/deploy-contract/v21.4.1-invoke-contract-json-object-endpoints.test.ts`, diff --git a/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts index a7ce4c4028..ea9c76cec0 100644 --- a/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts +++ b/packages/cactus-plugin-keychain-aws-sm/src/test/typescript/integration/openapi/openapi-validation.test.ts @@ -1,20 +1,15 @@ -import test, { Test } from "tape-promise/tape"; import { v4 as internalIpV4 } from "internal-ip"; - import { Containers, LocalStackContainer, K_DEFAULT_LOCALSTACK_HTTP_PORT, } from "@hyperledger/cactus-test-tooling"; - import { v4 as uuidv4 } from "uuid"; - import { IListenOptions, LogLevelDesc, Servers, } from "@hyperledger/cactus-common"; - import { IPluginKeychainAwsSmOptions, PluginKeychainAwsSm, @@ -26,14 +21,12 @@ import { HasKeychainEntryRequestV1, DeleteKeychainEntryRequestV1, } from "../../../../main/typescript/public-api"; - import fs from "fs"; import path from "path"; import os from "os"; import express from "express"; import bodyParser from "body-parser"; import http from "http"; - import { installOpenapiValidationMiddleware, PluginRegistry, @@ -41,73 +34,80 @@ import { import OAS from "../../../../main/json/openapi.json"; import { AddressInfo } from "net"; -const logLevel: LogLevelDesc = "TRACE"; +const logLevel: LogLevelDesc = "INFO"; const testCase = "cactus-plugin-keychain-aws-sm API"; -test(testCase, async (t: Test) => { - const localStackContainer = new LocalStackContainer({ - logLevel: logLevel, - }); - await localStackContainer.start(); +describe("PluginKeychainAwsSm", () => { + let localStackContainer: LocalStackContainer; + let tmpDirPath: string; + let plugin: PluginKeychainAwsSm; + let expressApp: express.Express; + let server: http.Server; + let apiClient: KeychainAwsSmApi; - const ci = await Containers.getById(localStackContainer.containerId); - const localstackIpAddr = await internalIpV4(); - const hostPort = await Containers.getPublicPort( - K_DEFAULT_LOCALSTACK_HTTP_PORT, - ci, - ); - const localstackHost = `http://${localstackIpAddr}:${hostPort}`; + beforeAll(async () => { + localStackContainer = new LocalStackContainer({ logLevel: logLevel }); + await localStackContainer.start(); - test.onFinish(async () => { - await localStackContainer.stop(); - await localStackContainer.destroy(); - }); + const ci = await Containers.getById(localStackContainer.containerId); + const localstackIpAddr = await internalIpV4(); + const hostPort = await Containers.getPublicPort( + K_DEFAULT_LOCALSTACK_HTTP_PORT, + ci, + ); + const localstackHost = `http://${localstackIpAddr}:${hostPort}`; + + tmpDirPath = await fs.promises.mkdtemp(path.join(os.tmpdir(), "cactus-")); + await fs.promises.writeFile( + `${tmpDirPath}/credentials`, + "[default]\naws_secret_access_key = test\naws_access_key_id = test", + "utf-8", + ); - let tmpDirPath = "tmpDirPath"; - tmpDirPath = await fs.promises.mkdtemp(path.join(os.tmpdir(), "cactus-")); - await fs.promises.writeFile( - `${tmpDirPath}/credentials`, - "[default]\naws_secret_access_key = test\naws_access_key_id = test", - "utf-8", - ); + const options: IPluginKeychainAwsSmOptions = { + instanceId: uuidv4(), + keychainId: uuidv4(), + pluginRegistry: new PluginRegistry({}), + awsEndpoint: localstackHost, + awsRegion: "us-east-1", + awsProfile: "default", + awsCredentialType: AwsCredentialType.LocalFile, + awsCredentialFilePath: `${tmpDirPath}/credentials`, + logLevel: logLevel, + }; + plugin = new PluginKeychainAwsSm(options); - const options: IPluginKeychainAwsSmOptions = { - instanceId: uuidv4(), - keychainId: uuidv4(), - pluginRegistry: new PluginRegistry({}), - awsEndpoint: localstackHost, - awsRegion: "us-east-1", - awsProfile: "default", - awsCredentialType: AwsCredentialType.LocalFile, - awsCredentialFilePath: `${tmpDirPath}/credentials`, - logLevel: logLevel, - }; - const plugin = new PluginKeychainAwsSm(options); + expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + server = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "127.0.0.1", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - const { address, port } = addressInfo; - const apiHost = `http://${address}:${port}`; + const apiConfig = new Configuration({ basePath: apiHost }); + apiClient = new KeychainAwsSmApi(apiConfig); - const apiConfig = new Configuration({ basePath: apiHost }); - const apiClient = new KeychainAwsSmApi(apiConfig); + await installOpenapiValidationMiddleware({ + logLevel, + app: expressApp, + apiSpec: OAS, + }); - await installOpenapiValidationMiddleware({ - logLevel, - app: expressApp, - apiSpec: OAS, + await plugin.getOrCreateWebServices(); + await plugin.registerWebServices(expressApp); }); - await plugin.getOrCreateWebServices(); - await plugin.registerWebServices(expressApp); + afterAll(async () => { + await Servers.shutdown(server); + await localStackContainer.stop(); + await localStackContainer.destroy(); + fs.promises.rm(tmpDirPath, { recursive: true, force: true }); + }); const key = uuidv4(); const value = uuidv4(); @@ -120,115 +120,84 @@ test(testCase, async (t: Test) => { const cWithoutParams = "not sending all required parameters"; const cInvalidParams = "sending invalid parameters"; - test(`${testCase} - ${fSet} - ${cOk}`, async (t2: Test) => { - const res = await apiClient.setKeychainEntryV1({ - key, - value, - }); - t2.equal(res.status, 200, `Endpoint ${fSet}: response.status === 200 OK`); - t2.end(); + test(`${testCase} - ${fSet} - ${cOk}`, async () => { + const res = await apiClient.setKeychainEntryV1({ key, value }); + expect(res.status).toBe(200); }); - test(`${testCase} - ${fGet} - ${cOk}`, async (t2: Test) => { + test(`${testCase} - ${fGet} - ${cOk}`, async () => { const res = await apiClient.getKeychainEntryV1({ key }); - t2.equal(res.status, 200, `Endpoint ${fGet}: response.status === 200 OK`); - t2.equal(res.data.value, value, "response.data.value === value1"); - t2.end(); + expect(res.status).toBe(200); + expect(res.data.value).toBe(value); }); - test(`${testCase} - ${fHas} - ${cOk}`, async (t2: Test) => { + test(`${testCase} - ${fHas} - ${cOk}`, async () => { const res = await apiClient.hasKeychainEntryV1({ key }); - t2.equal(res.status, 200, `Endpoint ${fHas}: response.status === 200 OK`); - t2.end(); + expect(res.status).toBe(200); }); - test(`${testCase} - ${fDelete} - ${cOk}`, async (t2: Test) => { + test(`${testCase} - ${fDelete} - ${cOk}`, async () => { const res = await apiClient.deleteKeychainEntryV1({ key }); - t2.equal( - res.status, - 200, - `Endpoint ${fDelete}: response.status === 200 OK`, - ); - t2.end(); + expect(res.status).toBe(200); }); - test(`${testCase} - ${fSet} - ${cWithoutParams}`, async (t2: Test) => { + test(`${testCase} - ${fSet} - ${cWithoutParams}`, async () => { try { await apiClient.setKeychainEntryV1({ - value: value, + value, } as any as SetKeychainEntryRequestV1); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fSet} without required key: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok(fields.includes("key"), "Rejected because key is required"); + expect(fields.includes("key")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fGet} - ${cWithoutParams}`, async (t2: Test) => { + test(`${testCase} - ${fGet} - ${cWithoutParams}`, async () => { try { await apiClient.getKeychainEntryV1( {} as any as GetKeychainEntryRequestV1, ); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fGet} without required key: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok(fields.includes("key"), "Rejected because key is required"); + expect(fields.includes("key")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fHas} - ${cWithoutParams}`, async (t2: Test) => { + test(`${testCase} - ${fHas} - ${cWithoutParams}`, async () => { try { await apiClient.hasKeychainEntryV1( {} as any as HasKeychainEntryRequestV1, ); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fHas} without required key: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok(fields.includes("key"), "Rejected because key is required"); + expect(fields.includes("key")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fDelete} - ${cWithoutParams}`, async (t2: Test) => { + test(`${testCase} - ${fDelete} - ${cWithoutParams}`, async () => { try { await apiClient.deleteKeychainEntryV1( {} as any as DeleteKeychainEntryRequestV1, ); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDelete} without required key: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok(fields.includes("key"), "Rejected because key is required"); + expect(fields.includes("key")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fSet} - ${cInvalidParams}`, async (t2: Test) => { + test(`${testCase} - ${fSet} - ${cInvalidParams}`, async () => { try { await apiClient.setKeychainEntryV1({ key, @@ -236,90 +205,56 @@ test(testCase, async (t: Test) => { fake: 4, } as any as SetKeychainEntryRequestV1); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fSet} with fake=4: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); + expect(fields.includes("fake")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fGet} - ${cInvalidParams}`, async (t2: Test) => { + test(`${testCase} - ${fGet} - ${cInvalidParams}`, async () => { try { await apiClient.getKeychainEntryV1({ key, fake: 4, } as any as GetKeychainEntryRequestV1); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fGet} with fake=4: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); + expect(fields.includes("fake")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fHas} - ${cInvalidParams}`, async (t2: Test) => { + test(`${testCase} - ${fHas} - ${cInvalidParams}`, async () => { try { await apiClient.hasKeychainEntryV1({ key, fake: 4, } as any as HasKeychainEntryRequestV1); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fHas} with fake=4: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); + expect(fields.includes("fake")).toBeTruthy(); } - t2.end(); }); - test(`${testCase} - ${fDelete} - ${cInvalidParams}`, async (t2: Test) => { + test(`${testCase} - ${fDelete} - ${cInvalidParams}`, async () => { try { await apiClient.deleteKeychainEntryV1({ key, fake: 4, } as any as DeleteKeychainEntryRequestV1); } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDelete} with fake=4: response.status === 400 OK`, - ); + expect(e.response.status).toBe(400); const fields = e.response.data.map((param: any) => param.path.replace("/body/", ""), ); - t2.ok( - fields.includes("fake"), - "Rejected because fake is not a valid parameter", - ); + expect(fields.includes("fake")).toBeTruthy(); } - t2.end(); }); - - t.end(); }); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts index 714296fda8..a927b390a7 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts @@ -3,14 +3,12 @@ import http from "http"; import fs from "fs-extra"; import path from "path"; -import test, { Test } from "tape-promise/tape"; import { v4 as uuidv4 } from "uuid"; import express from "express"; import bodyParser from "body-parser"; import { - Containers, DEFAULT_FABRIC_2_AIO_IMAGE_NAME, FABRIC_25_LTS_AIO_FABRIC_VERSION, FABRIC_25_LTS_AIO_IMAGE_VERSION, @@ -21,7 +19,6 @@ import { } from "@hyperledger/cactus-test-tooling"; import { - Checks, IListenOptions, Logger, LoggerProvider, @@ -52,288 +49,280 @@ const log: Logger = LoggerProvider.getOrCreate({ label: "fabric-lock-asset", level: logLevel, }); -test("BEFORE " + testCase, async (t: Test) => { + +beforeAll(async () => { const pruning = pruneDockerAllIfGithubAction({ logLevel }); - await t.doesNotReject(pruning, "Pruning didn't throw OK"); - t.end(); + await expect(pruning).resolves.not.toThrow(); }); -test(testCase, async (t: Test) => { - const channelId = "mychannel"; - const channelName = channelId; +describe(testCase, () => { + let ledger: FabricTestLedgerV1; + let apiClient: FabricApi; + let keychainId: string; + let keychainEntryKey: string; + let server: http.Server; + + beforeAll(async () => { + ledger = new FabricTestLedgerV1({ + emitContainerLogs: true, + publishAllPorts: true, + imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, + envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), + logLevel, + }); - test.onFailure(async () => { - await Containers.logDiagnostics({ logLevel }); - }); + await ledger.start({ omitPull: false }); + + const connectionProfile = await ledger.getConnectionProfileOrg1(); + expect(connectionProfile).toBeTruthy(); + + const enrollAdminOut = await ledger.enrollAdmin(); + const adminWallet = enrollAdminOut[1]; + const [userIdentity] = await ledger.enrollUser(adminWallet); + const sshConfig = await ledger.getSshConfig(); - const ledger = new FabricTestLedgerV1({ - emitContainerLogs: true, - publishAllPorts: true, - imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, - envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), - logLevel, + const keychainInstanceId = uuidv4(); + keychainId = uuidv4(); + keychainEntryKey = "user2"; + const keychainEntryValue = JSON.stringify(userIdentity); + + const keychainPlugin = new PluginKeychainMemory({ + instanceId: keychainInstanceId, + keychainId, + logLevel, + backend: new Map([ + [keychainEntryKey, keychainEntryValue], + ["some-other-entry-key", "some-other-entry-value"], + ]), + }); + + const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); + + const discoveryOptions: DiscoveryOptions = { + enabled: true, + asLocalhost: true, + }; + + const pluginOptions: IPluginLedgerConnectorFabricOptions = { + instanceId: uuidv4(), + dockerBinary: "/usr/local/bin/docker", + peerBinary: "/fabric-samples/bin/peer", + goBinary: "/usr/local/go/bin/go", + pluginRegistry, + cliContainerEnv: FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + sshConfig, + logLevel, + connectionProfile, + discoveryOptions, + eventHandlerOptions: { + strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, + commitTimeout: 300, + }, + }; + + const plugin = new PluginLedgerConnectorFabric(pluginOptions); + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + server = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const { port } = addressInfo; + apiClient = new FabricApi( + new Configuration({ basePath: `http://127.0.0.1:${port}` }), + ); + + await plugin.getOrCreateWebServices(); + await plugin.registerWebServices(expressApp); }); - const tearDown = async () => { + + afterAll(async () => { await ledger.stop(); await ledger.destroy(); await pruneDockerAllIfGithubAction({ logLevel }); - }; - - test.onFinish(tearDown); - await ledger.start({ omitPull: false }); - - const connectionProfile = await ledger.getConnectionProfileOrg1(); - t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - - const enrollAdminOut = await ledger.enrollAdmin(); - const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); - const sshConfig = await ledger.getSshConfig(); - - const keychainInstanceId = uuidv4(); - const keychainId = uuidv4(); - const keychainEntryKey = "user2"; - const keychainEntryValue = JSON.stringify(userIdentity); - - const keychainPlugin = new PluginKeychainMemory({ - instanceId: keychainInstanceId, - keychainId, - logLevel, - backend: new Map([ - [keychainEntryKey, keychainEntryValue], - ["some-other-entry-key", "some-other-entry-value"], - ]), + await Servers.shutdown(server); }); - const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); - - const discoveryOptions: DiscoveryOptions = { - enabled: true, - asLocalhost: true, - }; - - const pluginOptions: IPluginLedgerConnectorFabricOptions = { - instanceId: uuidv4(), - dockerBinary: "/usr/local/bin/docker", - peerBinary: "/fabric-samples/bin/peer", - goBinary: "/usr/local/go/bin/go", - pluginRegistry, - cliContainerEnv: FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - sshConfig, - logLevel, - connectionProfile, - discoveryOptions, - eventHandlerOptions: { - strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, - commitTimeout: 300, - }, - }; - const plugin = new PluginLedgerConnectorFabric(pluginOptions); - - const expressApp = express(); - expressApp.use(bodyParser.json({ limit: "250mb" })); - const server = http.createServer(expressApp); - const listenOptions: IListenOptions = { - hostname: "127.0.0.1", - port: 0, - server, - }; - const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; - const { port } = addressInfo; - test.onFinish(async () => await Servers.shutdown(server)); - - await plugin.getOrCreateWebServices(); - await plugin.registerWebServices(expressApp); - const apiUrl = `http://127.0.0.1:${port}`; - - const config = new Configuration({ basePath: apiUrl }); - - const apiClient = new FabricApi(config); - - const contractName = "basic-asset-transfer-2"; - - const contractRelPath = "../../fixtures/go/lock-asset/chaincode-typescript"; - const contractDir = path.join(__dirname, contractRelPath); - - // ├── package.json - // ├── src - // │ ├── assetTransfer.ts - // │ ├── asset.ts - // │ └── index.ts - // ├── tsconfig.json - const sourceFiles: FileBase64[] = []; - { - const filename = "./tsconfig.json"; - const relativePath = "./"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, + test("deploys contract and performs transactions", async () => { + const channelId = "mychannel"; + const channelName = channelId; + const contractName = "basic-asset-transfer-2"; + + const contractRelPath = "../../fixtures/go/lock-asset/chaincode-typescript"; + const contractDir = path.join(__dirname, contractRelPath); + + // ├── package.json + // ├── src + // │ ├── assetTransfer.ts + // │ ├── asset.ts + // │ └── index.ts + // ├── tsconfig.json + const sourceFiles: FileBase64[] = []; + { + const filename = "./tsconfig.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./package.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./index.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./asset.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./assetTransfer.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + + const res = await apiClient.deployContractV1({ + channelId, + ccVersion: "1.0.0", + sourceFiles, + ccName: contractName, + targetOrganizations: [ + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, + ], + caFile: + FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, + ccLabel: "basic-asset-transfer-2", + ccLang: ChainCodeProgrammingLanguage.Typescript, + ccSequence: 1, + orderer: "orderer.example.com:7050", + ordererTLSHostnameOverride: "orderer.example.com", + connTimeout: 60, }); - } - { - const filename = "./package.json"; - const relativePath = "./"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, - }); - } - { - const filename = "./index.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, + + expect(res.status).toBe(200); + expect(res.data.success).toBe(true); + + const { + packageIds, + lifecycle: { + approveForMyOrgList, + installList, + queryInstalledList, + commit, + packaging, + queryCommitted, + }, + } = res.data; + + expect(packageIds).toBeTruthy(); + expect(Array.isArray(packageIds)).toBe(true); + expect(approveForMyOrgList).toBeTruthy(); + expect(Array.isArray(approveForMyOrgList)).toBe(true); + expect(installList).toBeTruthy(); + expect(Array.isArray(installList)).toBe(true); + expect(queryInstalledList).toBeTruthy(); + expect(Array.isArray(queryInstalledList)).toBe(true); + expect(commit).toBeTruthy(); + expect(packaging).toBeTruthy(); + expect(queryCommitted).toBeTruthy(); + + const assetId = uuidv4(); + + const createRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId, "19"], + methodName: "CreateAsset", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, }); - } - { - const filename = "./asset.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, + expect(createRes).toBeTruthy(); + expect(createRes.status).toBeGreaterThan(199); + expect(createRes.status).toBeLessThan(300); + + const getRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId], + methodName: "ReadAsset", + invocationType: FabricContractInvocationType.Call, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, }); - } - { - const filename = "./assetTransfer.ts"; - const relativePath = "./src/"; - const filePath = path.join(contractDir, relativePath, filename); - const buffer = await fs.readFile(filePath); - sourceFiles.push({ - body: buffer.toString("base64"), - filepath: relativePath, - filename, + expect(getRes).toBeTruthy(); + expect(getRes.data).toBeTruthy(); + expect(getRes.data.functionOutput).toBeTruthy(); + expect(getRes.status).toBeGreaterThan(199); + expect(getRes.status).toBeLessThan(300); + + const asset = JSON.parse(getRes.data.functionOutput); + + expect(asset).toBeTruthy(); + expect(asset.ID).toBeTruthy(); + expect(asset.ID).toBe(assetId); + + const lockRes = await apiClient.runTransactionV1({ + contractName, + channelName, + params: [assetId], + methodName: "LockAsset", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId, + keychainRef: keychainEntryKey, + }, }); - } - - const res = await apiClient.deployContractV1({ - channelId, - ccVersion: "1.0.0", - // constructorArgs: { Args: ["john", "99"] }, - sourceFiles, - ccName: contractName, - targetOrganizations: [ - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1, - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_2, - ], - caFile: - FABRIC_25_LTS_FABRIC_SAMPLES_ENV_INFO_ORG_1.ORDERER_TLS_ROOTCERT_FILE, - ccLabel: "basic-asset-transfer-2", - ccLang: ChainCodeProgrammingLanguage.Typescript, - ccSequence: 1, - orderer: "orderer.example.com:7050", - ordererTLSHostnameOverride: "orderer.example.com", - connTimeout: 60, - }); - - const { packageIds, lifecycle, success } = res.data; - t.equal(res.status, 200, "res.status === 200 OK"); - t.true(success, "res.data.success === true"); - - const { - approveForMyOrgList, - installList, - queryInstalledList, - commit, - packaging, - queryCommitted, - } = lifecycle; - - Checks.truthy(packageIds, `packageIds truthy OK`); - Checks.truthy( - Array.isArray(packageIds), - `Array.isArray(packageIds) truthy OK`, - ); - Checks.truthy(approveForMyOrgList, `approveForMyOrgList truthy OK`); - Checks.truthy( - Array.isArray(approveForMyOrgList), - `Array.isArray(approveForMyOrgList) truthy OK`, - ); - Checks.truthy(installList, `installList truthy OK`); - Checks.truthy( - Array.isArray(installList), - `Array.isArray(installList) truthy OK`, - ); - Checks.truthy(queryInstalledList, `queryInstalledList truthy OK`); - Checks.truthy( - Array.isArray(queryInstalledList), - `Array.isArray(queryInstalledList) truthy OK`, - ); - Checks.truthy(commit, `commit truthy OK`); - Checks.truthy(packaging, `packaging truthy OK`); - Checks.truthy(queryCommitted, `queryCommitted truthy OK`); - - const assetId = uuidv4(); - - // CreateAsset(id string, color string, size int, owner string, appraisedValue int) - const createRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId, "19"], - methodName: "CreateAsset", - invocationType: FabricContractInvocationType.Send, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - t.ok(createRes, "setRes truthy OK"); - t.true(createRes.status > 199, "createRes status > 199 OK"); - t.true(createRes.status < 300, "createRes status < 300 OK"); - t.comment(`BassicAssetTransfer.Create(): ${JSON.stringify(createRes.data)}`); - - const getRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId], - methodName: "ReadAsset", - invocationType: FabricContractInvocationType.Call, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, - }); - t.ok(getRes, "getRes truthy OK"); - t.ok(getRes.data, "getRes.data truthy OK"); - t.ok(getRes.data.functionOutput, "getRes.data.functionOutput truthy OK"); - t.true(getRes.status > 199 && getRes.status < 300, "getRes status 2xx OK"); - t.comment(`HelloWorld.get() ResponseBody: ${JSON.stringify(getRes.data)}`); - - const asset = JSON.parse(getRes.data.functionOutput); - - t.ok(asset, "JSON.parse(getRes.data.functionOutput) truthy OK"); - - t.ok(asset.ID, "asset.ID truthy OK"); - t.equal(asset.ID, assetId, "asset.ID === assetId truthy OK"); - const lockRes = await apiClient.runTransactionV1({ - contractName, - channelName, - params: [assetId], - methodName: "LockAsset", - invocationType: FabricContractInvocationType.Send, - signingCredential: { - keychainId, - keychainRef: keychainEntryKey, - }, + expect(lockRes).toBeTruthy(); + expect(lockRes.data).toBeTruthy(); + expect(lockRes.data.functionOutput).toBeTruthy(); + expect(lockRes.status).toBeGreaterThan(199); + expect(lockRes.status).toBeLessThan(300); + expect(lockRes.data.functionOutput).toBe("true"); + + log.warn(lockRes.data.functionOutput); }); - t.ok(lockRes, "lockRes truthy OK"); - t.ok(lockRes.data, "lockRes.data truthy OK"); - t.ok(lockRes.data.functionOutput, "lockRes.data.functionOutput truthy OK"); - t.true(lockRes.status > 199 && lockRes.status < 300, "lockRes status 2xx OK"); - t.equal(lockRes.data.functionOutput, "true", "lockRes returns true"); - log.warn(lockRes.data.functionOutput); - t.end(); }); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts index e2a8d1f1c8..9c633dc3e9 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-ws-ids.test.ts @@ -1,5 +1,4 @@ import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; -import test, { Test } from "tape-promise/tape"; import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric"; import { v4 as uuidv4 } from "uuid"; import { LogLevelDesc } from "@hyperledger/cactus-common"; @@ -13,7 +12,6 @@ import { FabricContractInvocationType, } from "../../../../main/typescript/public-api"; import { DiscoveryOptions } from "fabric-network"; - import { Containers, FabricTestLedgerV1, @@ -38,116 +36,122 @@ const logLevel: LogLevelDesc = "INFO"; // - make invoke (InitLedger) using 1st client // - make invoke (TransferAsset) using 2nd client (p384) client // - make query ("ReadAsset") using registrar(p256) -test("run-transaction-with-ws-ids", async (t: Test) => { - test.onFailure(async () => { + +describe("PluginLedgerConnectorFabric", () => { + let ledger: FabricTestLedgerV1; + let wsTestContainer: WsTestServer; + let wsAdmin: WsWallet; + let wsUser: WsWallet; + let wsIdClient: WsIdentityClient; + let keychainId: string; + let keychainPlugin: PluginKeychainMemory; + let plugin: PluginLedgerConnectorFabric; + const registrarKey = "registrar"; + const client2Key = "client-ws"; + + beforeAll(async () => { await Containers.logDiagnostics({ logLevel }); - }); - const ledger = new FabricTestLedgerV1({ - emitContainerLogs: true, - publishAllPorts: true, - imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, - imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, - envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), - logLevel, - }); + ledger = new FabricTestLedgerV1({ + emitContainerLogs: true, + publishAllPorts: true, + imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + imageVersion: FABRIC_25_LTS_AIO_IMAGE_VERSION, + envVars: new Map([["FABRIC_VERSION", FABRIC_25_LTS_AIO_FABRIC_VERSION]]), + logLevel, + }); - test.onFinish(async () => { - await ledger.stop(); - await ledger.destroy(); - await pruneDockerAllIfGithubAction({ logLevel }); - }); + await ledger.start({ omitPull: false }); - const wsTestContainer = new WsTestServer({}); - await wsTestContainer.start(); - await ledger.start({ omitPull: false }); + wsTestContainer = new WsTestServer({}); + await wsTestContainer.start(); - const connectionProfile = await ledger.getConnectionProfileOrg1(); - t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); + const connectionProfile = await ledger.getConnectionProfileOrg1(); + expect(connectionProfile).toBeTruthy(); - const registrarKey = "registrar"; - const client2Key = "client-ws"; - const keychainInstanceId = uuidv4(); - const keychainId = uuidv4(); + const keychainInstanceId = uuidv4(); + keychainId = uuidv4(); - const ci = await Containers.getById(wsTestContainer.containerId); - const wsIpAddr = await internalIpV4(); - const hostPort = await Containers.getPublicPort(WS_IDENTITY_HTTP_PORT, ci); + const ci = await Containers.getById(wsTestContainer.containerId); + const wsIpAddr = await internalIpV4(); + const hostPort = await Containers.getPublicPort(WS_IDENTITY_HTTP_PORT, ci); - const wsUrl = `http://${wsIpAddr}:${hostPort}`; + const wsUrl = `http://${wsIpAddr}:${hostPort}`; - const wsConfig: IWebSocketConfig = { - endpoint: wsUrl, - pathPrefix: "/identity", - }; + const wsConfig: IWebSocketConfig = { + endpoint: wsUrl, + pathPrefix: "/identity", + }; - // external web-socket client - const wsAdmin = new WsWallet({ - keyName: "admin", - logLevel, - strictSSL: false, - }); + wsAdmin = new WsWallet({ + keyName: "admin", + logLevel, + strictSSL: false, + }); - // external web-socket client - const wsUser = new WsWallet({ - keyName: "user", - logLevel, - strictSSL: false, - }); + wsUser = new WsWallet({ + keyName: "user", + logLevel, + strictSSL: false, + }); - test.onFinish(async () => { - await wsTestContainer.stop(); - await wsTestContainer.destroy(); - await wsAdmin.close(); - await wsUser.close(); - }); + keychainPlugin = new PluginKeychainMemory({ + instanceId: keychainInstanceId, + keychainId: keychainId, + logLevel, + }); - /// - const keychainPlugin = new PluginKeychainMemory({ - instanceId: keychainInstanceId, - keychainId: keychainId, - logLevel, - }); + const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); - const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); + const discoveryOptions: DiscoveryOptions = { + enabled: true, + asLocalhost: true, + }; - const discoveryOptions: DiscoveryOptions = { - enabled: true, - asLocalhost: true, - }; - const supportedIdentity: FabricSigningCredentialType[] = [ - FabricSigningCredentialType.WsX509, - FabricSigningCredentialType.X509, - ]; + const supportedIdentity: FabricSigningCredentialType[] = [ + FabricSigningCredentialType.WsX509, + FabricSigningCredentialType.X509, + ]; - const pluginOptions: IPluginLedgerConnectorFabricOptions = { - instanceId: uuidv4(), - pluginRegistry, - sshConfig: {}, - cliContainerEnv: {}, - peerBinary: "not-required", - logLevel, - connectionProfile, - discoveryOptions, - eventHandlerOptions: { - strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, - commitTimeout: 300, - }, - supportedIdentity, - webSocketConfig: wsConfig, - }; + const pluginOptions: IPluginLedgerConnectorFabricOptions = { + instanceId: uuidv4(), + pluginRegistry, + sshConfig: {}, + cliContainerEnv: {}, + peerBinary: "not-required", + logLevel, + connectionProfile, + discoveryOptions, + eventHandlerOptions: { + strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, + commitTimeout: 300, + }, + supportedIdentity, + webSocketConfig: wsConfig, + }; - const plugin = new PluginLedgerConnectorFabric(pluginOptions); + plugin = new PluginLedgerConnectorFabric(pluginOptions); - const wsIdClient = new WsIdentityClient({ - apiVersion: "v1", - endpoint: wsUrl, - rpDefaults: { - strictSSL: false, - }, + wsIdClient = new WsIdentityClient({ + apiVersion: "v1", + endpoint: wsUrl, + rpDefaults: { + strictSSL: false, + }, + }); }); - t.test("with-webSocketKey", async (t: Test) => { + afterAll(async () => { + await ledger.stop(); + await ledger.destroy(); + await pruneDockerAllIfGithubAction({ logLevel }); + await wsTestContainer.stop(); + await wsTestContainer.destroy(); + await wsAdmin.close(); + await wsUser.close(); + }); + + test("run-transaction-with-webSocketKey", async () => { let webSocketKeyAdmin, webSocketKeyUser; { const { sessionId, url } = JSON.parse( @@ -180,10 +184,10 @@ test("run-transaction-with-ws-ids", async (t: Test) => { }, ); const rawCert = await keychainPlugin.get(registrarKey + "-ws"); - t.ok(rawCert); + expect(rawCert).toBeTruthy(); const certData = JSON.parse(rawCert) as IIdentityData; - t.equal(certData.type, FabricSigningCredentialType.WsX509); - t.notok(certData.credentials.privateKey); + expect(certData.type).toBe(FabricSigningCredentialType.WsX509); + expect(certData.credentials.privateKey).toBeFalsy(); } { // register a client using registrar's ws identity @@ -201,7 +205,7 @@ test("run-transaction-with-ws-ids", async (t: Test) => { }, "ca.org1.example.com", ); - t.equal(secret, "pw"); + expect(secret).toBe("pw"); } { const { sessionId, url } = JSON.parse( @@ -233,12 +237,13 @@ test("run-transaction-with-ws-ids", async (t: Test) => { }, ); const rawCert = await keychainPlugin.get(client2Key); - t.ok(rawCert, "rawCert truthy OK"); + expect(rawCert).toBeTruthy(); const { type, credentials } = JSON.parse(rawCert) as IIdentityData; const { privateKey } = credentials; - t.equal(type, FabricSigningCredentialType.WsX509, "Cert is X509 OK"); - t.notok(privateKey, "certData.credentials.privateKey falsy OK"); + expect(type).toBe(FabricSigningCredentialType.WsX509); + expect(privateKey).toBeFalsy(); } + // Temporary workaround here: Deploy a second contract because the default // one is being hammered with "InitLedger" transactions by the container's // own healthcheck (see healthcheck.sh in the fabric-all-in-one folder). @@ -264,9 +269,7 @@ test("run-transaction-with-ws-ids", async (t: Test) => { const timeout = 180000; // 3 minutes const cwd = "/fabric-samples/test-network/"; const out = await Containers.exec(container, cmd, timeout, logLevel, cwd); - t.ok(out, "deploy Basic2 command output truthy OK"); - t.comment("Output of Basic2 contract deployment below:"); - t.comment(out); + expect(out).toBeTruthy(); { // make invoke InitLedger using a client1 client @@ -315,9 +318,7 @@ test("run-transaction-with-ws-ids", async (t: Test) => { params: ["asset1"], }); const asset = JSON.parse(resp.functionOutput); - t.equal(asset.Owner, "client2"); + expect(asset.Owner).toBe("client2"); } - t.end(); }); - t.end(); });