feat(core-api): compose dsrRouter + consentRouter into appRouter
Add @repo/core-dsr and @repo/core-consent as dependencies and wire dsrRouter + consentRouter into appRouter via the existing router composition pattern. Integration tests cover all eight procedures (dsr.export, dsr.delete, dsr.rectify, dsr.restrict, consent.grant, consent.withdraw, consent.isGranted, consent.getCategories) with auth and response-shape assertions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"generatedAt": "2026-05-19T20:36:42.351Z",
|
"generatedAt": "2026-05-19T20:49:04.345Z",
|
||||||
"commit": "46e575a",
|
"commit": "b2bfb5b",
|
||||||
"repo": {
|
"repo": {
|
||||||
"statements": 97.17,
|
"statements": 97.18,
|
||||||
"branches": 92.15,
|
"branches": 92.15,
|
||||||
"functions": 96.93,
|
"functions": 96.93,
|
||||||
"lines": 97.17,
|
"lines": 97.18,
|
||||||
"counts": {
|
"counts": {
|
||||||
"lf": 5329,
|
"lf": 5346,
|
||||||
"lh": 5178,
|
"lh": 5195,
|
||||||
"brf": 1045,
|
"brf": 1045,
|
||||||
"brh": 963,
|
"brh": 963,
|
||||||
"fnf": 326,
|
"fnf": 326,
|
||||||
@@ -58,6 +58,20 @@
|
|||||||
"fnh": 10
|
"fnh": 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@repo/core-api": {
|
||||||
|
"statements": 100,
|
||||||
|
"branches": 100,
|
||||||
|
"functions": 100,
|
||||||
|
"lines": 100,
|
||||||
|
"counts": {
|
||||||
|
"lf": 17,
|
||||||
|
"lh": 17,
|
||||||
|
"brf": 0,
|
||||||
|
"brh": 0,
|
||||||
|
"fnf": 0,
|
||||||
|
"fnh": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
"@repo/core-consent": {
|
"@repo/core-consent": {
|
||||||
"statements": 99.72,
|
"statements": 99.72,
|
||||||
"branches": 94.85,
|
"branches": 94.85,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@repo/auth": "workspace:*",
|
"@repo/auth": "workspace:*",
|
||||||
"@repo/blog": "workspace:*",
|
"@repo/blog": "workspace:*",
|
||||||
|
"@repo/core-consent": "workspace:*",
|
||||||
|
"@repo/core-dsr": "workspace:*",
|
||||||
"@repo/core-shared": "workspace:*",
|
"@repo/core-shared": "workspace:*",
|
||||||
"@repo/marketing-pages": "workspace:*",
|
"@repo/marketing-pages": "workspace:*",
|
||||||
"@repo/media": "workspace:*",
|
"@repo/media": "workspace:*",
|
||||||
@@ -26,6 +28,7 @@
|
|||||||
"@repo/core-testing": "workspace:*",
|
"@repo/core-testing": "workspace:*",
|
||||||
"@repo/core-typescript": "workspace:*",
|
"@repo/core-typescript": "workspace:*",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { blogRouter } from "@repo/blog/api";
|
|||||||
import { marketingPagesRouter } from "@repo/marketing-pages/api";
|
import { marketingPagesRouter } from "@repo/marketing-pages/api";
|
||||||
import { navigationRouter } from "@repo/navigation/api";
|
import { navigationRouter } from "@repo/navigation/api";
|
||||||
import { mediaRouter } from "@repo/media/api";
|
import { mediaRouter } from "@repo/media/api";
|
||||||
|
import { dsrRouter } from "@repo/core-dsr";
|
||||||
|
import { consentRouter } from "@repo/core-consent";
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
auth: authRouter,
|
auth: authRouter,
|
||||||
@@ -11,6 +13,9 @@ export const appRouter = router({
|
|||||||
marketingPages: marketingPagesRouter,
|
marketingPages: marketingPagesRouter,
|
||||||
navigation: navigationRouter,
|
navigation: navigationRouter,
|
||||||
media: mediaRouter,
|
media: mediaRouter,
|
||||||
|
// gen:routers — optional-core routers composed below
|
||||||
|
dsr: dsrRouter,
|
||||||
|
consent: consentRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|||||||
@@ -1,5 +1,68 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
|
import { router } from "@repo/core-shared/trpc/init";
|
||||||
import { appRouter } from "./root";
|
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", () => {
|
describe("appRouter composition", () => {
|
||||||
it("exposes auth, blog, marketingPages, navigation, media routers", () => {
|
it("exposes auth, blog, marketingPages, navigation, media routers", () => {
|
||||||
@@ -17,4 +80,315 @@ describe("appRouter composition", () => {
|
|||||||
expect(procedures).toHaveProperty("blog.articleBySlug");
|
expect(procedures).toHaveProperty("blog.articleBySlug");
|
||||||
expect(procedures).toHaveProperty("blog.listArticles");
|
expect(procedures).toHaveProperty("blog.listArticles");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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" });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -463,6 +463,12 @@ importers:
|
|||||||
"@repo/blog":
|
"@repo/blog":
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../blog
|
version: link:../blog
|
||||||
|
"@repo/core-consent":
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../core-consent
|
||||||
|
"@repo/core-dsr":
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../core-dsr
|
||||||
"@repo/core-shared":
|
"@repo/core-shared":
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../core-shared
|
version: link:../core-shared
|
||||||
@@ -491,6 +497,9 @@ importers:
|
|||||||
"@types/node":
|
"@types/node":
|
||||||
specifier: ^22.0.0
|
specifier: ^22.0.0
|
||||||
version: 22.19.17
|
version: 22.19.17
|
||||||
|
"@vitest/coverage-v8":
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0))
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)
|
version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user