113 lines
3.4 KiB
TypeScript
113 lines
3.4 KiB
TypeScript
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 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"];
|
|
|
|
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 },
|
|
];
|
|
|
|
type RequiredStudioGatewayOperation =
|
|
| "getDashboard"
|
|
| "listDocuments"
|
|
| "createDocument"
|
|
| "getDocument"
|
|
| "saveDocument"
|
|
| "validateDocument"
|
|
| "createPreview"
|
|
| "getCurrentPreview"
|
|
| "publishDocument"
|
|
| "unpublishPublication"
|
|
| "listPublications"
|
|
| "getPublicationSnapshot"
|
|
| "getCatalog";
|
|
|
|
type StudioGatewayExposesNoMissingOrAdditionalOperations = Assert<
|
|
Equal<keyof StudioGateway, RequiredStudioGatewayOperation>
|
|
>;
|
|
|
|
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("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: "VERSION_CONFLICT",
|
|
retryable: false,
|
|
});
|
|
|
|
expect(error).toMatchObject({
|
|
name: "StudioGatewayError",
|
|
status: 409,
|
|
code: "VERSION_CONFLICT",
|
|
retryable: false,
|
|
});
|
|
expect(isStudioGatewayError(error)).toBe(true);
|
|
expect(isStudioGatewayError(new DOMException("Aborted", "AbortError"))).toBe(false);
|
|
});
|
|
});
|