Skip to content

Commit

Permalink
test(cactus-core): refactor handle-rest-endpoint-exception.test.ts
Browse files Browse the repository at this point in the history
This change makes it easier to read and debug some of the assertions of
the test case which are a little convoluted due to the usage of the Jest
spies.

These changes were done as part of another fix but ultimately turned out
to be not a factor for that fix and therefore I thought it bess to separate
the change out onto it's own PR to make it easier to review.

Related to #2966 but this is not the actual fix.

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
petermetz committed Feb 8, 2024
1 parent de99950 commit d7c0806
Showing 1 changed file with 64 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,74 +10,98 @@ import {
} from "../../../main/typescript/public-api"; // replace with the correct path to your module

import { LoggerProvider } from "@hyperledger/cactus-common";
import {
identifierByCodes,
INTERNAL_SERVER_ERROR,
} from "http-errors-enhanced-cjs";

// Since we are testing error handling, lots of false positive error logs appear
// that are hard to untangle/confusing when reading the test log output.
// The SILENT level suppresses these.
const log = LoggerProvider.getOrCreate({
label: "handle-rest-endpoint-exception.test.ts",
level: "DEBUG",
level: "SILENT",
});

describe("handleRestEndpointException", () => {
afterEach(() => {
jest.clearAllMocks();
});

it("should handle HttpError with statusCode >= 500", async () => {
it("should handle HttpError with statusCode gte 500", async () => {
const mockResponse = createResponse();

const mockOptions: IHandleRestEndpointExceptionOptions = {
errorMsg: "Test error message",
const rootCauseErrorMsg = "Root Cause Exception that should cause gte 500";
const rootError = new Error(rootCauseErrorMsg);

const reThrowErrorMsg =
"Message of the Re-thrown Exception that should have some context for debugging on top of the information already available in the rootCauseErrorMsg.";

const ctx: IHandleRestEndpointExceptionOptions = {
errorMsg: reThrowErrorMsg,
log: jest.mocked(log), // Provide a mock logger if needed
error: new Error("Test error"), // Provide appropriate error objects for testing
error: rootError, // Provide appropriate error objects for testing
res: mockResponse,
};

const mockHttpError = createHttpError(500, "Test HTTP error", {
expose: true,
});

const errorAsSanitizedJson = safeStringifyException(mockHttpError);
const spyLogDebug = jest.spyOn(mockOptions.log, "debug");
const spyLog = jest.spyOn(ctx.log, "error");
const spyStatus = jest.spyOn(mockResponse, "status");
const spyJson = jest.spyOn(mockResponse, "json");

await handleRestEndpointException({ ...mockOptions, error: mockHttpError });
await handleRestEndpointException(ctx);

expect(spyStatus).toHaveBeenCalledWith(500);

expect(spyLogDebug).toHaveBeenCalledWith(
mockOptions.errorMsg,
errorAsSanitizedJson,
expect(spyLog).toHaveBeenCalledWith(
ctx.errorMsg,
safeStringifyException(rootError),
);

expect(spyJson).toHaveBeenCalledWith({
message: "InternalServerError",
error: errorAsSanitizedJson,
});
expect(spyJson).toHaveBeenCalledWith(
expect.objectContaining({
message: expect.stringMatching(
identifierByCodes[INTERNAL_SERVER_ERROR],
),
error: expect.stringContaining(reThrowErrorMsg),
}),
);

expect(spyJson).toHaveBeenCalledWith(
expect.objectContaining({
message: expect.stringMatching(
identifierByCodes[INTERNAL_SERVER_ERROR],
),
error: expect.stringContaining(rootCauseErrorMsg),
}),
);
});

it("should handle HttpError with statusCode < 500", async () => {
const mockResponse = createResponse();

const mockOptions: IHandleRestEndpointExceptionOptions = {
const rootErrorStr = "Test HTTP 404 error";
const mockHttpError = createHttpError(404, rootErrorStr, {
expose: true,
});

const ctx: IHandleRestEndpointExceptionOptions = {
errorMsg: "Test error message",
log: jest.mocked(log), // Provide a mock logger if needed
error: new Error("Test error"), // Provide appropriate error objects for testing
error: mockHttpError, // Provide appropriate error objects for testing
res: mockResponse,
};

const mockHttpError = createHttpError(404, "Test HTTP error", {
expose: true,
});

const errorAsSanitizedJson = safeStringifyException(mockHttpError);
const spyLogError = jest.spyOn(mockOptions.log, "error");
const spyLogError = jest.spyOn(ctx.log, "error");
const spyStatus = jest.spyOn(mockResponse, "status");
const spyJson = jest.spyOn(mockResponse, "json");
await handleRestEndpointException({ ...mockOptions, error: mockHttpError });

await handleRestEndpointException(ctx);

expect(spyStatus).toHaveBeenCalledWith(404);

expect(spyLogError).toHaveBeenCalledWith(
mockOptions.errorMsg,
ctx.errorMsg,
errorAsSanitizedJson,
);

Expand All @@ -89,30 +113,24 @@ describe("handleRestEndpointException", () => {

it("should handle non-HttpError", async () => {
const mockResponse = createResponse();

const mockError = new Error("An unexpected exception. Ha!");

const mockOptions: IHandleRestEndpointExceptionOptions = {
const ctx: IHandleRestEndpointExceptionOptions = {
errorMsg: "Test error message",
log: jest.mocked(log), // Provide a mock logger if needed
error: mockError,
res: mockResponse,
};

const mockErrorJson = safeStringifyException(mockError);
const spyLoggerFn = jest.spyOn(mockOptions.log, "error");
const spyLoggerFn = jest.spyOn(ctx.log, "error");
const spyStatus = jest.spyOn(mockResponse, "status");
const spyJson = jest.spyOn(mockResponse, "json");

await handleRestEndpointException({ ...mockOptions, error: mockError });
await handleRestEndpointException(ctx);

expect(spyStatus).toHaveBeenCalledWith(500);

expect(spyLoggerFn).toHaveBeenCalledWith(
mockOptions.errorMsg,
mockErrorJson,
);

expect(spyLoggerFn).toHaveBeenCalledWith(ctx.errorMsg, mockErrorJson);
expect(spyJson).toHaveBeenCalledOnce();

const mostRecentCall = spyJson.mock.lastCall;
Expand All @@ -131,26 +149,23 @@ describe("handleRestEndpointException", () => {
const dummyXssPayload = `<script>stealAndUploadPrivateKeys();</script>`;
const mockError = new Error(dummyXssPayload);

const mockOptions: IHandleRestEndpointExceptionOptions = {
const ctx: IHandleRestEndpointExceptionOptions = {
errorMsg: "Test error message",
log: jest.mocked(log), // Provide a mock logger if needed
error: mockError,
res: mockResponse,
};

const mockErrorJson = safeStringifyException(mockError);
const spyLoggerFn = jest.spyOn(mockOptions.log, "error");
const spyLoggerFn = jest.spyOn(ctx.log, "error");
const spyStatus = jest.spyOn(mockResponse, "status");
const spyJson = jest.spyOn(mockResponse, "json");

await handleRestEndpointException({ ...mockOptions, error: mockError });
await handleRestEndpointException(ctx);

expect(spyStatus).toHaveBeenCalledWith(500);

expect(spyLoggerFn).toHaveBeenCalledWith(
mockOptions.errorMsg,
mockErrorJson,
);
expect(spyLoggerFn).toHaveBeenCalledWith(ctx.errorMsg, mockErrorJson);

expect(spyJson).toHaveBeenCalledOnce();

Expand All @@ -173,27 +188,22 @@ describe("handleRestEndpointException", () => {
const dummyXssPayload = `<script>stealAndUploadPrivateKeys();</script>`;
const mockError = dummyXssPayload;

const mockOptions: IHandleRestEndpointExceptionOptions = {
const ctx: IHandleRestEndpointExceptionOptions = {
errorMsg: "Test error message",
log: jest.mocked(log), // Provide a mock logger if needed
error: mockError,
res: mockResponse,
};

const mockErrorJson = safeStringifyException(mockError);
const spyLoggerFn = jest.spyOn(mockOptions.log, "error");
const spyLoggerFn = jest.spyOn(ctx.log, "error");
const spyStatus = jest.spyOn(mockResponse, "status");
const spyJson = jest.spyOn(mockResponse, "json");

await handleRestEndpointException({ ...mockOptions, error: mockError });
await handleRestEndpointException(ctx);

expect(spyStatus).toHaveBeenCalledWith(500);

expect(spyLoggerFn).toHaveBeenCalledWith(
mockOptions.errorMsg,
mockErrorJson,
);

expect(spyLoggerFn).toHaveBeenCalledWith(ctx.errorMsg, mockErrorJson);
expect(spyJson).toHaveBeenCalledOnce();

const mostRecentCall = spyJson.mock.lastCall;
Expand Down

0 comments on commit d7c0806

Please sign in to comment.