import type { NextFunction, Request, Response } from "express";
import { describe, expect, it, vi } from "vitest";
import { requestIdMiddleware } from "./request-id.js";
/**
* Mock Express Request object
*/
function createMockRequest(): Request {
return {} as Request;
}
/**
* Mock Express Response object with setHeader spy
*/
function createMockResponse(): Response & { setHeader: ReturnType<typeof vi.fn> } {
return {
setHeader: vi.fn(),
} as unknown as Response & { setHeader: ReturnType<typeof vi.fn> };
}
/**
* Mock Express NextFunction
*/
function createMockNext(): NextFunction {
return vi.fn() as unknown as NextFunction;
}
describe("requestIdMiddleware", () => {
it("should generate UUID v4 format", () => {
const req = createMockRequest();
const res = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req, res, next);
// UUID v4 format: 8-4-4-4-12 hexadecimal characters
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
expect(req.requestId).toBeDefined();
expect(req.requestId).toMatch(uuidRegex);
});
it("should attach requestId to req object", () => {
const req = createMockRequest();
const res = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req, res, next);
expect(req.requestId).toBeDefined();
expect(typeof req.requestId).toBe("string");
expect(req.requestId?.length).toBeGreaterThan(0);
});
it("should set X-Request-ID header on response", () => {
const req = createMockRequest();
const res = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req, res, next);
expect(res.setHeader).toHaveBeenCalledWith("X-Request-ID", req.requestId);
expect(res.setHeader).toHaveBeenCalledTimes(1);
});
it("should call next() to continue middleware chain", () => {
const req = createMockRequest();
const res = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req, res, next);
expect(next).toHaveBeenCalledTimes(1);
expect(next).toHaveBeenCalledWith();
});
it("should generate unique IDs for multiple requests", () => {
const req1 = createMockRequest();
const req2 = createMockRequest();
const req3 = createMockRequest();
const res1 = createMockResponse();
const res2 = createMockResponse();
const res3 = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req1, res1, next);
requestIdMiddleware(req2, res2, next);
requestIdMiddleware(req3, res3, next);
expect(req1.requestId).not.toBe(req2.requestId);
expect(req2.requestId).not.toBe(req3.requestId);
expect(req1.requestId).not.toBe(req3.requestId);
});
it("should generate requestId even if Request object is minimal", () => {
// Test defensive programming - minimal Request object
const req = {} as Request;
const res = createMockResponse();
const next = createMockNext();
requestIdMiddleware(req, res, next);
expect(req.requestId).toBeDefined();
expect(typeof req.requestId).toBe("string");
});
});