feat(core-consent): scaffold package with types, IConsent, withConsent brand wrapper
- Add packages/core-consent with ConsentCategory, ConsentState, UserConsentState types and IConsent interface - Add withConsent wrapper attaching __consentChecked brand at bind time; unit tests assert brand attachment and factory passthrough - Add ConsentChecked<F> type to core-shared/conformance/brands.ts and isConsentChecked helper to brand-runtime.ts - Extend FeatureManifest with requiresConsent?: readonly string[] field - Extend assertFeatureConformance to require __consentChecked brand when requiresConsent.length > 0; synthetic fixture tests cover pass/fail cases - Propagate __consentChecked in withSpan PROPAGATED_BRANDS so the outermost binding carries the brand Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
55
packages/core-consent/src/with-consent.test.ts
Normal file
55
packages/core-consent/src/with-consent.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, it, expect, expectTypeOf } from "vitest";
|
||||
import { withConsent, type ConsentChecked } from "@/with-consent";
|
||||
import type { IConsent } from "@/consent.interface";
|
||||
import { isConsentChecked } from "@repo/core-shared/conformance";
|
||||
|
||||
function makeConsent(): IConsent {
|
||||
return {
|
||||
isGranted: () => true,
|
||||
grant: () => Promise.resolve(),
|
||||
withdraw: () => Promise.resolve(),
|
||||
getCategories: () => [],
|
||||
};
|
||||
}
|
||||
|
||||
describe("withConsent", () => {
|
||||
it("returns a ConsentChecked<F>", () => {
|
||||
const consent = makeConsent();
|
||||
const fn = async (_input: { id: string }) => ({ ok: true });
|
||||
const wrapped = withConsent(consent, fn);
|
||||
expectTypeOf(wrapped).toMatchTypeOf<ConsentChecked<typeof fn>>();
|
||||
});
|
||||
|
||||
it("attaches __consentChecked as a non-enumerable property on the wrapped function", () => {
|
||||
const consent = makeConsent();
|
||||
const fn = async () => ({ ok: true });
|
||||
const wrapped = withConsent(consent, fn);
|
||||
expect(isConsentChecked(wrapped)).toBe(true);
|
||||
expect(Object.keys(wrapped)).not.toContain("__consentChecked");
|
||||
});
|
||||
|
||||
it("does NOT pollute the original input function with the brand", () => {
|
||||
const consent = makeConsent();
|
||||
const fn = async () => ({ ok: true });
|
||||
const wrapped = withConsent(consent, fn);
|
||||
expect(isConsentChecked(fn)).toBe(false);
|
||||
expect(wrapped).not.toBe(fn);
|
||||
});
|
||||
|
||||
it("passes input and output through unchanged", async () => {
|
||||
const consent = makeConsent();
|
||||
const fn = async (input: { id: string }) => ({ ok: true, id: input.id });
|
||||
const wrapped = withConsent(consent, fn);
|
||||
const result = await wrapped({ id: "abc" });
|
||||
expect(result).toEqual({ ok: true, id: "abc" });
|
||||
});
|
||||
|
||||
it("propagates errors", async () => {
|
||||
const consent = makeConsent();
|
||||
const err = new Error("boom");
|
||||
const wrapped = withConsent(consent, async () => {
|
||||
throw err;
|
||||
});
|
||||
await expect(wrapped()).rejects.toBe(err);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user