Add hierarchical policy simulation helpers
This commit is contained in:
67
tests/test_policy_hierarchy.py
Normal file
67
tests/test_policy_hierarchy.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from govoplan_policy.backend.hierarchy import (
|
||||
PolicyRestrictionRule,
|
||||
simulate_hierarchical_policy_change,
|
||||
validate_hierarchical_policy_patch,
|
||||
)
|
||||
|
||||
|
||||
class PolicyHierarchyTests(unittest.TestCase):
|
||||
def test_parent_locks_block_lower_level_field_changes_and_limit_reenable(self) -> None:
|
||||
issues = validate_hierarchical_policy_patch(
|
||||
parent_policy={"retention_days": 30},
|
||||
patch={"retention_days": 20, "allow_lower_level_limits": {"retention_days": True}},
|
||||
field_keys=("retention_days",),
|
||||
parent_allow_lower_level_limits={"retention_days": False},
|
||||
)
|
||||
|
||||
self.assertEqual(["field_locked_by_parent", "lower_level_limit_locked_by_parent"], [issue.code for issue in issues])
|
||||
self.assertEqual(["retention_days", "retention_days"], [issue.field for issue in issues])
|
||||
|
||||
def test_restriction_rules_block_less_restrictive_patch(self) -> None:
|
||||
issues = validate_hierarchical_policy_patch(
|
||||
parent_policy={"retention_days": 30},
|
||||
patch={"retention_days": 45},
|
||||
field_keys=("retention_days",),
|
||||
parent_allow_lower_level_limits={"retention_days": True},
|
||||
restriction_rules=(
|
||||
PolicyRestrictionRule(
|
||||
field="retention_days",
|
||||
is_more_restrictive_or_equal=lambda parent, requested: int(requested) <= int(parent),
|
||||
less_restrictive_message="retention_days cannot be widened",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual(1, len(issues))
|
||||
self.assertEqual("less_restrictive_than_parent", issues[0].code)
|
||||
self.assertEqual("retention_days cannot be widened", issues[0].message)
|
||||
|
||||
def test_policy_simulation_returns_decision_payload(self) -> None:
|
||||
simulation = simulate_hierarchical_policy_change(
|
||||
parent_policy={"retention_days": 30},
|
||||
current_policy={"retention_days": 30},
|
||||
patch={"retention_days": 45},
|
||||
field_keys=("retention_days",),
|
||||
parent_allow_lower_level_limits={"retention_days": True},
|
||||
restriction_rules=(
|
||||
PolicyRestrictionRule(
|
||||
field="retention_days",
|
||||
is_more_restrictive_or_equal=lambda parent, requested: int(requested) <= int(parent),
|
||||
less_restrictive_message="retention_days cannot be widened",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.assertFalse(simulation.allowed)
|
||||
self.assertEqual(("retention_days",), simulation.changed_fields)
|
||||
self.assertIsNotNone(simulation.decision)
|
||||
self.assertEqual(("retention_days",), simulation.decision.requirements)
|
||||
self.assertEqual("Requested policy change is blocked by parent policy constraints.", simulation.decision.reason)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
30
tests/test_policy_module_contract.py
Normal file
30
tests/test_policy_module_contract.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import tomllib
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class PolicyModuleContractTests(unittest.TestCase):
|
||||
def test_policy_package_does_not_hard_require_access(self) -> None:
|
||||
project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"]
|
||||
dependencies = tuple(project["dependencies"])
|
||||
|
||||
self.assertIn("govoplan-core>=0.1.6", dependencies)
|
||||
self.assertFalse(any(item.startswith("govoplan-access") for item in dependencies))
|
||||
|
||||
def test_policy_source_does_not_import_access_implementation(self) -> None:
|
||||
offenders: list[str] = []
|
||||
for path in (ROOT / "src" / "govoplan_policy").rglob("*.py"):
|
||||
source = path.read_text(encoding="utf-8")
|
||||
if "govoplan_access" in source:
|
||||
offenders.append(str(path.relative_to(ROOT)))
|
||||
|
||||
self.assertEqual([], offenders)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user