diff --git a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts index 75dcda5cb3..3888c56389 100644 --- a/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts +++ b/packages/cactus-cmd-api-server/src/main/typescript/api-server.ts @@ -43,6 +43,7 @@ import { Bools, Logger, LoggerProvider, + LogHelper, Servers, } from "@hyperledger/cactus-common"; @@ -241,7 +242,8 @@ export class ApiServer { return { addressInfoCockpit, addressInfoApi, addressInfoGrpc }; } catch (ex) { - const errorMessage = `Failed to start ApiServer: ${ex.stack}`; + const stack = LogHelper.getExceptionStack(ex); + const errorMessage = `Failed to start ApiServer: ${stack}`; this.log.error(errorMessage); this.log.error(`Attempting shutdown...`); try { @@ -296,9 +298,10 @@ export class ApiServer { await this.getPluginImportsCount(), ); return this.pluginRegistry; - } catch (e) { + } catch (ex) { this.pluginRegistry = new PluginRegistry({ plugins: [] }); - const errorMessage = `Failed init PluginRegistry: ${e.stack}`; + const stack = LogHelper.getExceptionStack(ex); + const errorMessage = `Failed init PluginRegistry: ${stack}`; this.log.error(errorMessage); throw new Error(errorMessage); } diff --git a/packages/cactus-common/src/main/typescript/logging/log-helper.ts b/packages/cactus-common/src/main/typescript/logging/log-helper.ts new file mode 100644 index 0000000000..5fca30528e --- /dev/null +++ b/packages/cactus-common/src/main/typescript/logging/log-helper.ts @@ -0,0 +1,100 @@ +import { RuntimeError } from "run-time-error"; + +export class LogHelper { + public static getExceptionStack(exception: unknown): string { + // handle unknown exception input + const defaultStack = "NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION"; + const invalidStack = "INVALID_STACK_INFORMATION"; + let stack = defaultStack; + let exceptionHandled = false; + + // 1st need to check that exception is not null or undefined before trying to access the wanted stack information + if (exception) { + if (exception instanceof RuntimeError) { + // handling RuntimeError stack inclusive nested / cascaded stacks + stack = this.safeJsonStringify(exception); + exceptionHandled = true; + } + + if (!exceptionHandled && typeof exception === "object") { + // 2nd need to check if a stack property is available + if (Object.hasOwnProperty.call(exception, "stack")) { + // 3rd check if the stack property is already of type string + if ( + typeof (exception as Record).stack === "string" + ) { + stack = (exception as { stack: string }).stack; + } else { + // need to stringify stack information first + stack = this.safeJsonStringify( + (exception as { stack: unknown }).stack, + invalidStack, + ); + } + } + } + } + return stack; + } + + public static getExceptionMessage(exception: unknown): string { + // handle unknown exception input + const defaultMessage = "NO_MESSAGE_INCLUDED_IN_EXCEPTION"; + const invalidException = "INVALID_EXCEPTION"; + const invalidMessage = "INVALID_EXCEPTION_MESSAGE"; + const customExceptionPrefix = "A CUSTOM EXCEPTION WAS THROWN: "; + let message = defaultMessage; + let exceptionHandled = false; + + // 1st need to check that exception is not null or undefined before trying to access the wanted message information + if (exception) { + if (typeof exception === "object") { + // 2nd need to check if a message property is available + if (Object.hasOwnProperty.call(exception, "message")) { + // 3rd check if the message property is already of type string + if ( + typeof (exception as Record).message === "string" + ) { + message = (exception as { message: string }).message; + } else { + // need to stringify message information first + message = this.safeJsonStringify( + (exception as { message: unknown }).message, + invalidMessage, + ); + } + exceptionHandled = true; + } + } + + // handling of custom exceptions + if (!exceptionHandled) { + // check if thrown custom exception is a string type only -> directly use it as exception message + if (typeof exception === "string") { + message = exception; + } else { + // custom exception is of a different type -> need to stringify it + message = + customExceptionPrefix && + this.safeJsonStringify(exception, invalidException); + } + } + } + return message; + } + + private static safeJsonStringify( + input: unknown, + catchMessage = "INVALID_INPUT", + ): string { + let message = ""; + + try { + message = JSON.stringify(input); + } catch (error) { + // stringify failed maybe due to cyclic dependency + message = catchMessage; + } + return message; + } +} diff --git a/packages/cactus-common/src/main/typescript/public-api.ts b/packages/cactus-common/src/main/typescript/public-api.ts index 209b91f6dc..d46813e557 100755 --- a/packages/cactus-common/src/main/typescript/public-api.ts +++ b/packages/cactus-common/src/main/typescript/public-api.ts @@ -1,4 +1,5 @@ export { LoggerProvider } from "./logging/logger-provider"; +export { LogHelper } from "./logging/log-helper"; export { Logger, ILoggerOptions } from "./logging/logger"; export { LogLevelDesc } from "loglevel"; export { Objects } from "./objects"; diff --git a/packages/cactus-common/src/test/typescript/unit/logging/log-helper.test.ts b/packages/cactus-common/src/test/typescript/unit/logging/log-helper.test.ts new file mode 100644 index 0000000000..23097d08df --- /dev/null +++ b/packages/cactus-common/src/test/typescript/unit/logging/log-helper.test.ts @@ -0,0 +1,301 @@ +import "jest-extended"; +import { RuntimeError } from "run-time-error"; +import { LogHelper } from "../../../../main/typescript/logging/log-helper"; + +describe("log-helper tests", () => { + const no_message_available = "NO_MESSAGE_INCLUDED_IN_EXCEPTION"; + const no_stack_available = "NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION"; + const errorMessage = "Oops"; + const errorNumber = 2468; + + describe("exception stack-tests", () => { + it("gets the stack information from a regular Error object", () => { + let expectedResult: string | undefined = ""; + let stack = no_stack_available; + + try { + const testError = new Error(errorMessage); + expectedResult = testError.stack; + throw testError; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + + it("gets stack information from a faked Error object which is containing stack information as string type", () => { + const expectedResult = "Faked stack"; + let stack = no_stack_available; + + const fakeErrorWithStack = { + message: + "This is a fake error object with string-type stack information", + stack: expectedResult, + }; + + try { + throw fakeErrorWithStack; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + + it("gets stack information from a faked Error object which is containing stack information as number type and therefore need to be stringified", () => { + const expectedResult = "123456"; + let stack = no_stack_available; + + const fakeErrorWithStack = { + message: + "This is a fake error object with number-type stack information", + stack: 123456, + }; + + try { + throw fakeErrorWithStack; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + + it("gets no stack information as the faked Error object is not containing any stack information", () => { + const expectedResult = no_stack_available; + let stack = no_stack_available; + + const fakeErrorWithoutStack = { + message: "This is a fake error object without stack information", + }; + + try { + throw fakeErrorWithoutStack; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + + it("handles throwing null successfully and returns NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION string", () => { + const expectedResult = no_stack_available; + let stack = no_stack_available; + + const fakeError = null; + + try { + throw fakeError; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + + it("handles throwing undefined successfully and returns NO_STACK_INFORMATION_INCLUDED_IN_EXCEPTION string", () => { + const expectedResult = no_stack_available; + let stack = no_stack_available; + + const fakeError = undefined; + + try { + throw fakeError; + } catch (error) { + stack = LogHelper.getExceptionStack(error); + } + + // check stack + expect(stack).toBe(expectedResult); + }); + }); + + describe("exception message-tests", () => { + it("gets the exception message from a regular Error object", () => { + const expectedResult = errorMessage; + let message = no_message_available; + + try { + const testError = new Error(errorMessage); + throw testError; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + + it("gets the exception message from a faked Error object which is containing message as string type", () => { + const expectedResult = errorMessage; + let message = no_message_available; + + const fakeErrorWithMessage = { + message: errorMessage, + stack: expectedResult, + }; + + try { + throw fakeErrorWithMessage; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + + it("gets exception message from a faked Error object which is containing message information as number type and therefore need to be stringified", () => { + const expectedResult = "123456"; + let message = no_message_available; + + const fakeErrorWithNumberMessage = { + message: 123456, + }; + + try { + throw fakeErrorWithNumberMessage; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + + it("gets no exception message information as the faked Error object is not containing any message information and therefore tries to stringify the whole exception", () => { + const expectedResult = + '{"stack":"This is a fake error object without message information"}'; + let message = no_message_available; + + const fakeErrorWithoutMessage = { + stack: "This is a fake error object without message information", + }; + + try { + throw fakeErrorWithoutMessage; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + + it("handles throwing null successfully and returning NO_MESSAGE_INCLUDED_IN_EXCEPTION string", () => { + const expectedResult = no_message_available; + let message = no_message_available; + + const fakeError = null; + + try { + throw fakeError; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + + it("handles throwing undefined successfully and returning NO_MESSAGE_INCLUDED_IN_EXCEPTION string", () => { + const expectedResult = no_message_available; + let message = no_message_available; + + const fakeError = undefined; + + try { + throw fakeError; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + } + + // check message + expect(message).toBe(expectedResult); + }); + }); + + describe("handling of custom exceptions", () => { + it("handles a thrown string", () => { + const expectedErrorMessage = errorMessage; + const expectedStack = no_stack_available; + let message = no_message_available; + let stack = no_stack_available; + + try { + throw errorMessage; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + stack = LogHelper.getExceptionStack(error); + } + + // check message + stack + expect(message).toBe(expectedErrorMessage); + expect(stack).toBe(expectedStack); + }); + + it("handles a thrown number", () => { + const expectedErrorMessage = `${errorNumber}`; + const expectedStack = no_stack_available; + let message = no_message_available; + let stack = no_stack_available; + + try { + throw errorNumber; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + stack = LogHelper.getExceptionStack(error); + } + + // check message + stack + expect(message).toBe(expectedErrorMessage); + expect(stack).toBe(expectedStack); + }); + + it("handles an arbitrary exception", () => { + const expectedErrorMessage = '{"error":"Oops"}'; + const expectedStack = no_stack_available; + let message = no_message_available; + let stack = no_stack_available; + const arbitraryException = { error: errorMessage }; + + try { + throw arbitraryException; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + stack = LogHelper.getExceptionStack(error); + } + + // check message + stack + expect(message).toBe(expectedErrorMessage); + expect(stack).toBe(expectedStack); + }); + + it("handles nested exceptions", () => { + const expectedErrorMessage = "RTE3"; + const expectedStackPart = "RTE1"; + let message = no_message_available; + let stack = no_stack_available; + const rtE1 = new RuntimeError("RTE1"); + const rtE2 = new RuntimeError("RTE2", rtE1); + const rtE3 = new RuntimeError("RTE3", rtE2); + + try { + throw rtE3; + } catch (error) { + message = LogHelper.getExceptionMessage(error); + stack = LogHelper.getExceptionStack(error); + } + + // check message + stack + expect(message).toBe(expectedErrorMessage); + expect(stack).toContain(expectedStackPart); + }); + }); +}); diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts index 70e94a8f39..ff4bb4d0da 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-endpoint.ts @@ -12,6 +12,7 @@ import { LogLevelDesc, LoggerProvider, IAsyncProvider, + LogHelper, } from "@hyperledger/cactus-common"; import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; @@ -91,12 +92,21 @@ export class DeployContractSolidityBytecodeEndpoint try { const resBody = await this.options.connector.deployContract(reqBody); res.json(resBody); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`Crash while serving ${reqTag}`, ex); - res.status(500).json({ - message: "Internal Server Error", - error: ex?.stack || ex?.message, - }); + if (ex instanceof Error) { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } else { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-json-object-endpoint.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-json-object-endpoint.ts index fef76c14b4..70e2abd446 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-json-object-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/deploy-contract-solidity-bytecode-json-object-endpoint.ts @@ -19,6 +19,7 @@ import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; import { PluginLedgerConnectorXdai } from "../plugin-ledger-connector-xdai"; import { DeployContractJsonObjectV1Request } from "../generated/openapi/typescript-axios"; import OAS from "../../json/openapi.json"; +import { LogHelper } from "@hyperledger/cactus-common"; export interface IDeployContractSolidityBytecodeJsonObjectOptions { logLevel?: LogLevelDesc; @@ -96,12 +97,21 @@ export class DeployContractSolidityBytecodeJsonObjectEndpoint reqBody, ); res.json(resBody); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`Crash while serving ${reqTag}`, ex); - res.status(500).json({ - message: "Internal Server Error", - error: ex?.stack || ex?.message, - }); + if (ex instanceof Error) { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } else { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts index ccac3b7803..ef724e3329 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/get-prometheus-exporter-metrics-endpoint-v1.ts @@ -16,6 +16,7 @@ import { LoggerProvider, Checks, IAsyncProvider, + LogHelper, } from "@hyperledger/cactus-common"; import { PluginLedgerConnectorXdai } from "../plugin-ledger-connector-xdai"; @@ -90,11 +91,19 @@ export class GetPrometheusExporterMetricsEndpointV1 const resBody = await this.options.connector.getPrometheusExporterMetrics(); res.status(200); res.send(resBody); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`${fnTag} failed to serve request`, ex); - res.status(500); - res.statusMessage = ex.message; - res.json({ error: ex.stack }); + if (ex instanceof Error) { + res.status(500); + res.statusMessage = messages; + res.json({ error: stack }); + } else { + res.status(500); + res.statusMessage = messages; + res.json({ error: stack }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-endpoint.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-endpoint.ts index ef93f648d3..97e7778d3d 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-endpoint.ts @@ -6,6 +6,7 @@ import { LogLevelDesc, LoggerProvider, IAsyncProvider, + LogHelper, } from "@hyperledger/cactus-common"; import { IEndpointAuthzOptions, @@ -88,12 +89,21 @@ export class InvokeContractEndpoint implements IWebServiceEndpoint { try { const resBody = await this.options.connector.invokeContract(reqBody); res.json(resBody); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`Crash while serving ${reqTag}`, ex); - res.status(500).json({ - message: "Internal Server Error", - error: ex?.stack || ex?.message, - }); + if (ex instanceof Error) { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } else { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-json-object-endpoint.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-json-object-endpoint.ts index 7a400e4199..325dd6d225 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-json-object-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/invoke-contract-json-object-endpoint.ts @@ -6,6 +6,7 @@ import { LogLevelDesc, LoggerProvider, IAsyncProvider, + LogHelper, } from "@hyperledger/cactus-common"; import { IEndpointAuthzOptions, @@ -93,12 +94,21 @@ export class InvokeContractJsonObjectEndpoint implements IWebServiceEndpoint { reqBody, ); res.json(resBody); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`Crash while serving ${reqTag}`, ex); - res.status(500).json({ - message: "Internal Server Error", - error: ex?.stack || ex?.message, - }); + if (ex instanceof Error) { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } else { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/run-transaction-endpoint.ts b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/run-transaction-endpoint.ts index d25d2e35fe..aafb58156b 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/run-transaction-endpoint.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/main/typescript/web-services/run-transaction-endpoint.ts @@ -6,6 +6,7 @@ import { LogLevelDesc, LoggerProvider, IAsyncProvider, + LogHelper, } from "@hyperledger/cactus-common"; import { IEndpointAuthzOptions, @@ -17,6 +18,7 @@ import { registerWebServiceEndpoint } from "@hyperledger/cactus-core"; import { PluginLedgerConnectorXdai } from "../plugin-ledger-connector-xdai"; import OAS from "../../json/openapi.json"; +import axios from "axios"; export interface IRunTransactionEndpointOptions { logLevel?: LogLevelDesc; @@ -88,12 +90,21 @@ export class RunTransactionEndpoint implements IWebServiceEndpoint { try { const resBody = await this.options.connector.transact(reqBody); res.json({ success: true, data: resBody }); - } catch (ex) { + } catch (ex: unknown) { + const stack = LogHelper.getExceptionStack(ex); + const messages = LogHelper.getExceptionMessage(ex); this.log.error(`Crash while serving ${reqTag}`, ex); - res.status(500).json({ - message: "Internal Server Error", - error: ex?.stack || ex?.message, - }); + if (axios.isAxiosError(ex)) { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } else { + res.status(500).json({ + message: "Internal Server Error", + error: stack || messages, + }); + } } } } diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai-json-object.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai-json-object.test.ts index 323be6510d..bb38c15d2a 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai-json-object.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai-json-object.test.ts @@ -29,6 +29,7 @@ import bodyParser from "body-parser"; import http from "http"; import { AddressInfo } from "net"; import { K_CACTUS_XDAI_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; +import axios from "axios"; const testCase = "deploys contract via .json file"; const logLevel: LogLevelDesc = "TRACE"; @@ -227,12 +228,16 @@ test(testCase, async (t: Test) => { contractJSON: HelloWorldContractJson, }); t2.ifError(setNameOutInvalid); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + t2.notStrictEqual( + error, + "Nonce too low", + "setName() invocation with invalid nonce", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContractJsonObject( { diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai.test.ts index 634be16779..fff999dd22 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/deploy-contract-from-json-xdai.test.ts @@ -32,6 +32,7 @@ import bodyParser from "body-parser"; import http from "http"; import { AddressInfo } from "net"; import { K_CACTUS_XDAI_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; +import axios from "axios"; const testCase = "deploys contract via .json file"; describe(testCase, () => { @@ -248,8 +249,12 @@ describe(testCase, () => { nonce: 1, }); fail("It should not reach here"); - } catch (error) { - expect(error).not.toBe("Nonce too low"); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + expect(error).not.toBe("Nonce too low"); + } else { + fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContract({ contractName, @@ -347,8 +352,12 @@ describe(testCase, () => { nonce: 4, }); fail("It should not reach here"); - } catch (error) { - expect(error).not.toBe("Nonce too low"); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + expect(error).not.toBe("Nonce too low"); + } else { + fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContract({ diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai-json-object.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai-json-object.test.ts index 46d8917aa1..0f2b7ad241 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai-json-object.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai-json-object.test.ts @@ -18,6 +18,7 @@ import { LogLevelDesc } from "@hyperledger/cactus-common"; import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; import Web3 from "web3"; import { PluginImportType } from "@hyperledger/cactus-core-api"; +import axios from "axios"; test("deploys contract via .json file", async (t: Test) => { const logLevel: LogLevelDesc = "TRACE"; @@ -196,12 +197,16 @@ test("deploys contract via .json file", async (t: Test) => { contractJSON: HelloWorldContractJson, }); t2.ifError(setNameOutInvalid.transactionReceipt); - } catch (error) { - t2.notStrictEqual( - error, - "Nonce too low", - "setName() invocation with invalid nonce", - ); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + t2.notStrictEqual( + error, + "Nonce too low", + "setName() invocation with invalid nonce", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContractJsonObject( { diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai.test.ts index a426c9a00f..cbfa7bfd1d 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/invoke-contract-xdai.test.ts @@ -20,6 +20,7 @@ import { LogLevelDesc } from "@hyperledger/cactus-common"; import HelloWorldContractJson from "../../solidity/hello-world-contract/HelloWorld.json"; import Web3 from "web3"; import { PluginImportType } from "@hyperledger/cactus-core-api"; +import axios from "axios"; const logLevel: LogLevelDesc = "TRACE"; let xdaiTestLedger: OpenEthereumTestLedger; @@ -204,8 +205,12 @@ describe(testCase, () => { nonce: 1, }); fail("invalid nonce should have thrown"); - } catch (error: any) { - expect(error.message).toContain("Transaction nonce is too low."); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + expect(error.message).toContain("Transaction nonce is too low."); + } else { + fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContract({ contractName, @@ -302,10 +307,14 @@ describe(testCase, () => { nonce: 4, }); fail("invalid nonce should have thrown"); - } catch (error: any) { - expect(error.message).toContain( - "Transaction with the same hash was already imported", - ); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + expect(error.message).toContain( + "Transaction with the same hash was already imported", + ); + } else { + fail("expected an axios error, got something else"); + } } const { callOutput: getNameOut } = await connector.invokeContract({ contractName, diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation-no-keychain.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation-no-keychain.test.ts index e5e8b8cf9f..bb6d4bdb4e 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation-no-keychain.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation-no-keychain.test.ts @@ -30,6 +30,7 @@ import { import { installOpenapiValidationMiddleware } from "@hyperledger/cactus-core"; import OAS from "../../../../main/json/openapi.json"; +import axios from "axios"; const testCase = "xDai API"; const logLevel: LogLevelDesc = "TRACE"; @@ -141,19 +142,23 @@ test(testCase, async (t: Test) => { await apiClient.deployContractJsonObjectV1( (parameters as any) as DeployContractJsonObjectV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} without required contractJSON: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractJSON"), - "Rejected because contractJSON is required", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fDeploy} without required contractJSON: response.status === 400 OK`, + ); + const fields = e.response?.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok( + fields.includes("contractJSON"), + "Rejected because contractJSON is required", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -175,19 +180,23 @@ test(testCase, async (t: Test) => { await apiClient.deployContractJsonObjectV1( (parameters as any) as DeployContractJsonObjectV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} 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", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fDeploy} 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", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -236,19 +245,23 @@ test(testCase, async (t: Test) => { await apiClient.invokeContractJsonObject( (parameters as any) as InvokeContractJsonObjectV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} without required contractJSON: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractJSON"), - "Rejected because contractJSON is required", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fInvoke} without required contractJSON: response.status === 400 OK`, + ); + const fields = e.response?.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok( + fields.includes("contractJSON"), + "Rejected because contractJSON is required", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -273,19 +286,23 @@ test(testCase, async (t: Test) => { await apiClient.invokeContractJsonObject( (parameters as any) as InvokeContractJsonObjectV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} 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", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fInvoke} 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", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); diff --git a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation.test.ts b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation.test.ts index 4463ebfa46..f485999f6b 100644 --- a/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation.test.ts +++ b/packages/cactus-plugin-ledger-connector-xdai/src/test/typescript/integration/openapi/openapi-validation.test.ts @@ -33,6 +33,7 @@ import { import { installOpenapiValidationMiddleware } from "@hyperledger/cactus-core"; import OAS from "../../../../main/json/openapi.json"; +import axios from "axios"; const testCase = "xDai API"; const logLevel: LogLevelDesc = "TRACE"; @@ -165,19 +166,23 @@ test(testCase, async (t: Test) => { await apiClient.deployContractV1( (parameters as any) as DeployContractV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} without required keychainId: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("keychainId"), - "Rejected because keychainId is required", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fDeploy} without required keychainId: response.status === 400 OK`, + ); + const fields = e.response?.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok( + fields.includes("keychainId"), + "Rejected because keychainId is required", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -200,19 +205,23 @@ test(testCase, async (t: Test) => { await apiClient.deployContractV1( (parameters as any) as DeployContractV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fDeploy} 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", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fDeploy} 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", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -261,19 +270,23 @@ test(testCase, async (t: Test) => { await apiClient.invokeContractV1( (parameters as any) as InvokeContractV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} without required contractName: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("contractName"), - "Rejected because contractName is required", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fInvoke} without required contractName: response.status === 400 OK`, + ); + const fields = e.response?.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok( + fields.includes("contractName"), + "Rejected because contractName is required", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -298,19 +311,23 @@ test(testCase, async (t: Test) => { await apiClient.invokeContractV1( (parameters as any) as InvokeContractV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fInvoke} 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", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fInvoke} 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", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -361,19 +378,23 @@ test(testCase, async (t: Test) => { await apiClient.runTransactionV1( (parameters as any) as RunTransactionV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fRun} without required consistencyStrategy: response.status === 400 OK`, - ); - const fields = e.response.data.map((param: any) => - param.path.replace(".body.", ""), - ); - t2.ok( - fields.includes("consistencyStrategy"), - "Rejected because consistencyStrategy is required", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fRun} without required consistencyStrategy: response.status === 400 OK`, + ); + const fields = e.response?.data.map((param: any) => + param.path.replace(".body.", ""), + ); + t2.ok( + fields.includes("consistencyStrategy"), + "Rejected because consistencyStrategy is required", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end(); @@ -403,19 +424,23 @@ test(testCase, async (t: Test) => { await apiClient.runTransactionV1( (parameters as any) as RunTransactionV1Request, ); - } catch (e) { - t2.equal( - e.response.status, - 400, - `Endpoint ${fRun} 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", - ); + } catch (e: unknown) { + if (axios.isAxiosError(e)) { + t2.equal( + e.response?.status, + 400, + `Endpoint ${fRun} 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", + ); + } else { + t2.fail("expected an axios error, got something else"); + } } t2.end();