UNPKG

@node-idempotency/fastify

Version:

fastify plugin that provides Race-Condition free idempotency for HTTP requests, preventing unintended duplicate operations.

133 lines 6.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const request = require("supertest"); const test_module_1 = require("./modules/test/test.module"); const shared_1 = require("@node-idempotency/shared"); const idempotencyKey = "Idempotency-Key"; const IDEMPOTENCY_REPLAYED_HEADER = shared_1.HTTPHeaderEnum.idempotentReplayed; describe("Node-Idempotency", () => { beforeAll(async () => { await test_module_1.default.ready(); }); afterAll(async () => { await test_module_1.default.close(); }); it("should return a cached response when key is reused", async () => { const res1 = await request(test_module_1.default.server) .get("/") .set({ [idempotencyKey]: "1" }) .expect(200, "0"); const res2 = await request(test_module_1.default.server) .get("/") .set({ [idempotencyKey]: "1" }) .expect(200, "0"); expect(res1.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); expect(res2.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toEqual("true"); }); it("should return a cached response when key is reused for json endpoint", async () => { const res1 = await request(test_module_1.default.server) .get("/json") .set({ [idempotencyKey]: "1" }) .expect(200, { jsonCounter: 1 }); const res2 = await request(test_module_1.default.server) .get("/json") .set({ [idempotencyKey]: "1" }) .expect(200, { jsonCounter: 1 }); expect(res1.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); expect(res2.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toEqual("true"); }); it("should return a cached response when key is reused with request body", async () => { const res1 = await request(test_module_1.default.server) .post("/") .set({ [idempotencyKey]: "4" }) .send({ number: 2 }) .expect(201, "2"); const res2 = await request(test_module_1.default.server) .post("/") .set({ [idempotencyKey]: "4" }) .send({ number: 2 }) .expect(201, "2"); expect(res1.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); expect(res2.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toEqual("true"); }); it("should return a cached error when key is reused", async () => { const res1 = await request(test_module_1.default.server) .get("/error") .set({ [idempotencyKey]: "5" }) .expect(500, { statusCode: 500, error: "Internal Server Error", message: "unknown", }); const res2 = await request(test_module_1.default.server) .get("/error") .set({ [idempotencyKey]: "5" }) .expect(500, { statusCode: 500, error: "Internal Server Error", message: "unknown", }); expect(res1.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); expect(res2.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toEqual("true"); }); it("should return 400 when idempotency key is not sent when its needed", async () => { await request(test_module_1.default.server).get("/").expect(400); }); it("should return 400 when idempotency key exceeds length", async () => { await request(test_module_1.default.server) .get("/") .set({ [idempotencyKey]: "2345678" }) .expect(400); }); it("should return a conflict when parallel requests are made", async () => { const [res1, res2] = await Promise.all([ request(test_module_1.default.server) .get("/slow") .set({ [idempotencyKey]: "2" }) .then((res) => res) .catch((err) => err), request(test_module_1.default.server) .get("/slow") .set({ [idempotencyKey]: "2" }) .then((res) => res) .catch((err) => err), ]); const success = res1.status === 200 ? res1.status : res2.status; const conflict = res1.status !== 200 ? res1.status : res2.status; const successBody = res1.status === 200 ? res1.text : res2.text; const conflictBody = res1.status !== 200 ? res1.body : res2.body; const conflictHeader = res1.status !== 200 ? res1.headers : res2.headers; expect(success).not.toBe(conflict); expect(success).toBe(200); expect(conflict).toBe(409); expect(successBody).toEqual("0"); expect(conflictBody).toEqual({ code: "REQUEST_IN_PROGRESS", error: "Conflict", message: "A request is outstanding for this Idempotency-Key", statusCode: 409, }); expect(conflictHeader[shared_1.HTTPHeaderEnum.retryAfter.toLowerCase()]).toEqual("1"); }); it("should return error when different body is used for the same key", async () => { const res1 = await request(test_module_1.default.server) .post("/") .set({ [idempotencyKey]: "3" }) .send({ number: 1 }) .expect(201, "3"); const res2 = await request(test_module_1.default.server) .post("/") .set({ [idempotencyKey]: "3" }) .send({ number: 2 }) .expect(422); expect(JSON.parse(res2.text)).toEqual({ code: "IDEMPOTENCY_FINGERPRINT_MISSMATCH", error: "Unprocessable Entity", message: "Idempotency-Key is already used", statusCode: 422, }); expect(res1.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); expect(res2.headers[IDEMPOTENCY_REPLAYED_HEADER.toLowerCase()]).toBeUndefined(); }); }); //# sourceMappingURL=idempotency.spec.js.map