257 lines
7.8 KiB
JavaScript
257 lines
7.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createIntegratedConsentEngine } from "../../integration/index.js";
|
|
|
|
// This suite exercises real persistence (acceptAll/revoke/reset expected to
|
|
// take effect), unlike tests/integration/integration-test.mjs which
|
|
// deliberately asserts the no-op behavior of a bare Node environment.
|
|
// A document.cookie mock is required here for the same reason it is
|
|
// required in test-core.mjs.
|
|
let jar = new Map();
|
|
|
|
globalThis.location = { protocol: "http:" };
|
|
globalThis.document = {};
|
|
Object.defineProperty(globalThis.document, "cookie", {
|
|
configurable: true,
|
|
get() {
|
|
return [...jar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
},
|
|
set(value) {
|
|
const parts = value.split(";").map((x) => x.trim());
|
|
const [name, raw = ""] = parts[0].split("=");
|
|
const maxAge = parts.find((x) => /^Max-Age=/i.test(x));
|
|
if (maxAge && maxAge.toLowerCase() === "max-age=0") {
|
|
jar.delete(name);
|
|
return;
|
|
}
|
|
jar.set(name, raw);
|
|
}
|
|
});
|
|
|
|
let passed = 0;
|
|
|
|
function test(name, fn) {
|
|
// Each test starts from a clean cookie jar so persisted consent from
|
|
// one test cannot leak into the next (they share default jurisdiction
|
|
// and policyVersion, so a stale cookie would otherwise be reloaded).
|
|
jar.clear();
|
|
|
|
try {
|
|
fn();
|
|
console.log(`PASS ${name}`);
|
|
passed += 1;
|
|
} catch (error) {
|
|
console.error(`FAIL ${name}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function createEngine(jurisdiction = "us-ma") {
|
|
return createIntegratedConsentEngine({
|
|
jurisdiction: {
|
|
mode: "fixed",
|
|
jurisdiction
|
|
}
|
|
});
|
|
}
|
|
|
|
test("dynamic jurisdiction is reflected in Core state", () => {
|
|
const consent = createEngine("global");
|
|
consent.initialize();
|
|
const state = consent.setJurisdiction("us-ma");
|
|
|
|
assert.equal(state.jurisdiction, "us-ma");
|
|
});
|
|
|
|
test("GPC state is represented without granting optional consent", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
const state = consent.setGpc(true);
|
|
|
|
assert.equal(state.gpc, true);
|
|
assert.equal(state.hasDecision, false);
|
|
assert.equal(state.categories.preferences, "unknown");
|
|
assert.equal(state.categories.analytics, "unknown");
|
|
assert.equal(state.categories.marketing, "unknown");
|
|
});
|
|
|
|
test("revoke produces the Core-defined denied state and is idempotent", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
consent.acceptAll();
|
|
const accepted = consent.getState();
|
|
assert.equal(accepted.hasDecision, true);
|
|
|
|
let revokedEvents = 0;
|
|
consent.on("consent:revoked", () => revokedEvents++);
|
|
|
|
const revoked = consent.revoke();
|
|
const secondRevoke = consent.revoke();
|
|
|
|
assert.equal(revoked.categories.necessary, "granted");
|
|
assert.equal(revoked.categories.preferences, "denied");
|
|
assert.equal(revoked.categories.analytics, "denied");
|
|
assert.equal(revoked.categories.marketing, "denied");
|
|
assert.equal(secondRevoke.categories.analytics, "denied");
|
|
assert.equal(revokedEvents, 1);
|
|
});
|
|
|
|
test("reset returns Core to an undecided state", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
consent.acceptAll();
|
|
|
|
const reset = consent.reset();
|
|
|
|
assert.equal(reset.hasDecision, false);
|
|
assert.equal(reset.categories.necessary, "granted");
|
|
assert.equal(reset.categories.preferences, "unknown");
|
|
assert.equal(reset.categories.analytics, "unknown");
|
|
assert.equal(reset.categories.marketing, "unknown");
|
|
});
|
|
|
|
test("policy version is part of the Core state", () => {
|
|
const consent = createEngine();
|
|
const config = consent.getConfig();
|
|
|
|
assert.equal(typeof config.policyVersion, "string");
|
|
assert.ok(config.policyVersion.length > 0);
|
|
});
|
|
|
|
test("persistence preserves the resolved jurisdiction after acceptance", () => {
|
|
const consent = createEngine("us-ma");
|
|
consent.initialize();
|
|
|
|
const saved = consent.acceptAll();
|
|
|
|
assert.equal(saved.hasDecision, true);
|
|
assert.equal(saved.jurisdiction, "us-ma");
|
|
});
|
|
|
|
test("event sequence reports the consent transition", () => {
|
|
const consent = createEngine();
|
|
const events = [];
|
|
|
|
consent.on("consent:changed", () => events.push("changed"));
|
|
consent.on("consent:accepted", () => events.push("accepted"));
|
|
|
|
consent.initialize();
|
|
consent.acceptAll();
|
|
|
|
assert.deepEqual(events, ["changed", "accepted"]);
|
|
});
|
|
|
|
test("Core state remains isolated from returned object mutation", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
const state = consent.getState();
|
|
state.categories.analytics = "granted";
|
|
state.jurisdiction = "changed";
|
|
|
|
const current = consent.getState();
|
|
|
|
assert.equal(current.categories.analytics, "unknown");
|
|
assert.equal(current.jurisdiction, "us-ma");
|
|
});
|
|
|
|
|
|
test("separate Core instances do not share in-memory state", () => {
|
|
const first = createEngine("us-ma");
|
|
const second = createEngine("eu");
|
|
|
|
first.initialize();
|
|
second.initialize();
|
|
|
|
first.setConsent({ analytics: "granted" });
|
|
|
|
assert.equal(first.getState().categories.analytics, "granted");
|
|
assert.equal(second.getState().categories.analytics, "unknown");
|
|
assert.equal(first.getState().jurisdiction, "us-ma");
|
|
assert.equal(second.getState().jurisdiction, "eu");
|
|
});
|
|
|
|
test("partial consent remains undecided until all optional categories are resolved", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
const state = consent.setConsent({ analytics: "granted" });
|
|
|
|
assert.equal(state.categories.analytics, "granted");
|
|
assert.equal(state.categories.preferences, "unknown");
|
|
assert.equal(state.categories.marketing, "unknown");
|
|
assert.equal(state.hasDecision, false);
|
|
assert.equal(consent.isComplete(), false);
|
|
});
|
|
|
|
test("necessary remains granted through valid consent changes", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
const state = consent.setConsent({
|
|
preferences: "denied",
|
|
analytics: "granted",
|
|
marketing: "denied"
|
|
});
|
|
|
|
assert.equal(state.categories.necessary, "granted");
|
|
assert.equal(state.categories.preferences, "denied");
|
|
assert.equal(state.categories.analytics, "granted");
|
|
assert.equal(state.categories.marketing, "denied");
|
|
});
|
|
|
|
test("acceptAll is idempotent through the integration boundary", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
let changed = 0;
|
|
let accepted = 0;
|
|
|
|
consent.on("consent:changed", () => changed++);
|
|
consent.on("consent:accepted", () => accepted++);
|
|
|
|
consent.acceptAll();
|
|
const first = consent.getState();
|
|
consent.acceptAll();
|
|
const second = consent.getState();
|
|
|
|
assert.equal(first.hasDecision, true);
|
|
assert.equal(second.hasDecision, true);
|
|
assert.equal(changed, 1);
|
|
assert.equal(accepted, 1);
|
|
assert.equal(second.timestamp, first.timestamp);
|
|
});
|
|
|
|
test("reset is idempotent through the integration boundary", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
consent.acceptAll();
|
|
|
|
let reset = 0;
|
|
consent.on("consent:reset", () => reset++);
|
|
|
|
consent.reset();
|
|
const first = consent.getState();
|
|
consent.reset();
|
|
const second = consent.getState();
|
|
|
|
assert.equal(first.hasDecision, false);
|
|
assert.equal(second.hasDecision, false);
|
|
assert.equal(second.categories.analytics, "unknown");
|
|
});
|
|
|
|
test("GPC never grants optional categories through the integration boundary", () => {
|
|
const consent = createEngine();
|
|
consent.initialize();
|
|
|
|
const state = consent.setGpc(true);
|
|
|
|
assert.equal(state.gpc, true);
|
|
assert.equal(state.categories.preferences, "unknown");
|
|
assert.equal(state.categories.analytics, "unknown");
|
|
assert.equal(state.categories.marketing, "unknown");
|
|
assert.equal(state.hasDecision, false);
|
|
});
|
|
|
|
console.log(`${passed} audit tests passed.`);
|