import { http, HttpResponse } from "msw"; import { createMockStudioGateway } from "../../../src/features/tech-log/adapters/mock/mock-studio-gateway.ts"; import { isStudioGatewayError } from "../../../src/features/tech-log/application/ports/studio-gateway-error.ts"; const BASE = "http://api.test"; function problemResponse(error: unknown) { if (isStudioGatewayError(error)) { return HttpResponse.json(error.problem, { status: error.status, headers: { "content-type": "application/problem+json" }, }); } throw error; } async function json(work: () => Promise, status = 200) { try { return HttpResponse.json((await work()) as never, { status }); } catch (error) { return problemResponse(error); } } /** * Forwards every query parameter a `projectRequest` puts on the wire, not * just the ones a first pass happened to reach for. A handler that silently * drops a documented filter lets a later filter test pass for the wrong * reason — it would observe the *unfiltered* mock result and never learn the * parameter never left the URL. */ function queryParams( url: URL, stringNames: readonly string[], numberNames: readonly string[] = [], ): Record { const result: Record = {}; for (const name of stringNames) { const value = url.searchParams.get(name); if (value !== null) result[name] = value; } for (const name of numberNames) { const value = url.searchParams.get(name); if (value !== null) result[name] = Number(value); } return result; } export function createTechLogStudioHandlers( gateway = createMockStudioGateway(), baseUrl = BASE, ) { const key = (request: Request) => request.headers.get("Idempotency-Key") ?? "missing-key"; return { gateway, handlers: [ http.get(`${baseUrl}/api/v1/studio/session`, () => HttpResponse.json({ authenticated: true, displayName: "테스트 편집자", roles: ["STUDIO_EDITOR"], csrfToken: "csrf-test-token", csrfHeaderName: "X-CSRF-TOKEN", }), ), http.get(`${baseUrl}/api/v1/studio/dashboard`, () => json(() => gateway.getDashboard()), ), http.get(`${baseUrl}/api/v1/studio/documents`, ({ request }) => { const url = new URL(request.url); return json(() => gateway.listDocuments( queryParams( url, ["q", "kind", "publicationStatus", "nextAction", "projectId", "sort", "cursor"], ["limit"], ) as never, ), ); }), http.post(`${baseUrl}/api/v1/studio/documents`, async ({ request }) => json( async () => gateway.createDocument((await request.json()) as never, { idempotencyKey: key(request), }), 201, ), ), http.get(`${baseUrl}/api/v1/studio/documents/:documentId`, ({ params }) => json(() => gateway.getDocument(String(params.documentId))), ), http.put( `${baseUrl}/api/v1/studio/documents/:documentId`, async ({ request, params }) => json(async () => gateway.saveDocument( String(params.documentId), (await request.json()) as never, { idempotencyKey: key(request) }, ), ), ), http.post( `${baseUrl}/api/v1/studio/documents/:documentId/validate`, async ({ request, params }) => json(async () => gateway.validateDocument( String(params.documentId), (await request.json()) as never, { idempotencyKey: key(request) }, ), ), ), http.get( `${baseUrl}/api/v1/studio/documents/:documentId/preview`, ({ params }) => json(() => gateway.getCurrentPreview(String(params.documentId))), ), http.post( `${baseUrl}/api/v1/studio/documents/:documentId/preview`, async ({ request, params }) => json( async () => gateway.createPreview( String(params.documentId), (await request.json()) as never, { idempotencyKey: key(request) }, ), 201, ), ), http.post( `${baseUrl}/api/v1/studio/documents/:documentId/publish`, async ({ request, params }) => json(async () => gateway.publishDocument( String(params.documentId), (await request.json()) as never, { idempotencyKey: key(request) }, ), ), ), http.get(`${baseUrl}/api/v1/studio/publications`, ({ request }) => { const url = new URL(request.url); return json(() => gateway.listPublications( queryParams(url, ["q", "type", "cursor"], ["limit"]) as never, ), ); }), http.post( `${baseUrl}/api/v1/studio/publications/:publicationId/unpublish`, async ({ request, params }) => json(async () => gateway.unpublishPublication( String(params.publicationId), (await request.json()) as never, { idempotencyKey: key(request) }, ), ), ), http.get( `${baseUrl}/api/v1/studio/publications/:publicationEventId/preview`, ({ params }) => json(() => gateway.getPublicationSnapshot(String(params.publicationEventId))), ), http.get(`${baseUrl}/api/v1/studio/catalog`, ({ request }) => { const url = new URL(request.url); return json(() => gateway.getCatalog( queryParams(url, ["type", "q", "cursor"], ["limit"]) as never, ), ); }), ], }; }