Files
HealthLog/src/lib/__tests__/api-handler-safe-request-prop.test.ts
T
Marc-André Bombeck 17b8d8d2c2 chore(release): v1.4.25
Largest feature delta in the v1.4.x line. Insights expands into seven dedicated metric sub-pages; GLP-1 medication tracking lands end-to-end (drug-level chart with Research Mode acknowledgment, EMA titration ladder, cadence + compliance, side-effect taxonomy, pen/vial inventory); cross-source priority becomes a two-axis resolver; per-user timezone threads through ten analytics surfaces; Withings activity + sleep v2 sync; onboarding rebuilt as a nested-route wizard; Personal Records ship end-to-end; Health Score provenance accordion; Coach runs native first-party prompts across six locales with a 1800-assertion refusal-probe matrix; OpenAPI drift gate hard-fails; multi-arch GHCR image. Migrations 0043 through 0060 are additive and forward-only. PROMPT_VERSION 4.24.0 → 4.25.0 with GROUND RULE 9 (refuses dose recommendations) and GROUND RULE 15 (refuses drug-level estimates with MDR + MDCG 2021-24 cites).

See CHANGELOG.md for the full per-section delta.
2026-05-14 23:49:32 +02:00

165 lines
4.5 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
// --- Mocks must be hoisted before importing the module under test. ---
vi.mock("@/lib/db", () => ({
prisma: {},
}));
vi.mock("@/lib/auth/session", () => ({
getSession: vi.fn(),
}));
vi.mock("@/lib/auth/hmac", () => ({
hashToken: vi.fn(),
}));
vi.mock("@/lib/auth/audit", () => ({
auditLog: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("@/lib/logging/transports", () => ({
emitIfSampled: vi.fn(),
}));
vi.mock("next/headers", () => ({
headers: vi.fn(async () => ({ get: () => null })),
cookies: vi.fn(async () => ({
get: () => undefined,
set: () => {},
delete: () => {},
})),
}));
import { __testables } from "@/lib/api-handler";
const { safeRequestProp, isTolerableRequestProbeError } = __testables;
describe("isTolerableRequestProbeError — narrow-catch classifier", () => {
it("returns true for the V8 private-field TypeError", () => {
const err = new TypeError(
"Cannot read private member #state from an object whose class did not declare it",
);
expect(isTolerableRequestProbeError(err)).toBe(true);
});
it("returns true for the alternative 'private field' wording", () => {
const err = new TypeError(
"Cannot read private field #foo from object that was not created from its class",
);
expect(isTolerableRequestProbeError(err)).toBe(true);
});
it("returns true for the Bun / older-V8 'private name' wording", () => {
const err = new TypeError("Cannot read private name #bar from object");
expect(isTolerableRequestProbeError(err)).toBe(true);
});
it("returns true when the request handed in is undefined or null", () => {
expect(
isTolerableRequestProbeError(
new TypeError("Cannot read properties of undefined (reading 'url')"),
),
).toBe(true);
expect(
isTolerableRequestProbeError(
new TypeError("Cannot read properties of null (reading 'method')"),
),
).toBe(true);
expect(
isTolerableRequestProbeError(
new TypeError("Cannot read property 'headers' of undefined"),
),
).toBe(true);
});
it("returns false for any other TypeError (real bug, must surface)", () => {
expect(
isTolerableRequestProbeError(
new TypeError("undefined is not a function"),
),
).toBe(false);
expect(
isTolerableRequestProbeError(
new TypeError("Cannot read properties of {} (reading 'parse')"),
),
).toBe(false);
});
it("returns false for non-TypeError exceptions", () => {
expect(isTolerableRequestProbeError(new Error("private member #x"))).toBe(
false,
);
expect(
isTolerableRequestProbeError(new RangeError("private member")),
).toBe(false);
expect(isTolerableRequestProbeError("private member string")).toBe(false);
expect(isTolerableRequestProbeError(null)).toBe(false);
expect(isTolerableRequestProbeError(undefined)).toBe(false);
});
});
describe("safeRequestProp — narrow-catch behaviour", () => {
it("returns the read result when the request is a real NextRequest-shape", () => {
const req = { method: "POST" };
const result = safeRequestProp(req, (r) => r.method, "GET");
expect(result).toBe("POST");
});
it("returns the fallback when the read raises the private-field TypeError", () => {
const result = safeRequestProp(
{},
() => {
throw new TypeError(
"Cannot read private member #state from an object whose class did not declare it",
);
},
"FALLBACK",
);
expect(result).toBe("FALLBACK");
});
it("re-throws any other error so real bugs surface", () => {
expect(() =>
safeRequestProp(
{},
() => {
throw new Error("downstream parser exploded");
},
"FALLBACK",
),
).toThrow("downstream parser exploded");
expect(() =>
safeRequestProp(
{},
() => {
throw new TypeError("undefined is not a function");
},
"FALLBACK",
),
).toThrow("undefined is not a function");
expect(() =>
safeRequestProp(
{},
() => {
throw new RangeError("out of range");
},
"FALLBACK",
),
).toThrow("out of range");
});
it("supports null fallbacks (header lookups return string | null)", () => {
const result = safeRequestProp<string | null>(
{},
() => {
throw new TypeError("Cannot read private member #headers from object");
},
null,
);
expect(result).toBeNull();
});
});