mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-11-05 04:51:00 +00:00
feat(core): implement policy module with decision making and effect composition
This commit is contained in:
1 parent
8492411265
commit
b24500010f
2 files changed
+808
No files matched your search
@@ -0,0 +1,428 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
BUILTIN_POLICY_PROFILES,
|
||||
createPolicyEffect,
|
||||
createPolicyProfile,
|
||||
createPolicyDefinition,
|
||||
createPolicyContext,
|
||||
createGovernanceDecision,
|
||||
getPolicyEffect,
|
||||
composeGovernanceDecisions
|
||||
} from "../src/policy.js";
|
||||
|
||||
describe("policy", () => {
|
||||
describe("BUILTIN_POLICY_PROFILES", () => {
|
||||
it("should define built-in policy profiles with effects", () => {
|
||||
expect(BUILTIN_POLICY_PROFILES).toHaveProperty("default");
|
||||
expect(BUILTIN_POLICY_PROFILES).toHaveProperty("moderate");
|
||||
expect(BUILTIN_POLICY_PROFILES).toHaveProperty("strict");
|
||||
expect(BUILTIN_POLICY_PROFILES).toHaveProperty("commerce-default");
|
||||
expect(BUILTIN_POLICY_PROFILES).toHaveProperty("commerce-transaction");
|
||||
|
||||
// Check that each profile has a name and effects
|
||||
for (const [name, profile] of Object.entries(BUILTIN_POLICY_PROFILES)) {
|
||||
expect(profile.name).toBe(name);
|
||||
expect(profile.effects).toBeDefined();
|
||||
expect(typeof profile.effects).toBe("object");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPolicyEffect", () => {
|
||||
it("should create a valid policy effect", () => {
|
||||
const effect = createPolicyEffect("hide", "User is blocked", { details: "more info" });
|
||||
expect(effect).toEqual({
|
||||
effect: "hide",
|
||||
reason: "User is blocked",
|
||||
details: { details: "more info" }
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a policy effect without optional fields", () => {
|
||||
const effect = createPolicyEffect("allow");
|
||||
expect(effect).toEqual({ effect: "allow" });
|
||||
});
|
||||
|
||||
it("should throw error for invalid effect", () => {
|
||||
expect(() => createPolicyEffect(/** @type {any} */ ("invalid"))).toThrow();
|
||||
expect(() => createPolicyEffect(/** @type {any} */ (123))).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPolicyProfile", () => {
|
||||
it("should create a valid policy profile", () => {
|
||||
const profile = createPolicyProfile("test-profile", {
|
||||
"visibility": { effect: "hide", reason: "Hidden" },
|
||||
"interaction": { effect: "deny", reason: "Denied" }
|
||||
});
|
||||
|
||||
expect(profile).toEqual({
|
||||
name: "test-profile",
|
||||
effects: {
|
||||
"visibility": { effect: "hide", reason: "Hidden" },
|
||||
"interaction": { effect: "deny", reason: "Denied" }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid profile name", () => {
|
||||
expect(() => createPolicyProfile("", {})).toThrow();
|
||||
expect(() => createPolicyProfile(/** @type {any} */ (123), {})).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid effects", () => {
|
||||
expect(() => createPolicyProfile("test-profile", /** @type {any} */ (null))).toThrow();
|
||||
expect(() => createPolicyProfile("test-profile", /** @type {any} */ ("not-an-object"))).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid effect types", () => {
|
||||
expect(() => createPolicyProfile("test-profile", {
|
||||
"": { effect: "allow" }
|
||||
})).toThrow();
|
||||
|
||||
expect(() => createPolicyProfile("test-profile", {
|
||||
"visibility": /** @type {any} */ (null)
|
||||
})).toThrow();
|
||||
|
||||
expect(() => createPolicyProfile("test-profile", {
|
||||
"visibility": { effect: /** @type {any} */ ("invalid") }
|
||||
})).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPolicyDefinition", () => {
|
||||
it("should create a valid policy definition", () => {
|
||||
const definition = createPolicyDefinition(
|
||||
"test-policy",
|
||||
"Test Policy",
|
||||
"A test policy",
|
||||
{
|
||||
"default": {
|
||||
name: "default",
|
||||
effects: {
|
||||
"visibility": { effect: "allow" }
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(definition).toEqual({
|
||||
id: "test-policy",
|
||||
name: "Test Policy",
|
||||
description: "A test policy",
|
||||
profiles: {
|
||||
"default": {
|
||||
name: "default",
|
||||
effects: {
|
||||
"visibility": { effect: "allow" }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid ID", () => {
|
||||
expect(() => createPolicyDefinition("", "Test Policy", "A test policy", {})).toThrow();
|
||||
expect(() => createPolicyDefinition(/** @type {any} */ (123), "Test Policy", "A test policy", {})).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid name", () => {
|
||||
expect(() => createPolicyDefinition("test-policy", "", "A test policy", {})).toThrow();
|
||||
expect(() => createPolicyDefinition("test-policy", /** @type {any} */ (123), "A test policy", {})).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid description", () => {
|
||||
expect(() => createPolicyDefinition("test-policy", "Test Policy", /** @type {any} */ (123), {})).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid profiles", () => {
|
||||
expect(() => createPolicyDefinition("test-policy", "Test Policy", "A test policy", /** @type {any} */ (null))).toThrow();
|
||||
expect(() => createPolicyDefinition("test-policy", "Test Policy", "A test policy", /** @type {any} */ ("not-an-object"))).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid profile names", () => {
|
||||
expect(() => createPolicyDefinition(
|
||||
"test-policy",
|
||||
"Test Policy",
|
||||
"A test policy",
|
||||
{
|
||||
"": {
|
||||
name: "",
|
||||
effects: {}
|
||||
}
|
||||
}
|
||||
)).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for profile name mismatch", () => {
|
||||
expect(() => createPolicyDefinition(
|
||||
"test-policy",
|
||||
"Test Policy",
|
||||
"A test policy",
|
||||
{
|
||||
"default": {
|
||||
name: "different-name",
|
||||
effects: {}
|
||||
}
|
||||
}
|
||||
)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPolicyContext", () => {
|
||||
it("should create a valid policy context", () => {
|
||||
const context = createPolicyContext("feed", "default");
|
||||
expect(context).toEqual({
|
||||
surface: "feed",
|
||||
policyProfile: "default"
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a policy context with enforcement options", () => {
|
||||
const context = createPolicyContext("feed", "default", {
|
||||
hardHide: true,
|
||||
allowOverrides: false
|
||||
});
|
||||
|
||||
expect(context).toEqual({
|
||||
surface: "feed",
|
||||
policyProfile: "default",
|
||||
enforcement: {
|
||||
hardHide: true,
|
||||
allowOverrides: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid surface", () => {
|
||||
expect(() => createPolicyContext("", "default")).toThrow();
|
||||
expect(() => createPolicyContext(/** @type {any} */ (123), "default")).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid policy profile", () => {
|
||||
expect(() => createPolicyContext("feed", "")).toThrow();
|
||||
expect(() => createPolicyContext("feed", /** @type {any} */ (123))).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createGovernanceDecision", () => {
|
||||
it("should create a valid governance decision", () => {
|
||||
const decision = createGovernanceDecision(
|
||||
{ effect: "hide", reason: "Hidden" },
|
||||
{ effect: "deny", reason: "Denied" },
|
||||
{ effect: "deny", reason: "Transaction denied" },
|
||||
["reason1", "reason2"],
|
||||
[{ evidence: "evidence1" }, { evidence: "evidence2" }],
|
||||
{ metadata: "data" }
|
||||
);
|
||||
|
||||
expect(decision).toEqual({
|
||||
visibility: { effect: "hide", reason: "Hidden" },
|
||||
interaction: { effect: "deny", reason: "Denied" },
|
||||
transaction: { effect: "deny", reason: "Transaction denied" },
|
||||
reasons: ["reason1", "reason2"],
|
||||
evidence: [{ evidence: "evidence1" }, { evidence: "evidence2" }],
|
||||
metadata: { metadata: "data" }
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a governance decision without optional fields", () => {
|
||||
const decision = createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" }
|
||||
);
|
||||
|
||||
expect(decision).toEqual({
|
||||
visibility: { effect: "allow" },
|
||||
interaction: { effect: "allow" },
|
||||
reasons: [],
|
||||
evidence: []
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid visibility effect", () => {
|
||||
expect(() => createGovernanceDecision(
|
||||
/** @type {any} */ ({ effect: "invalid" }),
|
||||
{ effect: "allow" }
|
||||
)).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid interaction effect", () => {
|
||||
expect(() => createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
/** @type {any} */ ({ effect: "invalid" })
|
||||
)).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid transaction effect", () => {
|
||||
expect(() => createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" },
|
||||
/** @type {any} */ ({ effect: "invalid" })
|
||||
)).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid reasons", () => {
|
||||
expect(() => createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" },
|
||||
undefined,
|
||||
/** @type {any} */ ("not-an-array")
|
||||
)).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for invalid evidence", () => {
|
||||
expect(() => createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" },
|
||||
undefined,
|
||||
[],
|
||||
/** @type {any} */ ("not-an-array")
|
||||
)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPolicyEffect", () => {
|
||||
it("should return effect for built-in profiles", () => {
|
||||
const effect = getPolicyEffect("strict", "visibility");
|
||||
expect(effect).toEqual({ effect: "hide" });
|
||||
});
|
||||
|
||||
it("should return effect for custom policies", () => {
|
||||
const customPolicies = {
|
||||
"custom-policy": {
|
||||
id: "custom-policy",
|
||||
name: "Custom Policy",
|
||||
description: "A custom policy",
|
||||
profiles: {
|
||||
"custom-profile": {
|
||||
name: "custom-profile",
|
||||
effects: {
|
||||
"visibility": { effect: "hide", reason: "Custom hidden" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const effect = getPolicyEffect("custom-profile", "visibility", /** @type {any} */ (customPolicies));
|
||||
expect(effect).toEqual({ effect: "hide", reason: "Custom hidden" });
|
||||
});
|
||||
|
||||
it("should prefer custom policies over built-in profiles", () => {
|
||||
const customPolicies = {
|
||||
"custom-policy": {
|
||||
id: "custom-policy",
|
||||
name: "Custom Policy",
|
||||
description: "A custom policy",
|
||||
profiles: {
|
||||
"default": {
|
||||
name: "default",
|
||||
effects: {
|
||||
"visibility": { effect: "hide", reason: "Custom default" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const effect = getPolicyEffect("default", "visibility", /** @type {any} */ (customPolicies));
|
||||
expect(effect).toEqual({ effect: "hide", reason: "Custom default" });
|
||||
});
|
||||
|
||||
it("should return default allow effect for unknown profiles/effects", () => {
|
||||
const effect = getPolicyEffect("unknown-profile", "unknown-effect");
|
||||
expect(effect).toEqual({ effect: "allow" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("composeGovernanceDecisions", () => {
|
||||
it("should return default allow decision for empty array", () => {
|
||||
const decision = composeGovernanceDecisions([]);
|
||||
expect(decision).toEqual({
|
||||
visibility: { effect: "allow" },
|
||||
interaction: { effect: "allow" },
|
||||
reasons: [],
|
||||
evidence: []
|
||||
});
|
||||
});
|
||||
|
||||
it("should return the single decision for array with one element", () => {
|
||||
const inputDecision = createGovernanceDecision(
|
||||
{ effect: "hide", reason: "Hidden" },
|
||||
{ effect: "deny", reason: "Denied" },
|
||||
undefined,
|
||||
["reason1"],
|
||||
[{ evidence: "evidence1" }]
|
||||
);
|
||||
|
||||
const decision = composeGovernanceDecisions([inputDecision]);
|
||||
expect(decision).toEqual(inputDecision);
|
||||
});
|
||||
|
||||
it("should compose multiple decisions with most restrictive effects", () => {
|
||||
const decisions = [
|
||||
createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" }
|
||||
),
|
||||
createGovernanceDecision(
|
||||
{ effect: "hide", reason: "Hidden" },
|
||||
{ effect: "deny", reason: "Denied" }
|
||||
),
|
||||
createGovernanceDecision(
|
||||
{ effect: "restrict", reason: "Restricted" },
|
||||
{ effect: "allow" }
|
||||
)
|
||||
];
|
||||
|
||||
const decision = composeGovernanceDecisions(decisions);
|
||||
expect(decision.visibility).toEqual({ effect: "hide", reason: "Hidden" });
|
||||
expect(decision.interaction).toEqual({ effect: "deny", reason: "Denied" });
|
||||
});
|
||||
|
||||
it("should combine reasons and evidence from all decisions", () => {
|
||||
const decisions = [
|
||||
createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" },
|
||||
undefined,
|
||||
["reason1", "reason2"],
|
||||
[{ evidence: "evidence1" }]
|
||||
),
|
||||
createGovernanceDecision(
|
||||
{ effect: "hide" },
|
||||
{ effect: "deny" },
|
||||
undefined,
|
||||
["reason2", "reason3"],
|
||||
[{ evidence: "evidence2" }]
|
||||
)
|
||||
];
|
||||
|
||||
const decision = composeGovernanceDecisions(decisions);
|
||||
expect(decision.reasons).toEqual(["reason1", "reason2", "reason3"]);
|
||||
expect(decision.evidence).toEqual([
|
||||
{ evidence: "evidence1" },
|
||||
{ evidence: "evidence2" }
|
||||
]);
|
||||
});
|
||||
|
||||
it("should compose transaction effects when present", () => {
|
||||
const decisions = [
|
||||
createGovernanceDecision(
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" },
|
||||
{ effect: "allow" }
|
||||
),
|
||||
createGovernanceDecision(
|
||||
{ effect: "hide" },
|
||||
{ effect: "deny" },
|
||||
{ effect: "deny", reason: "Transaction denied" }
|
||||
)
|
||||
];
|
||||
|
||||
const decision = composeGovernanceDecisions(decisions);
|
||||
expect(decision.transaction).toEqual({ effect: "deny", reason: "Transaction denied" });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user