import { describe, expect, it, vi } from "vitest"; import { createFeatureHttpBinding, defineFeatureHttpOperation, type InstalledHttpOperationExecutor, } from "../../../src/adapters/http/index.ts"; import { mappingFailure, mappingSuccess, } from "../../../src/contracts/boundary-mapper.ts"; type Resource = Readonly<{ id: string; title: string }>; const OPERATIONS = Object.freeze({ LOAD_RESOURCE: defineFeatureHttpOperation< Readonly<{ resourceId: string }>, Resource >({ operationId: "LOAD_RESOURCE", routeId: "RESOURCE_DETAIL", mapSuccess(value) { if ( !value || typeof value !== "object" || typeof (value as Record).id !== "string" || typeof (value as Record).title !== "string" ) { return mappingFailure("MAPPING_INVARIANT_REJECTED"); } const candidate = value as Readonly<{ id: string; title: string }>; return mappingSuccess( Object.freeze({ id: candidate.id, title: candidate.title }), ); }, }), } as const); describe("feature HTTP binding", () => { it("keeps typed feature input while platform owns route/context execution", async () => { const execute = vi.fn( async (_operationId, input, context) => { expect(input).toEqual({ resourceId: "resource-1" }); expect(context.routeId).toBe("RESOURCE_DETAIL"); return Object.freeze({ kind: "SUCCESS" as const, value: Object.freeze({ id: "resource-1", title: "Reference" }), metadata: Object.freeze({ status: 200 }), effect: "NOT_APPLICABLE" as const, }); }, ); const binding = createFeatureHttpBinding( Object.freeze({ execute }), OPERATIONS, ); const result = await binding.execute("LOAD_RESOURCE", { resourceId: "resource-1", }); expect(result).toEqual({ ok: true, value: { id: "resource-1", title: "Reference" }, }); expect(execute).toHaveBeenCalledTimes(1); }); it("normalizes transport failure before it crosses the feature gateway", async () => { const executor: InstalledHttpOperationExecutor = Object.freeze({ async execute() { return Object.freeze({ kind: "TRANSPORT_FAILURE" as const, failure: Object.freeze({ kind: "TIMEOUT" as const, retryable: true, }), effect: "NOT_STARTED" as const, }); }, }); const binding = createFeatureHttpBinding(executor, OPERATIONS); const result = await binding.execute("LOAD_RESOURCE", { resourceId: "resource-1", }); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.kind).toBe("REQUEST_TIMEOUT"); expect(result.error.operationId).toBe("LOAD_RESOURCE"); expect(result.error.effect).toBe("NOT_STARTED"); }); it("turns feature mapper rejection into the shared mapping failure", async () => { const executor: InstalledHttpOperationExecutor = Object.freeze({ async execute() { return Object.freeze({ kind: "SUCCESS" as const, value: Object.freeze({ unexpected: true }), metadata: Object.freeze({ status: 200 }), effect: "NOT_APPLICABLE" as const, }); }, }); const binding = createFeatureHttpBinding(executor, OPERATIONS); const result = await binding.execute("LOAD_RESOURCE", { resourceId: "resource-1", }); expect(result.ok).toBe(false); if (result.ok) return; expect(result.error.kind).toBe("MAPPING_CONTRACT_VIOLATION"); expect(result.error.code).toBe("MAPPING_INVARIANT_REJECTED"); }); });