test: enforce exact TechLog contract keys
This commit is contained in:
@@ -5,29 +5,23 @@ import {
|
|||||||
TECH_LOG_FEATURE_ID,
|
TECH_LOG_FEATURE_ID,
|
||||||
type TechLogFeatureInput,
|
type TechLogFeatureInput,
|
||||||
} from "../../../src/features/tech-log/application/tech-log-feature-input.ts";
|
} 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(
|
type Equal<Left, Right> =
|
||||||
input: ApplicationFeatureInputs["tech-log"],
|
(<Value>() => Value extends Left ? 1 : 2) extends
|
||||||
): TechLogFeatureInput {
|
(<Value>() => Value extends Right ? 1 : 2)
|
||||||
return input;
|
? 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", () => {
|
describe("TechLog feature input", () => {
|
||||||
it("uses the fixed application feature identifier", () => {
|
it("uses the fixed application feature identifier", () => {
|
||||||
expect(TECH_LOG_FEATURE_ID).toBe("tech-log");
|
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ import type {
|
|||||||
StudioGateway,
|
StudioGateway,
|
||||||
} from "../../../src/features/tech-log/application/ports/studio-gateway.ts";
|
} 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 Inline = components["schemas"]["Inline"];
|
||||||
type CaseRenderBlock = components["schemas"]["CaseRenderBlock"];
|
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 },
|
{ type: "EVIDENCE_FIGURE", key: "fetch-plan", alt: "Fetch plan", caption: "Measured fetch plan", zoom: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const gatewayMethodNames = [
|
type RequiredStudioGatewayOperation =
|
||||||
"getDashboard",
|
| "getDashboard"
|
||||||
"listDocuments",
|
| "listDocuments"
|
||||||
"createDocument",
|
| "createDocument"
|
||||||
"getDocument",
|
| "getDocument"
|
||||||
"saveDocument",
|
| "saveDocument"
|
||||||
"validateDocument",
|
| "validateDocument"
|
||||||
"createPreview",
|
| "createPreview"
|
||||||
"getCurrentPreview",
|
| "getCurrentPreview"
|
||||||
"publishDocument",
|
| "publishDocument"
|
||||||
"unpublishPublication",
|
| "unpublishPublication"
|
||||||
"listPublications",
|
| "listPublications"
|
||||||
"getPublicationSnapshot",
|
| "getPublicationSnapshot"
|
||||||
"getCatalog",
|
| "getCatalog";
|
||||||
] as const;
|
|
||||||
|
|
||||||
function gatewayWithEveryRequiredMethod(): StudioGateway {
|
type StudioGatewayExposesNoMissingOrAdditionalOperations = Assert<
|
||||||
return {
|
Equal<keyof StudioGateway, RequiredStudioGatewayOperation>
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("TechLog Studio contracts", () => {
|
describe("TechLog Studio contracts", () => {
|
||||||
it("preserves every public wire discriminator exactly once", () => {
|
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);
|
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", () => {
|
it("keeps RFC 9457-style gateway failures inspectable at the application boundary", () => {
|
||||||
const error = new StudioGatewayError({
|
const error = new StudioGatewayError({
|
||||||
type: "https://techlog.dev/problems/conflict",
|
type: "https://techlog.dev/problems/conflict",
|
||||||
title: "Conflict",
|
title: "Conflict",
|
||||||
status: 409,
|
status: 409,
|
||||||
detail: "The working copy has changed.",
|
detail: "The working copy has changed.",
|
||||||
code: "DOCUMENT_VERSION_CONFLICT",
|
code: "VERSION_CONFLICT",
|
||||||
retryable: false,
|
retryable: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(error).toMatchObject({
|
expect(error).toMatchObject({
|
||||||
name: "StudioGatewayError",
|
name: "StudioGatewayError",
|
||||||
status: 409,
|
status: 409,
|
||||||
code: "DOCUMENT_VERSION_CONFLICT",
|
code: "VERSION_CONFLICT",
|
||||||
retryable: false,
|
retryable: false,
|
||||||
});
|
});
|
||||||
expect(isStudioGatewayError(error)).toBe(true);
|
expect(isStudioGatewayError(error)).toBe(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user