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

116 lines
3.4 KiB
TypeScript

import { z } from "zod";
import type { createHttpClient } from "../../src/adapters/http/client.ts";
import { canonicalize } from "../../src/contracts/query-keys.ts";
import { mappingSuccess } from "../../src/contracts/boundary-mapper.ts";
import { createDemoSessionAdapter } from "../../src/adapters/auth/external-session-adapter.ts";
const entitySchema = z
.object({
id: z.string().min(1),
name: z.string().min(1),
})
.passthrough();
const payloadSchemas = {
EntityListPayload: z.array(entitySchema),
EntityPayload: entitySchema,
};
const requestSchemas = {
EntityListQuery: z
.object({
cursor: z.string().optional(),
limit: z.coerce.number().int().min(1).max(100).default(20),
tags: z.array(z.string().trim().min(1)).optional(),
})
.strict(),
CreateEntityCommand: z
.object({ name: z.string().trim().min(1).max(120) })
.strict(),
};
export const TEST_OPERATIONS = Object.freeze({
LIST_ENTITIES: Object.freeze({
method: "GET",
path: "/api/entities",
operationId: "LIST_ENTITIES",
auth: "external-session",
timeoutMs: null,
idempotency: "safe",
retry: "runtime",
requestSource: "search",
requestSchema: "EntityListQuery",
responseSchema: "EntityListPayload",
owner: "test-fixture",
}),
CREATE_ENTITY: Object.freeze({
method: "POST",
path: "/api/entities",
operationId: "CREATE_ENTITY",
auth: "external-session",
timeoutMs: null,
idempotency: "keyed",
retry: "runtime",
requestSource: "body",
requestSchema: "CreateEntityCommand",
responseSchema: "EntityPayload",
owner: "test-fixture",
}),
});
export const entityQueryKeys = Object.freeze({
list: (filters: Readonly<Record<string, unknown>> = {}) =>
Object.freeze(["entity", 1, canonicalize(filters)]),
});
type Validation =
| Readonly<{ success: true; data: unknown }>
| Readonly<{ success: false }>;
function project(schema: z.ZodType | undefined, value: unknown): Validation {
const result = schema?.safeParse(value);
if (!result?.success) return { success: false };
return { success: true, data: structuredClone(result.data) };
}
type HttpDependencies = Parameters<typeof createHttpClient>[0];
export const TEST_HTTP_CONTRACT = Object.freeze({
authSession: createDemoSessionAdapter("authenticated"),
getOperation(operationId: string) {
const operation =
TEST_OPERATIONS[operationId as keyof typeof TEST_OPERATIONS];
if (!operation) throw new Error(`Unknown test operation: ${operationId}`);
return operation;
},
validatePayload(schemaId: string, value: unknown) {
return project(
payloadSchemas[schemaId as keyof typeof payloadSchemas],
value,
);
},
validateRequest(schemaId: string, value: unknown) {
return project(
requestSchemas[schemaId as keyof typeof requestSchemas],
value,
);
},
mapPayload(operationId: string, payload: unknown) {
const mapOne = (value: unknown) => {
const entity = value as { id: string; name: string };
return { id: entity.id, displayName: entity.name };
};
if (operationId === "LIST_ENTITIES") {
return mappingSuccess((payload as readonly unknown[]).map(mapOne));
}
if (operationId === "CREATE_ENTITY") return mappingSuccess(mapOne(payload));
throw new Error(`Unknown test mapper: ${operationId}`);
},
}) satisfies Pick<
HttpDependencies,
| "authSession"
| "getOperation"
| "validatePayload"
| "validateRequest"
| "mapPayload"
>;