feat: add removable reference feature vertical slice
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
buildRouteUrl,
|
||||
parseRouteInput,
|
||||
} from "../../../src/presentation/routes/route-codecs.js";
|
||||
import {
|
||||
mapReferenceOperation,
|
||||
toReferenceView,
|
||||
} from "../../../src/features/reference-feature/contracts/reference-mapper.js";
|
||||
import {
|
||||
validateReferencePayload,
|
||||
validateReferenceRequest,
|
||||
} from "../../../src/features/reference-feature/contracts/reference-schemas.js";
|
||||
import {
|
||||
REFERENCE_FEATURE_CONTRACT,
|
||||
referenceQueryKeys,
|
||||
} from "../../../src/features/reference-feature/contracts/reference-feature-contract.js";
|
||||
|
||||
describe("reference feature boundary contracts", () => {
|
||||
it("round-trips one canonical filter through URL and query identity", () => {
|
||||
const filters = {
|
||||
tags: ["open", "new"],
|
||||
cursor: "a/b",
|
||||
limit: 5,
|
||||
};
|
||||
const url = buildRouteUrl("REFERENCE_RESOURCE_LIST", { search: filters });
|
||||
expect(url).toBe(
|
||||
"/examples/reference-resources?cursor=a%2Fb&limit=5&tags=open&tags=new",
|
||||
);
|
||||
const parsed = parseRouteInput(
|
||||
"REFERENCE_RESOURCE_LIST",
|
||||
{},
|
||||
new URL(url, "https://app.test").searchParams,
|
||||
);
|
||||
expect(parsed).toMatchObject({
|
||||
success: true,
|
||||
data: { search: filters },
|
||||
});
|
||||
expect(referenceQueryKeys.list(filters).at(-1)).toEqual(filters);
|
||||
});
|
||||
|
||||
it("rejects unknown search and malformed DTO before mapping", () => {
|
||||
expect(
|
||||
parseRouteInput(
|
||||
"REFERENCE_RESOURCE_LIST",
|
||||
{},
|
||||
new URLSearchParams("unknown=value"),
|
||||
),
|
||||
).toEqual({ success: false, code: "ROUTE_SEARCH_INVALID" });
|
||||
expect(
|
||||
validateReferencePayload("ReferenceResourceListPayload", [
|
||||
{ id: "unsafe", name: 42 },
|
||||
]),
|
||||
).toMatchObject({ success: false });
|
||||
expect(() =>
|
||||
mapReferenceOperation("LIST_REFERENCE_RESOURCES", [
|
||||
{ id: "unsafe", name: 42 },
|
||||
]),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it("normalizes request input and maps only owned domain fields", () => {
|
||||
expect(
|
||||
validateReferenceRequest("CreateReferenceResourceCommand", {
|
||||
name: " Example ",
|
||||
}),
|
||||
).toMatchObject({ success: true, data: { name: "Example" } });
|
||||
const model = mapReferenceOperation("CREATE_REFERENCE_RESOURCE", {
|
||||
id: "reference-1",
|
||||
name: "Example",
|
||||
createdAt: "2026-07-26T00:00:00.000Z",
|
||||
});
|
||||
if (!("id" in model)) throw new Error("expected one model");
|
||||
expect(toReferenceView(model, () => "formatted")).toEqual({
|
||||
resourceId: "reference-1",
|
||||
title: "Example",
|
||||
createdAtLabel: "formatted",
|
||||
});
|
||||
});
|
||||
|
||||
it("owns route, operation and query contributions in one removable contract", () => {
|
||||
expect(Object.keys(REFERENCE_FEATURE_CONTRACT.routes)).toEqual([
|
||||
"REFERENCE_RESOURCE_LIST",
|
||||
]);
|
||||
expect(Object.keys(REFERENCE_FEATURE_CONTRACT.apiOperations)).toEqual([
|
||||
"LIST_REFERENCE_RESOURCES",
|
||||
"CREATE_REFERENCE_RESOURCE",
|
||||
]);
|
||||
expect(Object.keys(REFERENCE_FEATURE_CONTRACT.queryRegistry)).toEqual([
|
||||
"REFERENCE_RESOURCE",
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user