Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
import { describe, it, expect, beforeEach } from "vitest";
import { RecordingConsent } from "@repo/core-testing/instrumentation";
import { getCategoriesHandler } from "@/handlers/get-categories.handler";
describe("getCategoriesHandler", () => {
let consent: RecordingConsent;
beforeEach(() => {
consent = new RecordingConsent();
});
it("returns { categories: [] } when no consent has been recorded", () => {
expect(getCategoriesHandler(consent)).toEqual({ categories: [] });
});
it("returns granted categories after grant", async () => {
await consent.grant("analytics");
const { categories } = getCategoriesHandler(consent);
expect(categories).toHaveLength(1);
expect(categories[0]!.category).toBe("analytics");
expect(categories[0]!.state).toBe("granted");
});
it("reflects withdrawn state after withdraw", async () => {
await consent.grant("marketing");
await consent.withdraw("marketing");
const { categories } = getCategoriesHandler(consent);
expect(categories).toHaveLength(1);
expect(categories[0]!.state).toBe("denied");
});
it("returns all categories when multiple are recorded", async () => {
await consent.grant("necessary");
await consent.grant("analytics");
await consent.grant("marketing");
const { categories } = getCategoriesHandler(consent);
expect(categories).toHaveLength(3);
const catNames = categories.map((c) => c.category).sort();
expect(catNames).toEqual(["analytics", "marketing", "necessary"]);
});
});

View File

@@ -0,0 +1,8 @@
import type { IConsent } from "../consent.interface";
import type { UserConsentState } from "../consent-types";
export function getCategoriesHandler(consent: IConsent): {
categories: UserConsentState[];
} {
return { categories: consent.getCategories() };
}

View File

@@ -0,0 +1,43 @@
import { describe, it, expect, beforeEach } from "vitest";
import { RecordingConsent } from "@repo/core-testing/instrumentation";
import { grantHandler } from "@/handlers/grant.handler";
describe("grantHandler", () => {
let consent: RecordingConsent;
beforeEach(() => {
consent = new RecordingConsent();
});
it("returns { success: true }", async () => {
const result = await grantHandler(consent, { category: "analytics" });
expect(result).toEqual({ success: true });
});
it("calls consent.grant with the given category", async () => {
await grantHandler(consent, { category: "marketing" });
expect(consent.grants).toHaveLength(1);
expect(consent.grants[0]!.category).toBe("marketing");
});
it("forwards meta to consent.grant", async () => {
await grantHandler(consent, {
category: "functional",
meta: {
bannerVersion: "v2",
policyVersion: "2026-01",
method: "banner-accept",
},
});
expect(consent.grants[0]!.meta).toEqual({
bannerVersion: "v2",
policyVersion: "2026-01",
method: "banner-accept",
});
});
it("grants without meta when omitted", async () => {
await grantHandler(consent, { category: "necessary" });
expect(consent.grants[0]!.meta).toBeUndefined();
});
});

View File

@@ -0,0 +1,26 @@
import { z } from "zod";
import type { IConsent } from "../consent.interface";
export const grantHandlerInputSchema = z
.object({
category: z.string().min(1),
meta: z
.object({
bannerVersion: z.string().optional(),
policyVersion: z.string().optional(),
method: z.string().optional(),
})
.strict()
.optional(),
})
.strict();
export type GrantHandlerInput = z.infer<typeof grantHandlerInputSchema>;
export async function grantHandler(
consent: IConsent,
input: GrantHandlerInput,
): Promise<{ success: true }> {
await consent.grant(input.category, input.meta);
return { success: true };
}

View File

@@ -0,0 +1,32 @@
import { describe, it, expect, beforeEach } from "vitest";
import { RecordingConsent } from "@repo/core-testing/instrumentation";
import { isGrantedHandler } from "@/handlers/is-granted.handler";
describe("isGrantedHandler", () => {
let consent: RecordingConsent;
beforeEach(() => {
consent = new RecordingConsent();
});
it("returns { granted: false } before any grant", () => {
expect(isGrantedHandler(consent, { category: "analytics" })).toEqual({
granted: false,
});
});
it("returns { granted: true } after grant", async () => {
await consent.grant("analytics");
expect(isGrantedHandler(consent, { category: "analytics" })).toEqual({
granted: true,
});
});
it("returns { granted: false } after withdraw", async () => {
await consent.grant("analytics");
await consent.withdraw("analytics");
expect(isGrantedHandler(consent, { category: "analytics" })).toEqual({
granted: false,
});
});
});

View File

@@ -0,0 +1,17 @@
import { z } from "zod";
import type { IConsent } from "../consent.interface";
export const isGrantedHandlerInputSchema = z
.object({
category: z.string().min(1),
})
.strict();
export type IsGrantedHandlerInput = z.infer<typeof isGrantedHandlerInputSchema>;
export function isGrantedHandler(
consent: IConsent,
input: IsGrantedHandlerInput,
): { granted: boolean } {
return { granted: consent.isGranted(input.category) };
}

View File

@@ -0,0 +1,28 @@
import { describe, it, expect, beforeEach } from "vitest";
import { RecordingConsent } from "@repo/core-testing/instrumentation";
import { withdrawHandler } from "@/handlers/withdraw.handler";
describe("withdrawHandler", () => {
let consent: RecordingConsent;
beforeEach(async () => {
consent = new RecordingConsent();
await consent.grant("analytics");
});
it("returns { success: true }", async () => {
const result = await withdrawHandler(consent, { category: "analytics" });
expect(result).toEqual({ success: true });
});
it("calls consent.withdraw with the given category", async () => {
await withdrawHandler(consent, { category: "analytics" });
expect(consent.withdrawals).toHaveLength(1);
expect(consent.withdrawals[0]).toBe("analytics");
});
it("category is no longer granted after withdraw", async () => {
await withdrawHandler(consent, { category: "analytics" });
expect(consent.isGranted("analytics")).toBe(false);
});
});

View File

@@ -0,0 +1,18 @@
import { z } from "zod";
import type { IConsent } from "../consent.interface";
export const withdrawHandlerInputSchema = z
.object({
category: z.string().min(1),
})
.strict();
export type WithdrawHandlerInput = z.infer<typeof withdrawHandlerInputSchema>;
export async function withdrawHandler(
consent: IConsent,
input: WithdrawHandlerInput,
): Promise<{ success: true }> {
await consent.withdraw(input.category);
return { success: true };
}