test: enforce exact TechLog contract keys

This commit is contained in:
DongHyeonka
2026-08-15 18:16:21 +09:00
parent 01ed1e9300
commit 82f94423e5
2 changed files with 39 additions and 59 deletions
+13 -19
View File
@@ -5,29 +5,23 @@ import {
TECH_LOG_FEATURE_ID,
type TechLogFeatureInput,
} from "../../../src/features/tech-log/application/tech-log-feature-input.ts";
import type { PublicContentQueries } from "../../../src/features/tech-log/application/ports/public-content-queries.ts";
import type { StudioGateway } from "../../../src/features/tech-log/application/ports/studio-gateway.ts";
function acceptsApplicationFeatureInput(
input: ApplicationFeatureInputs["tech-log"],
): TechLogFeatureInput {
return input;
}
type Equal<Left, Right> =
(<Value>() => Value extends Left ? 1 : 2) extends
(<Value>() => Value extends Right ? 1 : 2)
? true
: false;
type Assert<Condition extends true> = Condition;
type TechLogFeatureInputExposesNoMissingOrAdditionalKeys = Assert<
Equal<keyof ApplicationFeatureInputs["tech-log"], "publicContent" | "createStudioGateway">
>;
type TechLogFeatureInputRegistryValueMatchesFeatureContract = Assert<
Equal<ApplicationFeatureInputs["tech-log"], TechLogFeatureInput>
>;
describe("TechLog feature input", () => {
it("uses the fixed application feature identifier", () => {
expect(TECH_LOG_FEATURE_ID).toBe("tech-log");
});
it("accepts public queries and a Studio gateway factory through the application registry", () => {
const publicContent = {} as PublicContentQueries;
const gateway = {} as StudioGateway;
const input = acceptsApplicationFeatureInput({
publicContent,
createStudioGateway: () => gateway,
});
expect(input.publicContent).toBe(publicContent);
expect(input.createStudioGateway()).toBe(gateway);
});
});
+26 -40
View File
@@ -9,6 +9,13 @@ import type {
StudioGateway,
} from "../../../src/features/tech-log/application/ports/studio-gateway.ts";
type Equal<Left, Right> =
(<Value>() => Value extends Left ? 1 : 2) extends
(<Value>() => Value extends Right ? 1 : 2)
? true
: false;
type Assert<Condition extends true> = Condition;
type Inline = components["schemas"]["Inline"];
type CaseRenderBlock = components["schemas"]["CaseRenderBlock"];
@@ -40,39 +47,24 @@ const blocks: CaseRenderBlock[] = [
{ type: "EVIDENCE_FIGURE", key: "fetch-plan", alt: "Fetch plan", caption: "Measured fetch plan", zoom: true },
];
const gatewayMethodNames = [
"getDashboard",
"listDocuments",
"createDocument",
"getDocument",
"saveDocument",
"validateDocument",
"createPreview",
"getCurrentPreview",
"publishDocument",
"unpublishPublication",
"listPublications",
"getPublicationSnapshot",
"getCatalog",
] as const;
type RequiredStudioGatewayOperation =
| "getDashboard"
| "listDocuments"
| "createDocument"
| "getDocument"
| "saveDocument"
| "validateDocument"
| "createPreview"
| "getCurrentPreview"
| "publishDocument"
| "unpublishPublication"
| "listPublications"
| "getPublicationSnapshot"
| "getCatalog";
function gatewayWithEveryRequiredMethod(): StudioGateway {
return {
getDashboard: async () => undefined as never,
listDocuments: async () => undefined as never,
createDocument: async () => undefined as never,
getDocument: async () => undefined as never,
saveDocument: async () => undefined as never,
validateDocument: async () => undefined as never,
createPreview: async () => undefined as never,
getCurrentPreview: async () => undefined as never,
publishDocument: async () => undefined as never,
unpublishPublication: async () => undefined as never,
listPublications: async () => undefined as never,
getPublicationSnapshot: async () => undefined as never,
getCatalog: async () => undefined as never,
};
}
type StudioGatewayExposesNoMissingOrAdditionalOperations = Assert<
Equal<keyof StudioGateway, RequiredStudioGatewayOperation>
>;
describe("TechLog Studio contracts", () => {
it("preserves every public wire discriminator exactly once", () => {
@@ -98,26 +90,20 @@ describe("TechLog Studio contracts", () => {
expect(new Set(blocks.map((block) => block.type)).size).toBe(blocks.length);
});
it("requires the exact Studio gateway operation set", () => {
expect(Object.keys(gatewayWithEveryRequiredMethod()).sort()).toEqual(
[...gatewayMethodNames].sort(),
);
});
it("keeps RFC 9457-style gateway failures inspectable at the application boundary", () => {
const error = new StudioGatewayError({
type: "https://techlog.dev/problems/conflict",
title: "Conflict",
status: 409,
detail: "The working copy has changed.",
code: "DOCUMENT_VERSION_CONFLICT",
code: "VERSION_CONFLICT",
retryable: false,
});
expect(error).toMatchObject({
name: "StudioGatewayError",
status: 409,
code: "DOCUMENT_VERSION_CONFLICT",
code: "VERSION_CONFLICT",
retryable: false,
});
expect(isStudioGatewayError(error)).toBe(true);