src/test-routes/http-responses.ts (346 lines of code) (raw):

import { app, HttpMethod, json, ValidationError } from "../api"; import { MockRequest } from "../api/mock-request"; import { MockResponse } from "../api/mock-response"; interface Data { [code: number]: HttpMethod[]; } function capitalize(value: string) { return value.charAt(0).toUpperCase() + value.slice(1); } app.category("vanilla", () => { function registerScenarios( basePath: string, name: string, scenarios: Data, handler: (config: { method: HttpMethod; code: number }, req: MockRequest) => MockResponse | Promise<MockResponse>, ) { for (const [code, methods] of Object.entries(scenarios)) { for (const method of methods as HttpMethod[]) { const coverageName = `Http${capitalize(name)}${code}${capitalize(method)}`; app[method](`/http${basePath}/${code}`, coverageName, (req) => { return handler({ code: Number(code), method }, req); }); } } } registerScenarios( "/success", "Success", { 200: ["head", "get", "put", "post", "patch", "delete"], 201: ["put", "post"], 202: ["put", "post", "patch", "delete"], 204: ["head", "put", "post", "patch", "delete"], 404: ["head"], }, ({ method, code }) => { const body = method === "get" ? json(true) : undefined; return { status: Number(code), body, testSuccessful: true }; }, ); // Use in redirection app.router.all("/http/success/:method/:code", (req, res, next) => { if (req.method.toLowerCase() === req.params.method) { res.status(JSON.parse(req.params.code)).end(); } else { res.send(400); } }); registerScenarios( "/redirect", "Redirect", { 300: ["head", "get"], 301: ["head", "put", "get"], 302: ["head", "get", "patch"], 303: ["post"], 307: ["head", "get", "put", "post", "patch", "delete", "options"], }, ({ code, method }) => { let location; if ((code === 301 || code === 302) && method !== "head" && method !== "get") { location = "/http/failure/500"; } else if (code === 303) { location = "/http/success/get/200"; } else { location = `/http/success/${method}/200`; } const body = method === "get" && code === 300 ? json(["/http/success/get/200"]) : undefined; return { testSuccessful: true, status: code, headers: { Location: location, }, body, }; }, ); registerScenarios( "/failure/client", "ClientFailure", { 400: ["head", "get", "options", "put", "post", "patch", "delete"], 401: ["head"], 402: ["get"], 403: ["get", "options"], 404: ["put"], 405: ["patch"], 406: ["post"], 407: ["delete"], 409: ["put"], 410: ["head"], 411: ["get"], 412: ["get", "options"], 413: ["put"], 414: ["patch"], 415: ["post"], 416: ["get"], 417: ["delete"], 429: ["head"], }, ({ code, method }) => { return { status: code, testSuccessful: true, }; }, ); registerScenarios( "/failure/server", "ServerFailure", { 501: ["head", "get"], 505: ["post", "delete"], }, ({ code, method }) => { return { status: code, testSuccessful: true, }; }, ); registerScenarios( "/retry", "Retry", { 408: ["head"], 502: ["get", "options"], 500: ["put", "patch"], 503: ["post", "delete"], 504: ["put", "patch"], }, ({ code, method }, req) => { if (isRetryRequest(req, code, method)) { removeRetryTracker(req); return { status: 200, }; } else { addRetryTracker(req, code, method); return { status: code, }; } }, ); //#region Multiple responses described as 204 app.get( "/http/payloads/200/A/204/none/default/Error/response/200/valid", "ResponsesScenarioA200MatchingModel", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }, ); app.get( "/http/payloads/200/A/204/none/default/Error/response/201/valid", "ResponsesScenarioA201DefaultNoModel", (req) => { return { status: 201, body: json({ statusCode: "201" }) }; }, ); app.get( "/http/payloads/200/A/204/none/default/Error/response/202/none", "ResponsesScenarioA202DefaultNoModel", (req) => { return { status: 202, body: json({ statusCode: "202" }) }; }, ); app.get( "/http/payloads/200/A/204/none/default/Error/response/204/none", "ResponsesScenarioA204MatchingNoModel", (req) => { return { status: 204 }; }, ); app.get( "/http/payloads/200/A/204/none/default/Error/response/400/valid", "ResponsesScenarioA400DefaultModel", (req) => { return { status: 400, testSuccessful: true, body: json({ message: "client error" }) }; }, ); // #endregion //#region Endpoint with response for 200, 201 default app.get( "/http/payloads/200/A/201/B/default/Error/response/200/valid", "ResponsesScenarioB200MatchingModel", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }, ); app.get( "/http/payloads/200/A/201/B/default/Error/response/201/valid", "ResponsesScenarioB201MatchingModel", (req) => { return { status: 201, body: json({ statusCode: "201", textStatusCode: "Created" }) }; }, ); app.get("/http/payloads/200/A/201/B/default/Error/response/400/valid", "ResponsesScenarioB400DefaultModel", (req) => { return { status: 400, testSuccessful: true, body: json({ message: "client error" }) }; }); // #endregion //#region Endpoint with response for 200, 201, 404 and default app.get( "/http/payloads/200/A/201/C/404/D/default/Error/response/200/valid", "ResponsesScenarioC200MatchingModel", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }, ); app.get( "/http/payloads/200/A/201/C/404/D/default/Error/response/201/valid", "ResponsesScenarioC201MatchingModel", (req) => { return { status: 201, body: json({ httpCode: "201" }) }; }, ); app.get( "/http/payloads/200/A/201/C/404/D/default/Error/response/404/valid", "ResponsesScenarioC404MatchingModel", (req) => { return { status: 404, testSuccessful: true, body: json({ httpStatusCode: "404" }) }; }, ); app.get( "/http/payloads/200/A/201/C/404/D/default/Error/response/400/valid", "ResponsesScenarioC400DefaultModel", (req) => { return { status: 400, testSuccessful: true, body: json({ message: "client error" }) }; }, ); // #endregion //#region Endpoint with response for 202, 204 where it is returning empty body. app.get( "/http/payloads/202/none/204/none/default/Error/response/202/none", "ResponsesScenarioD202MatchingNoModel", (req) => { return { status: 202 }; }, ); app.get( "/http/payloads/202/none/204/none/default/Error/response/204/none", "ResponsesScenarioD204MatchingNoModel", (req) => { return { status: 204 }; }, ); app.get( "/http/payloads/202/none/204/none/default/Error/response/400/valid", "ResponsesScenarioD400DefaultModel", (req) => { return { status: 400, testSuccessful: true, body: json({ message: "client error" }) }; }, ); // #endregion //#region Endpoint with response for 202, 204, 400 returning invalid payloads app.get( "/http/payloads/202/none/204/none/default/none/response/202/invalid", "ResponsesScenarioE202MatchingInvalid", (req) => { return { status: 202, body: json({ property: "value" }) }; }, ); app.get( "/http/payloads/202/none/204/none/default/none/response/204/none", "ResponsesScenarioE204MatchingNoModel", (req) => { return { status: 204 }; }, ); app.get( "/http/payloads/202/none/204/none/default/none/response/400/none", "ResponsesScenarioE400DefaultNoModel", (req) => { return { status: 400, testSuccessful: true }; }, ); app.get( "/http/payloads/202/none/204/none/default/none/response/400/invalid", "ResponsesScenarioE400DefaultInvalid", (req) => { return { status: 400, testSuccessful: true, body: json({ property: "value" }) }; }, ); // #endregion //#region Endpoint with response for 202, 204, 400 returning valid payloads app.get("/http/payloads/default/a/response/200/valid", "ResponsesScenarioF200DefaultModel", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/default/a/response/200/none", "ResponsesScenarioF200DefaultNone", (req) => { return { status: 200 }; }); app.get("/http/payloads/default/a/response/400/valid", "ResponsesScenarioF400DefaultModel", (req) => { return { status: 400, testSuccessful: true }; }); app.get("/http/payloads/default/a/response/400/none", "ResponsesScenarioF400DefaultNone", (req) => { return { status: 400, testSuccessful: true, body: json({ property: "value" }) }; }); // #endregion //#region Endpoint with response for 202, 204, 400 returning invalid payloads app.get("/http/payloads/default/none/response/200/invalid", "ResponsesScenarioG200DefaultInvalid", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/default/none/response/200/none", "ResponsesScenarioG200DefaultNoModel", (req) => { return { status: 200 }; }); app.get("/http/payloads/default/none/response/400/invalid", "ResponsesScenarioG400DefaultInvalid", (req) => { return { status: 400, testSuccessful: true, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/default/none/response/400/none", "ResponsesScenarioG400DefaultNoModel", (req) => { return { status: 400, testSuccessful: true }; }); // #endregion //#region Endpoint with response for 202, 204, 400 returning invalid payloads app.get("/http/payloads/200/a/response/200/none", "ResponsesScenarioH200MatchingNone", (req) => { return { status: 200 }; }); app.get("/http/payloads/200/a/response/200/valid", "ResponsesScenarioH200MatchingModel", (req) => { return { status: 200, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/200/a/response/200/invalid", "ResponsesScenarioH200MatchingInvalid", (req) => { return { status: 200, body: json({ statusCodeInvalid: "200" }) }; }); app.get("/http/payloads/200/a/response/400/none", "ResponsesScenarioH400NonMatchingNone", (req) => { return { status: 400, testSuccessful: true, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/200/a/response/400/valid", "ResponsesScenarioH400NonMatchingModel", (req) => { return { status: 400, testSuccessful: true, body: json({ statusCode: "200" }) }; }); app.get("/http/payloads/200/a/response/400/invalid", "ResponsesScenarioH400NonMatchingInvalid", (req) => { return { status: 400, testSuccessful: true, body: json({ statusCodeInvalid: "200" }) }; }); app.get("/http/payloads/200/a/response/202/valid", "ResponsesScenarioH202NonMatchingModel", (req) => { return { status: 202, testSuccessful: true, body: json({ statusCode: "200" }) }; }); // #endregion app.get("/http/failure/emptybody/error", "ResponsesScenarioEmptyErrorBody", () => { return { status: 400, testSuccessful: true, body: json({ message: "" }) }; }); app.get("/http/failure/nomodel/error", "ResponsesScenarioNoModelErrorBody", () => { return { status: 400, testSuccessful: true, body: json({ message: "NoErrorModel" }) }; }); app.get("/http/failure/nomodel/empty", "ResponsesScenarioNoModelEmptyBody", () => { return { status: 400, testSuccessful: true }; }); }); function isRetryRequest(req: MockRequest, code: number, method: HttpMethod) { const cookies = req.headers["cookie"]; let cookieMatch; if (cookies) { cookieMatch = /tries=(\w+)/.exec(cookies); if (cookieMatch && cookieMatch[1] && cookieMatch[1] === code + "_" + method) { return true; } } return false; } function addRetryTracker(req: MockRequest, code: number, method: HttpMethod) { req.originalRequest.res?.cookie("tries", code + "_" + method, { maxAge: 900000 }); } function removeRetryTracker(req: MockRequest) { req.originalRequest.res?.clearCookie("tries"); }