/**
* 相関IDの取得規則(ステップ9 の共通ヘルパー)。
* proxy.ts(ステップ7)が保留中でも request_id が欠けないことを固定する。
*/
import { describe, expect, test } from "vitest";
import {
getRequestId,
normalizeRequestId,
REQUEST_ID_HEADER,
runWithRequestId,
withRequestId,
} from "@/lib/logger/context";
function requestWith(headers: Record<string, string> = {}): Request {
return new Request("http://localhost/api/clean-logs", { headers });
}
describe("withRequestId", () => {
test("x-request-id ヘッダーがあればそれを使う", () => {
const request = requestWith({ [REQUEST_ID_HEADER]: "from-proxy-0001" });
const seen = withRequestId(request, () => getRequestId());
expect(seen).toBe("from-proxy-0001");
});
test("ヘッダーが無ければ生成する(UUID 形式)", () => {
const seen = withRequestId(requestWith(), () => getRequestId());
expect(seen).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
);
});
test("リクエストごとに別のIDになる", () => {
const a = withRequestId(requestWith(), () => getRequestId());
const b = withRequestId(requestWith(), () => getRequestId());
expect(a).not.toBe(b);
});
test("形が不正なヘッダー値は使わずに生成する(改行・長すぎる値)", () => {
const injected = withRequestId(
requestWith({ [REQUEST_ID_HEADER]: "abc def" }),
() => getRequestId()
);
expect(injected).not.toBe("abc def");
const tooLong = withRequestId(
requestWith({ [REQUEST_ID_HEADER]: "a".repeat(129) }),
() => getRequestId()
);
expect(tooLong).not.toBe("a".repeat(129));
});
test("空文字のヘッダー値は生成にフォールバックする", () => {
const seen = withRequestId(
requestWith({ [REQUEST_ID_HEADER]: "" }),
() => getRequestId()
);
expect(seen).toBeTruthy();
expect(seen).not.toBe("");
});
test("非同期の内側でも同じIDが読める", async () => {
const request = requestWith({ [REQUEST_ID_HEADER]: "async-0002" });
const seen = await withRequestId(request, async () => {
await new Promise((resolve) => setTimeout(resolve, 1));
return getRequestId();
});
expect(seen).toBe("async-0002");
});
test("ラップの外ではIDが残らない", () => {
withRequestId(requestWith(), () => getRequestId());
expect(getRequestId()).toBeUndefined();
});
test("runWithRequestId は明示指定した値をそのまま使う", () => {
expect(runWithRequestId("explicit-0003", () => getRequestId())).toBe(
"explicit-0003"
);
});
});
/**
* onRequestError() はルートの AsyncLocalStorage の外で走るため、
* ヘッダから同じ規則でIDを取り直す。その規則をここで固定する。
*/
describe("normalizeRequestId", () => {
test("妥当な値はそのまま返す", () => {
expect(normalizeRequestId("from-proxy-0001")).toBe("from-proxy-0001");
});
test("Node 形式の配列ヘッダは先頭を見る", () => {
expect(normalizeRequestId(["first-0001", "second-0002"])).toBe(
"first-0001"
);
});
test("未設定・空・不正な形は undefined(偽の相関を作らない)", () => {
expect(normalizeRequestId(undefined)).toBeUndefined();
expect(normalizeRequestId(null)).toBeUndefined();
expect(normalizeRequestId("")).toBeUndefined();
expect(normalizeRequestId("has space")).toBeUndefined();
expect(normalizeRequestId("line\nbreak")).toBeUndefined();
expect(normalizeRequestId("a".repeat(129))).toBeUndefined();
});
});