Files
tech-log-frontend/tests/features/tech-log/studio-contract-contribution.test.ts
T
DongHyeonkaandClaude Opus 5 35cc5c868a fix: invalidate the TechLog CSRF token on 403 and harden the bootstrap profile wiring
Item 1 (real bug): contractOperations.execute only invalidated the cached
CSRF token on UNAUTHENTICATED (401). A CSRF-specific rejection normally
arrives as FORBIDDEN (403) -- the platform classifies any 403 response as
FORBIDDEN unconditionally -- so a token rejected during an ordinary document
save left the stale token cached and every subsequent Studio mutation kept
failing until reload. Extracted invalidateTechLogCsrfOnOutcome() so
production and the composition test call the identical function; it now
invalidates on both UNAUTHENTICATED and FORBIDDEN.

Item 2: the upload transport's uncontracted-status fallback hardcoded status
503, so an uncontracted 401/403 body never reached the gateway's
error.status === 401 || 403 invalidation check. Passes the real
response.status through.

Item 3: safeOperation()'s auth-profile parameter is now typed as a union of
the two valid profile constants instead of a bare string, and
assertExactlyOneTechLogStudioBootstrapOperation() fails composition closed
if getStudioSession stops being the sole caller of the credential-free
bootstrap profile.

Item 4: corrected two stale operation counts in the adapter review doc.

Both new tests for items 1 and 2 were run and shown failing before their
fix, per this task's TDD standard for error-path changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 03:25:29 +09:00

106 lines
4.2 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "vitest";
import canonicalSource from "../../../src/features/tech-log/contracts/studio/canonical-source.json" with { type: "json" };
import {
TECH_LOG_STUDIO_CONTRIBUTION,
TECH_LOG_STUDIO_OPERATION_IDS,
} from "../../../src/features/tech-log/contracts/tech-log-studio-contract-contribution.ts";
import { composeContractContributions } from "../../../src/contracts/external-contract-runtime.ts";
import {
assertExactlyOneTechLogStudioBootstrapOperation,
TECH_LOG_STUDIO_BOOTSTRAP_AUTH_PROFILE_ID,
} from "../../../src/features/tech-log/adapters/http/studio-session-credentials.ts";
const UPLOAD = "uploadStudioAsset";
test("declares every canonical operation except the multipart upload", () => {
const expected = canonicalSource.operationIds.filter((id) => id !== UPLOAD);
assert.equal(expected.length, 18);
assert.deepEqual(
[...TECH_LOG_STUDIO_OPERATION_IDS].sort(),
[...expected].sort(),
);
});
test("pins the canonical digest and revision as external package provenance", () => {
const source = TECH_LOG_STUDIO_CONTRIBUTION.source;
assert.equal(source.kind, "EXTERNAL_PACKAGE");
if (source.kind !== "EXTERNAL_PACKAGE") return;
assert.equal(source.package.packageId, canonicalSource.packageId);
assert.equal(source.package.version, canonicalSource.version);
assert.equal(source.package.digest, canonicalSource.digest);
assert.equal(source.package.sourceRevision, canonicalSource.sourceRevision);
assert.equal(source.package.runtimeProtocolVersion, 1);
});
test("composes without violating the platform contract runtime", () => {
const composed = composeContractContributions([TECH_LOG_STUDIO_CONTRIBUTION]);
assert.equal(composed.externalPackages.length, 1);
});
test("every mutating operation replays by idempotency key and never auto-retries", () => {
for (const entry of TECH_LOG_STUDIO_CONTRIBUTION.http) {
if (entry.contract.retrySemantics !== "KEYED") continue;
assert.deepEqual(
entry.contract.commandRecovery,
{
mode: "IDEMPOTENCY_REPLAY",
operationIdentityField: "idempotencyKey",
},
`${entry.contract.operationId} recovery`,
);
assert.equal(
entry.frontend.retryBudget,
0,
`${entry.contract.operationId} budget`,
);
}
});
test("path templates match the canonical /api/v1/studio prefix", () => {
for (const entry of TECH_LOG_STUDIO_CONTRIBUTION.http) {
assert.ok(
entry.contract.pathTemplate.startsWith("/api/v1/studio/"),
`${entry.contract.operationId}: ${entry.contract.pathTemplate}`,
);
}
});
// Fix round 2, item 3. `getStudioSession` must be the sole operation on the
// credential-free bootstrap profile — anything else either reopens the C1
// cycle (zero operations) or lets a mutating operation dispatch with no CSRF
// header at all (a second operation on the bootstrap profile).
test("getStudioSession is the sole operation on the bootstrap auth profile", () => {
const bootstrapOperations = TECH_LOG_STUDIO_CONTRIBUTION.http.filter(
(entry) => entry.frontend.authProfileId === TECH_LOG_STUDIO_BOOTSTRAP_AUTH_PROFILE_ID,
);
assert.deepEqual(
bootstrapOperations.map((entry) => entry.contract.operationId),
["getStudioSession"],
);
});
test("assertExactlyOneTechLogStudioBootstrapOperation rejects zero or multiple bootstrap operations", () => {
assert.throws(
() => assertExactlyOneTechLogStudioBootstrapOperation([
{ operationId: "getStudioDashboard", authProfileId: "TECH_LOG_STUDIO_SESSION" },
]),
/must be used by exactly one operation/,
);
assert.throws(
() => assertExactlyOneTechLogStudioBootstrapOperation([
{ operationId: "getStudioSession", authProfileId: TECH_LOG_STUDIO_BOOTSTRAP_AUTH_PROFILE_ID },
{ operationId: "deleteStudioAsset", authProfileId: TECH_LOG_STUDIO_BOOTSTRAP_AUTH_PROFILE_ID },
]),
/must be used by exactly one operation/,
);
assert.doesNotThrow(() =>
assertExactlyOneTechLogStudioBootstrapOperation([
{ operationId: "getStudioSession", authProfileId: TECH_LOG_STUDIO_BOOTSTRAP_AUTH_PROFILE_ID },
{ operationId: "getStudioDashboard", authProfileId: "TECH_LOG_STUDIO_SESSION" },
]),
);
});