Files
tech-log-frontend/tests/helpers/external-contract-fixture.ts
T

221 lines
6.2 KiB
TypeScript

import { z } from "zod";
import type {
CommandEffectDescriptor,
InstalledContractContribution,
InstalledHttpContract,
RuntimeValidator,
} from "../../src/contracts/external-contract-runtime.ts";
function zodValidator<T>(
schemaId: string,
schema: z.ZodType<T>,
): RuntimeValidator<T> {
return Object.freeze({
schemaId,
safeParse(value: unknown) {
const result = schema.safeParse(value);
if (result.success) {
return Object.freeze({ success: true as const, data: result.data });
}
return Object.freeze({
success: false as const,
issues: Object.freeze(
result.error.issues.map((issue) =>
Object.freeze({
path: Object.freeze(
issue.path.map((segment) =>
typeof segment === "number" ? segment : String(segment),
),
),
code: String(issue.code),
}),
),
),
});
},
});
}
const entitySchema = z
.object({
id: z.string().min(1),
name: z.string().min(1),
})
.strip();
const problemSchema = z
.object({
type: z.string().min(1),
title: z.string().min(1),
status: z.int().min(100).max(599),
})
.strip();
type TestProblem = z.output<typeof problemSchema>;
const problemValidator = zodValidator("TestProblem", problemSchema);
const readPolicy = Object.freeze({
policyId: "TEST_READ_V1",
requestByteLimit: 0,
responseByteLimit: 32_768,
totalDeadlineMs: 10_000,
retryBudget: 2 as const,
authProfileId: "TEST_AUTH",
diagnosticsOperation: "test.read",
});
export const TEST_LIST_HTTP_CONTRACT: InstalledHttpContract<
Readonly<{ limit: number }>,
readonly z.output<typeof entitySchema>[],
TestProblem
> = Object.freeze({
contract: Object.freeze({
operationId: "TEST_LIST_ENTITIES",
method: "GET" as const,
pathTemplate: "/api/test-entities",
inputValidator: zodValidator(
"TestEntityListQuery",
z
.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
})
.strict(),
),
outputValidator: zodValidator(
"TestEntityListPayload",
z.array(entitySchema).max(100),
),
problemValidator,
acceptedStatuses: Object.freeze([200]),
emptyBodyStatuses: Object.freeze([]),
retrySemantics: "SAFE" as const,
requestBody: "NONE" as const,
responseBody: "REQUIRED_JSON" as const,
commandRecovery: null,
commandEffect: null,
projectRequest(input: Readonly<{ limit: number }>) {
return Object.freeze({
pathValues: Object.freeze({}),
queryEntries: Object.freeze([
Object.freeze(["limit", String(input.limit)] as const),
]),
body: null,
});
},
}),
frontend: readPolicy,
});
export const TEST_DETAIL_HTTP_CONTRACT: InstalledHttpContract<
Readonly<{ entityId: string }>,
z.output<typeof entitySchema>,
TestProblem
> = Object.freeze({
contract: Object.freeze({
operationId: "TEST_GET_ENTITY",
method: "GET" as const,
pathTemplate: "/api/test-entities/{entityId}",
inputValidator: zodValidator(
"TestEntityParams",
z.object({ entityId: z.string().min(1) }).strict(),
),
outputValidator: zodValidator("TestEntityPayload", entitySchema),
problemValidator,
acceptedStatuses: Object.freeze([200]),
emptyBodyStatuses: Object.freeze([]),
retrySemantics: "SAFE" as const,
requestBody: "NONE" as const,
responseBody: "REQUIRED_JSON" as const,
commandRecovery: null,
commandEffect: null,
projectRequest(input: Readonly<{ entityId: string }>) {
return Object.freeze({
pathValues: Object.freeze({ entityId: input.entityId }),
queryEntries: Object.freeze([]),
body: null,
});
},
}),
frontend: Object.freeze({
...readPolicy,
policyId: "TEST_DETAIL_V1",
diagnosticsOperation: "test.detail",
}),
});
const commandEffect: CommandEffectDescriptor<TestProblem> = Object.freeze({
successEffect: "APPLIED_CONFIRMED" as const,
classifyProblem({
status,
}: Readonly<{ status: number; problem: TestProblem }>) {
return status === 400 ? "NOT_APPLIED" : "MAYBE_APPLIED";
},
});
export const TEST_CREATE_HTTP_CONTRACT: InstalledHttpContract<
Readonly<{ name: string }>,
z.output<typeof entitySchema>,
TestProblem
> = Object.freeze({
contract: Object.freeze({
operationId: "TEST_CREATE_ENTITY",
method: "POST" as const,
pathTemplate: "/api/test-entities",
inputValidator: zodValidator(
"TestCreateEntityCommand",
z.object({ name: z.string().trim().min(1) }).strict(),
),
outputValidator: zodValidator("TestEntityPayload", entitySchema),
problemValidator,
acceptedStatuses: Object.freeze([201]),
emptyBodyStatuses: Object.freeze([]),
retrySemantics: "KEYED" as const,
requestBody: "JSON" as const,
responseBody: "REQUIRED_JSON" as const,
commandRecovery: Object.freeze({
mode: "IDEMPOTENCY_REPLAY" as const,
operationIdentityField: "idempotencyKey",
}),
commandEffect,
projectRequest(input: Readonly<{ name: string }>) {
return Object.freeze({
pathValues: Object.freeze({}),
queryEntries: Object.freeze([]),
body: Object.freeze({ ...input }),
});
},
}),
frontend: Object.freeze({
policyId: "TEST_CREATE_V1",
requestByteLimit: 32_768,
responseByteLimit: 32_768,
totalDeadlineMs: 10_000,
retryBudget: 0 as const,
authProfileId: "TEST_AUTH",
diagnosticsOperation: "test.create",
}),
});
export const TEST_CONTRACT_CONTRIBUTION: InstalledContractContribution =
Object.freeze({
contributionId: "test-http-v1",
featureId: "test-feature",
source: Object.freeze({
kind: "EXTERNAL_PACKAGE" as const,
package: Object.freeze({
packageId: "@test/contracts",
version: "1.0.0",
digest: `sha256:${"a".repeat(64)}`,
runtimeProtocolVersion: 1 as const,
sourceRevision: "abcdef1",
}),
}),
http: Object.freeze([
TEST_LIST_HTTP_CONTRACT,
TEST_DETAIL_HTTP_CONTRACT,
TEST_CREATE_HTTP_CONTRACT,
]) as readonly InstalledHttpContract<unknown, unknown, unknown>[],
events: Object.freeze([]),
});