From a53e0841e246a54df40f0ad06bd76660eaa0a09d Mon Sep 17 00:00:00 2001 From: Elena Izaguirre Date: Wed, 8 Sep 2021 13:25:45 +0200 Subject: [PATCH] fix: openapi validation test for keychain-vault plugin Includes tests for endpoints setKeychainEntryV1, getKeychainEntryV1, deleteKeychainEntryV1 and hasKeychainEntryV1, each of them, when appropiate, with test cases: - Right request - Request including an invalid parameter - Request without a required parameter Closes #1329 Relationed with #847 Signed-off-by: Elena Izaguirre --- .../src/main/json/openapi.json | 22 +- .../generated/openapi/typescript-axios/api.ts | 16 +- .../openapi/openapi-validation.test.ts | 310 ++++++++++++++++++ 3 files changed, 329 insertions(+), 19 deletions(-) create mode 100644 packages/cactus-plugin-keychain-vault/src/test/typescript/integration/openapi/openapi-validation.test.ts diff --git a/packages/cactus-plugin-keychain-vault/src/main/json/openapi.json b/packages/cactus-plugin-keychain-vault/src/main/json/openapi.json index a63e8f8b5f..4302e65dea 100644 --- a/packages/cactus-plugin-keychain-vault/src/main/json/openapi.json +++ b/packages/cactus-plugin-keychain-vault/src/main/json/openapi.json @@ -94,23 +94,23 @@ "summary": "Retrieves the contents of a keychain entry from the backend.", "parameters": [], "requestBody": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_get_entry_request_body" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_get_entry_request_body" }, "responses": { "200": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_200" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_200" }, "400": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_400" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_400" }, "401": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_401" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_401" }, "404": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_404" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_404" }, "500": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_500" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_get_entry_500" } } } @@ -195,20 +195,20 @@ "summary": "Sets a value under a key on the keychain backend.", "parameters": [], "requestBody": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_set_entry_request_body" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/requestBodies/keychain_set_entry_request_body" }, "responses": { "200": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_200" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_200" }, "400": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_400" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_400" }, "401": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_401" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_401" }, "500": { - "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.5.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_500" + "$ref": "https://raw.githubusercontent.com/hyperledger/cactus/v0.8.0/packages/cactus-core-api/src/main/json/openapi.json#/components/responses/keychain_set_entry_500" } } } diff --git a/packages/cactus-plugin-keychain-vault/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-keychain-vault/src/main/typescript/generated/openapi/typescript-axios/api.ts index e1cf69fa48..ab09075ca3 100644 --- a/packages/cactus-plugin-keychain-vault/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-keychain-vault/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -193,7 +193,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati /** * * @summary Retrieves the contents of a keychain entry from the backend. - * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Request body to obtain a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -293,7 +293,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati /** * * @summary Sets a value under a key on the keychain backend. - * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Request body to write/update a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -350,7 +350,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { /** * * @summary Retrieves the contents of a keychain entry from the backend. - * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Request body to obtain a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -382,7 +382,7 @@ export const DefaultApiFp = function(configuration?: Configuration) { /** * * @summary Sets a value under a key on the keychain backend. - * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Request body to write/update a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -413,7 +413,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa /** * * @summary Retrieves the contents of a keychain entry from the backend. - * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Request body to obtain a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -442,7 +442,7 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa /** * * @summary Sets a value under a key on the keychain backend. - * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Request body to write/update a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} */ @@ -474,7 +474,7 @@ export class DefaultApi extends BaseAPI { /** * * @summary Retrieves the contents of a keychain entry from the backend. - * @param {GetKeychainEntryRequest} getKeychainEntryRequest Requst body to obtain a keychain entry via its key + * @param {GetKeychainEntryRequest} getKeychainEntryRequest Request body to obtain a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApi @@ -509,7 +509,7 @@ export class DefaultApi extends BaseAPI { /** * * @summary Sets a value under a key on the keychain backend. - * @param {SetKeychainEntryRequest} setKeychainEntryRequest Requst body to write/update a keychain entry via its key + * @param {SetKeychainEntryRequest} setKeychainEntryRequest Request body to write/update a keychain entry via its key * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApi diff --git a/packages/cactus-plugin-keychain-vault/src/test/typescript/integration/openapi/openapi-validation.test.ts b/packages/cactus-plugin-keychain-vault/src/test/typescript/integration/openapi/openapi-validation.test.ts new file mode 100644 index 0000000000..15f3456393 --- /dev/null +++ b/packages/cactus-plugin-keychain-vault/src/test/typescript/integration/openapi/openapi-validation.test.ts @@ -0,0 +1,310 @@ +import test, { Test } from "tape-promise/tape"; +import { v4 as internalIpV4 } from "internal-ip"; + +import express from "express"; +import bodyParser from "body-parser"; +import http from "http"; +import { AddressInfo } from "net"; + +import { + Containers, + K_DEFAULT_VAULT_DEV_ROOT_TOKEN, + K_DEFAULT_VAULT_HTTP_PORT, + VaultTestServer, +} from "@hyperledger/cactus-test-tooling"; + +import { v4 as uuidv4 } from "uuid"; + +import { + LogLevelDesc, + IListenOptions, + Servers, +} from "@hyperledger/cactus-common"; + +import { + Configuration, + DeleteKeychainEntryRequestV1, + GetKeychainEntryRequest, + HasKeychainEntryRequestV1, + IPluginKeychainVaultOptions, + PluginKeychainVault, + SetKeychainEntryRequest, +} from "../../../../main/typescript/public-api"; + +import { DefaultApi as KeychainVaultApi } from "../../../../main/typescript/public-api"; + +import { installOpenapiValidationMiddleware } from "@hyperledger/cactus-core"; +import OAS from "../../../../main/json/openapi.json"; + +const logLevel: LogLevelDesc = "TRACE"; +const testCase = "cactus-plugin-keychain-vault API"; + +test(`${testCase}`, async (t: Test) => { + const vaultTestContainer = new VaultTestServer({}); + await vaultTestContainer.start(); + + const ci = await Containers.getById(vaultTestContainer.containerId); + const vaultIpAddr = await internalIpV4(); + const hostPort = await Containers.getPublicPort( + K_DEFAULT_VAULT_HTTP_PORT, + ci, + ); + const vaultHost = `http://${vaultIpAddr}:${hostPort}`; + + test.onFinish(async () => { + await vaultTestContainer.stop(); + await vaultTestContainer.destroy(); + }); + + const options: IPluginKeychainVaultOptions = { + instanceId: uuidv4(), + keychainId: uuidv4(), + endpoint: vaultHost, + token: K_DEFAULT_VAULT_DEV_ROOT_TOKEN, + apiVersion: "v1", + kvSecretsMountPath: "secret/data/", + logLevel, + }; + const plugin = new PluginKeychainVault(options); + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const server = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "0.0.0.0", + 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 }); + const apiClient = new KeychainVaultApi(apiConfig); + + await installOpenapiValidationMiddleware({ + logLevel, + app: expressApp, + apiSpec: OAS, + }); + + await plugin.getOrCreateWebServices(); + await plugin.registerWebServices(expressApp); + + t.equal(plugin.getKeychainId(), options.keychainId, "Keychain ID set OK"); + t.equal(plugin.getInstanceId(), options.instanceId, "Instance ID set OK"); + + const key1 = uuidv4(); + const value1 = uuidv4(); + + const fSet = "setKeychainEntryV1"; + const fGet = "getKeychainEntryV1"; + const fDelete = "deleteKeychainEntryV1"; + const fHas = "hasKeychainEntryV1"; + const cOk = "without bad request error"; + 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: key1, + value: value1, + }); + t2.equal(res.status, 200, `Endpoint ${fSet}: response.status === 200 OK`); + t2.end(); + }); + + test(`${testCase} - ${fHas} - ${cOk}`, async (t2: Test) => { + const res = await apiClient.hasKeychainEntryV1({ key: key1 }); + t2.equal(res.status, 200, `Endpoint ${fHas}: response.status === 200 OK`); + t2.end(); + }); + + test(`${testCase} - ${fGet} - ${cOk}`, async (t2: Test) => { + const res = await apiClient.getKeychainEntryV1({ key: key1 }); + t2.equal(res.status, 200, `Endpoint ${fGet}: response.status === 200 OK`); + t2.end(); + }); + + test(`${testCase} - ${fDelete} - ${cOk}`, async (t2: Test) => { + const res = await apiClient.deleteKeychainEntryV1({ key: key1 }); + t2.equal( + res.status, + 200, + `Endpoint ${fDelete}: response.status === 200 OK`, + ); + t2.end(); + }); + + test(`${testCase} - ${fSet} - ${cWithoutParams}`, async (t2: Test) => { + try { + await apiClient.setKeychainEntryV1(({ + value: value1, + } as any) as SetKeychainEntryRequest); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fSet} without required key: response.status === 400 OK`, + ); + const fields = e.response.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok(fields.includes("key"), "Rejected because key is required"); + } + t2.end(); + }); + + test(`${testCase} - ${fHas} - ${cWithoutParams}`, async (t2: Test) => { + 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`, + ); + const fields = e.response.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok(fields.includes("key"), "Rejected because key is required"); + } + t2.end(); + }); + + test(`${testCase} - ${fGet} - ${cWithoutParams}`, async (t2: Test) => { + try { + await apiClient.getKeychainEntryV1( + ({} as any) as GetKeychainEntryRequest, + ); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fGet} without required key: response.status === 400 OK`, + ); + const fields = e.response.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok(fields.includes("key"), "Rejected because key is required"); + } + t2.end(); + }); + + test(`${testCase} - ${fDelete} - ${cWithoutParams}`, async (t2: Test) => { + 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`, + ); + const fields = e.response.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok(fields.includes("key"), "Rejected because key is required"); + } + t2.end(); + }); + + test(`${testCase} - ${fSet} - ${cInvalidParams}`, async (t2: Test) => { + try { + await apiClient.setKeychainEntryV1(({ + key: key1, + value: value1, + fake: 4, + } as any) as SetKeychainEntryRequest); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fSet} with fake=4: response.status === 400 OK`, + ); + 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", + ); + } + t2.end(); + }); + + test(`${testCase} - ${fHas} - ${cInvalidParams}`, async (t2: Test) => { + try { + await apiClient.hasKeychainEntryV1(({ + key: key1, + fake: 4, + } as any) as HasKeychainEntryRequestV1); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fHas} with fake=4: response.status === 400 OK`, + ); + 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", + ); + } + t2.end(); + }); + + test(`${testCase} - ${fGet} - ${cInvalidParams}`, async (t2: Test) => { + try { + await apiClient.getKeychainEntryV1(({ + key: key1, + fake: 4, + } as any) as GetKeychainEntryRequest); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fGet} with fake=4: response.status === 400 OK`, + ); + 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", + ); + } + t2.end(); + }); + + test(`${testCase} - ${fDelete} - ${cInvalidParams}`, async (t2: Test) => { + try { + await apiClient.deleteKeychainEntryV1(({ + key: key1, + fake: 4, + } as any) as GetKeychainEntryRequest); + } catch (e) { + t2.equal( + e.response.status, + 400, + `Endpoint ${fDelete} with fake=4: response.status === 400 OK`, + ); + 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", + ); + } + t2.end(); + }); + + t.end(); +});