Files
agentic-dev/packages/core-api/src/router.test.ts
Danijel Martinek 9ee941863d refactor(marketing-pages)!: delete marketing-pages demo feature
Veect retrofit (ADR-027): fourth slice of the demo-content removal.
Deletes packages/marketing-pages whole and prunes every composition
edge in one commit: core-api router mount + dep, core-cms
collection/global composition + dep + regenerated Payload types,
web-next bindAll (prod + dev-seed) + tests + about page + Tailwind
source + transpilePackages + dep, cms/core-cms payload config test
assertions, marketing-page e2e spec, tsconfig paths, fallow ignore
entry, anchor-guard + generator e2e feature lists, compliance
data-map + retention-policy regeneration, lockfile prune, and
feature-list doc entries (CLAUDE.md, AGENTS.md, glossary, app/feature
AGENTS.md).

Event teardown: marketing-pages was the sole consumer of
auth.user.signed-up (welcome-email handler + job). The handler, its
Payload tasks, and the bus subscription all lived inside the package's
own binders, so they die with it — no other package wires the
subscription. Auth's manifest `publishes` stays untouched: a publisher
with zero consumers is legal (pnpm conformance only fails on orphan
consumers, verified green). The app-level sign-up-welcome-email test
asserted the marketing-pages mailer stays empty without a bus; it is
deleted with the feature, and the now-unused test-only bind-state
helpers in web-next bind-production.ts go with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 17:23:08 +02:00

386 lines
14 KiB
TypeScript

import { describe, it, expect, beforeEach } from "vitest";
import { router } from "@repo/core-shared/trpc/init";
import { appRouter } from "./root";
import { createDsrRouter } from "@repo/core-dsr";
import type { DsrBinding } from "@repo/core-dsr";
import type { DsrTrpcUser } from "@repo/core-dsr";
import { consentRouter } from "@repo/core-consent";
import type { ConsentFactory } from "@repo/core-consent";
import {
RecordingDataExport,
RecordingDataDelete,
RecordingDataRectify,
RecordingProcessingRestriction,
RecordingConsent,
} from "@repo/core-testing/instrumentation";
// ---------------------------------------------------------------------------
// Test helpers
// ---------------------------------------------------------------------------
function makeDsrBinding() {
return {
dataExport: new RecordingDataExport(),
dataDelete: new RecordingDataDelete(),
dataRectify: new RecordingDataRectify(),
processingRestriction: new RecordingProcessingRestriction(),
};
}
type DsrTestBinding = ReturnType<typeof makeDsrBinding>;
function makeIntegrationRouter(dsrBinding: DsrTestBinding) {
return router({
dsr: createDsrRouter(dsrBinding as unknown as DsrBinding),
consent: consentRouter,
});
}
type IntegrationRouter = ReturnType<typeof makeIntegrationRouter>;
function makeCaller(
testRouter: IntegrationRouter,
userId: string,
consentFactory: ConsentFactory,
roles: string[] = ["user"],
) {
return testRouter.createCaller({
user: { id: userId, roles } as DsrTrpcUser,
userId,
consentFactory,
} as Record<string, unknown>);
}
function makeUnauthCaller(
testRouter: IntegrationRouter,
consentFactory: ConsentFactory,
) {
return testRouter.createCaller({
consentFactory,
} as Record<string, unknown>);
}
// ---------------------------------------------------------------------------
// Structure tests — appRouter composition
// ---------------------------------------------------------------------------
describe("appRouter composition", () => {
it("exposes auth, navigation routers", () => {
const procedures = appRouter._def.procedures;
const keys = Object.keys(procedures);
expect(keys.some((k) => k.startsWith("auth."))).toBe(true);
expect(keys.some((k) => k.startsWith("navigation."))).toBe(true);
});
it("exposes dsr and consent routers", () => {
const keys = Object.keys(appRouter._def.procedures);
expect(keys.some((k) => k.startsWith("dsr."))).toBe(true);
expect(keys.some((k) => k.startsWith("consent."))).toBe(true);
});
it("dsr router exposes all four procedures", () => {
const procedures = appRouter._def.procedures;
expect(procedures).toHaveProperty("dsr.export");
expect(procedures).toHaveProperty("dsr.delete");
expect(procedures).toHaveProperty("dsr.rectify");
expect(procedures).toHaveProperty("dsr.restrict");
});
it("consent router exposes all four procedures", () => {
const procedures = appRouter._def.procedures;
expect(procedures).toHaveProperty("consent.grant");
expect(procedures).toHaveProperty("consent.withdraw");
expect(procedures).toHaveProperty("consent.isGranted");
expect(procedures).toHaveProperty("consent.getCategories");
});
});
// ---------------------------------------------------------------------------
// Integration tests — dsr procedures
// ---------------------------------------------------------------------------
describe("dsr.export — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with UserDataBundle body for authenticated user", async () => {
const result = await caller.dsr.export({
subjectId: "alice",
format: "json",
});
expect(result.subjectId).toBe("alice");
expect(result.format).toBe("json");
expect(binding.dataExport.calls).toHaveLength(1);
});
it("resolves with json-ld format", async () => {
const result = await caller.dsr.export({
subjectId: "alice",
format: "json-ld",
});
expect(result.format).toBe("json-ld");
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.export({ subjectId: "alice", format: "json" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.delete — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
});
it("resolves with DeletionCertificate for soft mode (authenticated user)", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const caller = makeCaller(testRouter, "alice", factory);
const result = await caller.dsr.delete({
subjectId: "alice",
mode: "soft",
});
expect(result.subjectId).toBe("alice");
expect(result.mode).toBe("soft");
expect(binding.dataDelete.calls).toHaveLength(1);
});
it("resolves with DeletionCertificate for cascade-hard mode (admin)", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const adminCaller = makeCaller(testRouter, "admin", factory, ["admin"]);
const result = await adminCaller.dsr.delete({
subjectId: "alice",
mode: "cascade-hard",
});
expect(result.mode).toBe("cascade-hard");
});
it("throws FORBIDDEN for cascade-hard when user lacks admin role", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const caller = makeCaller(testRouter, "alice", factory, ["user"]);
await expect(
caller.dsr.delete({ subjectId: "alice", mode: "cascade-hard" }),
).rejects.toMatchObject({ code: "FORBIDDEN" });
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.delete({ subjectId: "alice", mode: "soft" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.rectify — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with { ok: true } for authenticated user", async () => {
const result = await caller.dsr.rectify({
subjectId: "alice",
collection: "users",
field: "name",
value: "Alice Updated",
});
expect(result).toEqual({ ok: true });
expect(binding.dataRectify.calls[0]).toMatchObject({
subjectId: "alice",
collection: "users",
field: "name",
});
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.rectify({
subjectId: "alice",
collection: "users",
field: "name",
value: "x",
}),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.restrict — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with { ok: true } when granting restriction", async () => {
const result = await caller.dsr.restrict({
subjectId: "alice",
granted: true,
});
expect(result).toEqual({ ok: true });
expect(binding.processingRestriction.sets[0]).toMatchObject({
subjectId: "alice",
granted: true,
});
});
it("resolves with { ok: true } when lifting restriction", async () => {
const result = await caller.dsr.restrict({
subjectId: "alice",
granted: false,
});
expect(result).toEqual({ ok: true });
expect(binding.processingRestriction.sets[0]?.granted).toBe(false);
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.restrict({ subjectId: "alice", granted: true }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
// ---------------------------------------------------------------------------
// Integration tests — consent procedures
// ---------------------------------------------------------------------------
describe("consent — integration", () => {
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
const binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "user-1", factory);
});
describe("consent.grant", () => {
it("resolves with { success: true } and records the grant", async () => {
const result = await caller.consent.grant({ category: "analytics" });
expect(result).toEqual({ success: true });
expect(consent.grants).toHaveLength(1);
expect(consent.grants[0]!.category).toBe("analytics");
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.grant({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.withdraw", () => {
it("resolves with { success: true } and records the withdrawal", async () => {
await caller.consent.grant({ category: "marketing" });
const result = await caller.consent.withdraw({ category: "marketing" });
expect(result).toEqual({ success: true });
expect(consent.withdrawals).toHaveLength(1);
expect(consent.withdrawals[0]).toBe("marketing");
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.withdraw({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.isGranted", () => {
it("resolves with { granted: false } before any grant", async () => {
const result = await caller.consent.isGranted({ category: "analytics" });
expect(result).toEqual({ granted: false });
});
it("resolves with { granted: true } after grant", async () => {
await caller.consent.grant({ category: "analytics" });
const result = await caller.consent.isGranted({ category: "analytics" });
expect(result).toEqual({ granted: true });
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.isGranted({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.getCategories", () => {
it("resolves with { categories: [] } initially", async () => {
const result = await caller.consent.getCategories({});
expect(result).toEqual({ categories: [] });
});
it("resolves with all granted categories", async () => {
await caller.consent.grant({ category: "necessary" });
await caller.consent.grant({ category: "analytics" });
const { categories } = await caller.consent.getCategories({});
expect(categories).toHaveLength(2);
const names = categories.map((c) => c.category).sort();
expect(names).toEqual(["analytics", "necessary"]);
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.getCategories({}),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
});