refactor: 프론트엔드 리펙토링
This commit is contained in:
@@ -32,7 +32,7 @@ describe("reference feature boundary contracts", () => {
|
||||
effect: "MAYBE_APPLIED" as const,
|
||||
}));
|
||||
const installed = createReferenceFeatureInstalledInput({
|
||||
contractOperations: { execute },
|
||||
http: { execute },
|
||||
});
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -3,7 +3,6 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import { createContractHttpExecutor } from "../../../src/adapters/http/index.ts";
|
||||
import { createHttpObservationProjector } from "../../../src/bootstrap/runtime-adapters.ts";
|
||||
import { createReferenceFeatureInstalledInput } from "../../../src/features/reference-feature/adapters/create-reference-feature-input.ts";
|
||||
import { REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION } from "../../../src/features/reference-feature/contracts/reference-feature-contract-contribution.ts";
|
||||
|
||||
function scopeSnapshot() {
|
||||
return Object.freeze({
|
||||
@@ -34,17 +33,9 @@ describe("reference feature HTTP diagnostics", () => {
|
||||
telemetry: { emit: vi.fn() },
|
||||
}),
|
||||
});
|
||||
const operations = new Map(
|
||||
REFERENCE_FEATURE_TEMPLATE_CONTRIBUTION.http.map((operation) => [
|
||||
operation.contract.operationId,
|
||||
operation,
|
||||
]),
|
||||
);
|
||||
const installed = createReferenceFeatureInstalledInput({
|
||||
contractOperations: Object.freeze({
|
||||
async execute(operationId, input, context) {
|
||||
const operation = operations.get(operationId);
|
||||
if (!operation) throw new Error("Unregistered reference operation");
|
||||
http: Object.freeze({
|
||||
async execute(operation, input, context) {
|
||||
return contractHttp.execute(operation, input, {
|
||||
routeId: context.routeId,
|
||||
scope: scopeSnapshot(),
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
createFeatureHttpBinding,
|
||||
type InstalledHttpOperationExecutor,
|
||||
} from "../../../src/adapters/http/index.ts";
|
||||
import {
|
||||
createReferenceHttpGateway,
|
||||
REFERENCE_HTTP_OPERATIONS,
|
||||
} from "../../../src/features/reference-feature/adapters/reference-http-gateway.ts";
|
||||
|
||||
function executorReturning(
|
||||
outcome: Readonly<Record<string, unknown>>,
|
||||
): InstalledHttpOperationExecutor {
|
||||
const execute = (async () => outcome) as unknown as
|
||||
InstalledHttpOperationExecutor["execute"];
|
||||
return Object.freeze({ execute });
|
||||
}
|
||||
|
||||
describe("reference HTTP business problem mapping", () => {
|
||||
it("maps a typed 409 reference problem to the feature-owned business code", async () => {
|
||||
const executor = executorReturning(
|
||||
Object.freeze({
|
||||
kind: "PROBLEM",
|
||||
problem: Object.freeze({
|
||||
type: "https://example.test/problems/reference-conflict",
|
||||
title: "Reference conflict",
|
||||
status: 409,
|
||||
code: "REFERENCE_NAME_ALREADY_EXISTS",
|
||||
}),
|
||||
metadata: Object.freeze({ status: 409 }),
|
||||
effect: "NOT_APPLIED",
|
||||
}),
|
||||
);
|
||||
const gateway = createReferenceHttpGateway(
|
||||
createFeatureHttpBinding(executor, REFERENCE_HTTP_OPERATIONS),
|
||||
);
|
||||
|
||||
await expect(
|
||||
gateway.create({ name: "duplicate" }),
|
||||
).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
kind: "CONFLICT",
|
||||
code: "REFERENCE_NAME_ALREADY_EXISTS",
|
||||
httpStatus: 409,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
status: 409,
|
||||
code: undefined,
|
||||
expectedKind: "CONFLICT",
|
||||
expectedCode: "REFERENCE_RESOURCE_CONFLICT",
|
||||
},
|
||||
{
|
||||
status: 422,
|
||||
code: "REFERENCE_NAME_REJECTED",
|
||||
expectedKind: "VALIDATION_REJECTED",
|
||||
expectedCode: "REFERENCE_NAME_REJECTED",
|
||||
},
|
||||
{
|
||||
status: 422,
|
||||
code: undefined,
|
||||
expectedKind: "VALIDATION_REJECTED",
|
||||
expectedCode: "REFERENCE_RESOURCE_REJECTED",
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
code: "REFERENCE_BAD_REQUEST",
|
||||
expectedKind: "UNKNOWN_CLIENT_FAILURE",
|
||||
expectedCode: "CONTRACT_PROBLEM",
|
||||
},
|
||||
] as const)(
|
||||
"projects create problem status $status through the feature-owned mapper/fallback",
|
||||
async ({ status, code, expectedKind, expectedCode }) => {
|
||||
const executor = executorReturning(
|
||||
Object.freeze({
|
||||
kind: "PROBLEM",
|
||||
problem: Object.freeze({
|
||||
type: "https://example.test/problems/reference-" + status,
|
||||
title: "Reference request rejected",
|
||||
status,
|
||||
...(code === undefined ? {} : { code }),
|
||||
}),
|
||||
metadata: Object.freeze({ status }),
|
||||
effect: "NOT_APPLIED",
|
||||
}),
|
||||
);
|
||||
const gateway = createReferenceHttpGateway(
|
||||
createFeatureHttpBinding(executor, REFERENCE_HTTP_OPERATIONS),
|
||||
);
|
||||
|
||||
await expect(
|
||||
gateway.create({ name: "rejected" }),
|
||||
).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
kind: expectedKind,
|
||||
code: expectedCode,
|
||||
httpStatus: status,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps the platform status fallback when the operation has no problem mapper", async () => {
|
||||
const executor = executorReturning(
|
||||
Object.freeze({
|
||||
kind: "PROBLEM",
|
||||
problem: Object.freeze({
|
||||
type: "https://example.test/problems/not-found",
|
||||
title: "Not found",
|
||||
status: 404,
|
||||
}),
|
||||
metadata: Object.freeze({ status: 404 }),
|
||||
effect: "NOT_APPLIED",
|
||||
}),
|
||||
);
|
||||
const gateway = createReferenceHttpGateway(
|
||||
createFeatureHttpBinding(executor, REFERENCE_HTTP_OPERATIONS),
|
||||
);
|
||||
|
||||
await expect(gateway.get("missing")).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
kind: "NOT_FOUND",
|
||||
code: "CONTRACT_PROBLEM",
|
||||
httpStatus: 404,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
type InstalledHttpOperationExecutor,
|
||||
} from "../../../src/adapters/http/index.ts";
|
||||
import { createReferenceFeatureInstalledInput } from "../../../src/features/reference-feature/adapters/create-reference-feature-input.ts";
|
||||
|
||||
/**
|
||||
@@ -14,22 +17,31 @@ import { createReferenceFeatureInstalledInput } from "../../../src/features/refe
|
||||
describe("reference feature installed operation executor", () => {
|
||||
it("preserves the feature route id through the installed operation executor", async () => {
|
||||
const seen: Array<Readonly<Record<string, unknown>>> = [];
|
||||
const implementation = async (
|
||||
contract: Readonly<{ contract: Readonly<{ operationId: string }> }>,
|
||||
_input: unknown,
|
||||
context: Readonly<Record<string, unknown>>,
|
||||
) => {
|
||||
seen.push(Object.freeze({ ...context }));
|
||||
const value =
|
||||
contract.contract.operationId === "LIST_REFERENCE_RESOURCES"
|
||||
? []
|
||||
: {
|
||||
id: "resource-1",
|
||||
name: "Resource",
|
||||
};
|
||||
return Object.freeze({
|
||||
kind: "SUCCESS" as const,
|
||||
value,
|
||||
metadata: Object.freeze({ status: 200 }),
|
||||
effect: "NOT_APPLICABLE" as const,
|
||||
});
|
||||
};
|
||||
const execute = implementation as unknown as
|
||||
InstalledHttpOperationExecutor["execute"];
|
||||
|
||||
const installed = createReferenceFeatureInstalledInput({
|
||||
contractOperations: Object.freeze({
|
||||
async execute(
|
||||
_operationId: string,
|
||||
_input: unknown,
|
||||
context?: Readonly<Record<string, unknown>>,
|
||||
) {
|
||||
seen.push(Object.freeze({ ...(context ?? {}) }));
|
||||
return Object.freeze({
|
||||
kind: "SUCCESS" as const,
|
||||
value: [],
|
||||
metadata: Object.freeze({ status: 200 }),
|
||||
effect: "NOT_APPLICABLE" as const,
|
||||
});
|
||||
},
|
||||
}),
|
||||
http: Object.freeze({ execute }),
|
||||
});
|
||||
|
||||
await installed.input.listResources({ limit: 20 });
|
||||
|
||||
Reference in New Issue
Block a user