WFDASH-WEBAPP/src/lib/server/audit-context.test.ts
Dave 51bd4ec32c fix(quote-assistant): credit AI writes to the assistant, not the rep who clicked
AuditActor gains ignoreRequestUser: buildAuditHeaders then omits the
forwarded PB token (X-Audit-Auth) and request-derived email hint, so the
PB audit hook falls back to X-Audit-Actor-Name. fillQuote sets it for
all its writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 23:22:59 +02:00

90 lines
3.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { buildAuditHeaders, runWithAuditActor } from "./audit-context";
const reader = (h: Record<string, string>) => ({
get: (name: string) => h[name.toLowerCase()] ?? null,
});
describe("buildAuditHeaders", () => {
it("forwards the dashboard user's PB token from Authorization", () => {
const out = buildAuditHeaders(reader({ authorization: "pb-user-token" }), undefined, "agent-key");
expect(out).toMatchObject({ "X-Audit-Source": "web", "X-Audit-Auth": "pb-user-token" });
});
it("uses the fetch-wrapper header when Authorization is absent", () => {
const out = buildAuditHeaders(reader({ "x-wfdash-auth": "tok" }), undefined, "agent-key");
expect(out["X-Audit-Auth"]).toBe("tok");
});
it("never forwards the agent API key as a user token", () => {
const out = buildAuditHeaders(reader({ authorization: "Bearer agent-key" }), undefined, "agent-key");
expect(out["X-Audit-Source"]).toBe("agent");
expect(out["X-Audit-Auth"]).toBeUndefined();
});
it("prefers the wrapper token over the agent key for agent-authed browser calls", () => {
const out = buildAuditHeaders(
reader({ authorization: "Bearer agent-key", "x-wfdash-auth": "tok" }),
undefined,
"agent-key",
);
expect(out).toMatchObject({ "X-Audit-Source": "agent", "X-Audit-Auth": "tok" });
});
it("carries an explicit actor, URI-encoding non-ASCII names", () => {
const out = buildAuditHeaders(null, { id: "u1", name: "Zoë Naidoo", source: "mcp" }, undefined);
expect(out).toMatchObject({
"X-Audit-Source": "mcp",
"X-Audit-Actor-Id": "u1",
"X-Audit-Actor-Name": "Zo%C3%AB%20Naidoo",
});
});
it("reads the dev-identity cookie as an email hint but never invents one", () => {
expect(
buildAuditHeaders(reader({ cookie: "a=1; wfdash_dev_email=sam%40wf.co.za" }), undefined, undefined)[
"X-Audit-Actor-Email"
],
).toBe("sam%40wf.co.za");
expect(buildAuditHeaders(reader({}), undefined, undefined)["X-Audit-Actor-Email"]).toBeUndefined();
});
it("omits the request user's token and email hint when the actor opts out (ignoreRequestUser)", () => {
const incoming = reader({
authorization: "Bearer pb-user-token",
"x-wfdash-auth": "tok",
cookie: "wfdash_dev_email=sam%40wf.co.za",
});
const out = buildAuditHeaders(
incoming,
{ name: "AI quote assistant (run by Sam)", source: "agent", ignoreRequestUser: true },
"agent-key",
);
expect(out["X-Audit-Auth"]).toBeUndefined();
expect(out["X-Audit-Actor-Email"]).toBeUndefined();
expect(out).toMatchObject({
"X-Audit-Source": "agent",
"X-Audit-Actor-Name": encodeURIComponent("AI quote assistant (run by Sam)"),
});
});
it("still forwards the request user's token when the actor does not opt out", () => {
const out = buildAuditHeaders(
reader({ authorization: "Bearer pb-user-token" }),
{ name: "Someone", source: "mcp" },
"agent-key",
);
expect(out["X-Audit-Auth"]).toBe("pb-user-token");
});
it("falls back to source=server outside a request", () => {
expect(buildAuditHeaders(null, undefined, undefined)).toEqual({ "X-Audit-Source": "server" });
});
});
describe("runWithAuditActor", () => {
it("returns the callback's value", async () => {
await expect(runWithAuditActor({ source: "mcp" }, async () => 42)).resolves.toBe(42);
});
});