test: cover Studio error mapping's remaining outcome branches and CSRF rejection recovery

This commit is contained in:
DongHyeonka
2026-08-18 00:51:50 +09:00
parent 7381be1477
commit 6c40c291d9
@@ -58,6 +58,99 @@ test("maps UNAUTHENTICATED onto AUTHENTICATION_REQUIRED", () => {
assert.equal(error.status, 401); assert.equal(error.status, 401);
}); });
test("falls back to STUDIO_UNAVAILABLE and drops an uncontracted problem code", () => {
const error = toStudioGatewayError(
{
kind: "PROBLEM",
problem: {
type: "https://techlog.local/problems/bogus",
title: "BOGUS",
status: 418,
detail: "server sent a code outside the contract",
code: "TOTALLY_MADE_UP_CODE",
},
metadata: { status: 418 },
effect: "NOT_APPLIED",
} as never,
"saveStudioDocument",
);
assert.equal(error.code, "STUDIO_UNAVAILABLE");
assert.equal(error.status, 418);
// The unrecognised code must not survive into the resulting error anywhere,
// not just be absent from `.code` — otherwise a caller reading `.problem`
// could still observe it.
assert.ok(
!JSON.stringify(error.problem).includes("TOTALLY_MADE_UP_CODE"),
"the bogus code leaked into the synthesized problem",
);
});
test("maps FORBIDDEN onto STUDIO_ACCESS_DENIED", () => {
const error = toStudioGatewayError(
{ kind: "FORBIDDEN", effect: "NOT_APPLIED" } as never,
"getStudioDashboard",
);
assert.equal(error.code, "STUDIO_ACCESS_DENIED");
assert.equal(error.status, 403);
assert.equal(error.retryable, false);
});
test("maps RATE_LIMITED onto a retryable STUDIO_UNAVAILABLE", () => {
const error = toStudioGatewayError(
{ kind: "RATE_LIMITED", effect: "NOT_APPLIED" } as never,
"getStudioDashboard",
);
assert.equal(error.code, "STUDIO_UNAVAILABLE");
assert.equal(error.status, 429);
assert.equal(error.retryable, true);
});
test("maps CANCELLED onto a non-retryable STUDIO_UNAVAILABLE", () => {
const error = toStudioGatewayError(
{ kind: "CANCELLED", effect: "NOT_STARTED" } as never,
"getStudioDashboard",
);
assert.equal(error.code, "STUDIO_UNAVAILABLE");
assert.equal(error.status, 499);
assert.equal(error.retryable, false);
});
test("maps CONTRACT_VIOLATION and AUTH_INTEGRATION_FAILURE onto the same non-retryable STUDIO_UNAVAILABLE", () => {
const contractViolation = toStudioGatewayError(
{
kind: "CONTRACT_VIOLATION",
violation: { kind: "UNEXPECTED_STATUS", operation: "RESPONSE" },
effect: "NOT_APPLICABLE",
} as never,
"getStudioDashboard",
);
const authIntegrationFailure = toStudioGatewayError(
{ kind: "AUTH_INTEGRATION_FAILURE", reason: "UNKNOWN_AUTH_PROFILE", effect: "NOT_APPLICABLE" } as never,
"getStudioDashboard",
);
for (const error of [contractViolation, authIntegrationFailure]) {
assert.equal(error.code, "STUDIO_UNAVAILABLE");
assert.equal(error.status, 502);
assert.equal(error.retryable, false);
}
});
test("throws for a SUCCESS outcome instead of returning a fabricated error", () => {
assert.throws(
() =>
toStudioGatewayError(
{ kind: "SUCCESS", value: undefined, metadata: { status: 200 }, effect: "NOT_APPLICABLE" } as never,
"getStudioDashboard",
),
/success outcome is not an error/,
);
});
test("fetches the CSRF token once and reuses it until invalidated", async () => { test("fetches the CSRF token once and reuses it until invalidated", async () => {
let calls = 0; let calls = 0;
const provider = createCsrfTokenProvider({ const provider = createCsrfTokenProvider({
@@ -89,3 +182,34 @@ test("does not stampede concurrent CSRF requests", async () => {
await Promise.all([provider.token(), provider.token(), provider.token()]); await Promise.all([provider.token(), provider.token(), provider.token()]);
assert.equal(calls, 1); assert.equal(calls, 1);
}); });
test("resolves the header name from the same cached snapshot as the token", async () => {
let calls = 0;
const provider = createCsrfTokenProvider({
async execute() {
calls += 1;
return { csrfToken: "token", csrfHeaderName: "X-CSRF-TOKEN" };
},
});
assert.equal(await provider.headerName(), "X-CSRF-TOKEN");
assert.equal(await provider.token(), "token");
assert.equal(calls, 1);
});
test("does not cache a rejected CSRF fetch and retries cleanly on the next call", async () => {
let calls = 0;
const provider = createCsrfTokenProvider({
async execute() {
calls += 1;
if (calls === 1) throw new Error("network failure");
return { csrfToken: "token", csrfHeaderName: "X-CSRF-TOKEN" };
},
});
await assert.rejects(provider.token());
assert.equal(calls, 1);
assert.equal(await provider.token(), "token");
assert.equal(calls, 2);
});