Files
clean-architecture-frontend…/tests/features/reference-feature/reference-http-problem-mapping.test.ts
T

136 lines
3.8 KiB
TypeScript

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,
},
});
});
});