109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import type { createHttpClient } from "../../src/adapters/http/client.js";
|
|
import { canonicalize } from "../../src/contracts/query-keys.js";
|
|
|
|
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({
|
|
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 (payload as readonly unknown[]).map(mapOne);
|
|
}
|
|
if (operationId === "CREATE_ENTITY") return mapOne(payload);
|
|
throw new Error(`Unknown test mapper: ${operationId}`);
|
|
},
|
|
}) satisfies Pick<
|
|
HttpDependencies,
|
|
"getOperation" | "validatePayload" | "validateRequest" | "mapPayload"
|
|
>;
|