refactor: 프론트 템플릿 리펙토링
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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<string, unknown>).id !== "string" ||
|
||||
typeof (value as Record<string, unknown>).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<InstalledHttpOperationExecutor["execute"]>(
|
||||
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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user