feat: add TechLog feature contracts
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { ApplicationFeatureInputs } from "../../../src/application/ports/in/application-api.ts";
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { components } from "../../../src/features/tech-log/contracts/studio/generated.ts";
|
||||
import {
|
||||
isStudioGatewayError,
|
||||
StudioGatewayError,
|
||||
} from "../../../src/features/tech-log/application/ports/studio-gateway-error.ts";
|
||||
import type {
|
||||
StudioGateway,
|
||||
} from "../../../src/features/tech-log/application/ports/studio-gateway.ts";
|
||||
|
||||
type Inline = components["schemas"]["Inline"];
|
||||
type CaseRenderBlock = components["schemas"]["CaseRenderBlock"];
|
||||
|
||||
const inlines: Inline[] = [
|
||||
{ type: "TEXT", text: "plain" },
|
||||
{ type: "EMPHASIS", children: [{ type: "TEXT", text: "emphasis" }] },
|
||||
{ type: "STRONG", children: [{ type: "TEXT", text: "strong" }] },
|
||||
{ type: "INLINE_CODE", code: "findById" },
|
||||
{ type: "LINK", label: "TechLog", href: "https://example.com" },
|
||||
{ type: "STATUS", label: "검증 필요", tone: "warning" },
|
||||
];
|
||||
|
||||
const blocks: CaseRenderBlock[] = [
|
||||
{ type: "HEADING", id: "problem", level: 2, content: [] },
|
||||
{ type: "PARAGRAPH", content: [] },
|
||||
{ type: "BLOCKQUOTE", content: [] },
|
||||
{ type: "UNORDERED_LIST", items: [] },
|
||||
{ type: "ORDERED_LIST", items: [] },
|
||||
{ type: "CODE_BLOCK", code: "select 1", language: "sql", label: null },
|
||||
{
|
||||
type: "DATA_TABLE",
|
||||
id: "query-counts",
|
||||
caption: "Query counts",
|
||||
rowHeaderColumn: null,
|
||||
columns: [{ id: "query-counts-column-1", label: "Case", alignment: "LEFT" }],
|
||||
rows: [],
|
||||
},
|
||||
{ type: "CALLOUT", tone: "warning", label: "주의", content: [] },
|
||||
{ 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;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
describe("TechLog Studio contracts", () => {
|
||||
it("preserves every public wire discriminator exactly once", () => {
|
||||
expect(inlines.map((inline) => inline.type)).toEqual([
|
||||
"TEXT",
|
||||
"EMPHASIS",
|
||||
"STRONG",
|
||||
"INLINE_CODE",
|
||||
"LINK",
|
||||
"STATUS",
|
||||
]);
|
||||
expect(blocks.map((block) => block.type)).toEqual([
|
||||
"HEADING",
|
||||
"PARAGRAPH",
|
||||
"BLOCKQUOTE",
|
||||
"UNORDERED_LIST",
|
||||
"ORDERED_LIST",
|
||||
"CODE_BLOCK",
|
||||
"DATA_TABLE",
|
||||
"CALLOUT",
|
||||
"EVIDENCE_FIGURE",
|
||||
]);
|
||||
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",
|
||||
retryable: false,
|
||||
});
|
||||
|
||||
expect(error).toMatchObject({
|
||||
name: "StudioGatewayError",
|
||||
status: 409,
|
||||
code: "DOCUMENT_VERSION_CONFLICT",
|
||||
retryable: false,
|
||||
});
|
||||
expect(isStudioGatewayError(error)).toBe(true);
|
||||
expect(isStudioGatewayError(new DOMException("Aborted", "AbortError"))).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user