feat(idm): govern delegation chains and timed escalation
Module Package Release / publish-packages (push) Successful in 11s
Module Package Release / publish-packages (push) Successful in 11s
This commit is contained in:
@@ -208,6 +208,82 @@ class AssignmentExpiryTests(unittest.TestCase):
|
||||
history = session.query(IdmFunctionAssignmentChangeEvent).all()
|
||||
self.assertEqual(["expired"], [item.action for item in history])
|
||||
|
||||
def test_sweep_escalates_due_review_once_without_substituting_approval(self) -> None:
|
||||
boundary = datetime(2026, 8, 22, 12, tzinfo=timezone.utc)
|
||||
with self.database.session() as session:
|
||||
session.add(
|
||||
IdmFunctionAssignmentChange(
|
||||
id="change-escalate",
|
||||
tenant_id="tenant-1",
|
||||
kind="grant",
|
||||
state="awaiting_holder",
|
||||
profile="holder_grant",
|
||||
function_id="function-1",
|
||||
organization_unit_id="unit-1",
|
||||
candidate_identity_id="identity-1",
|
||||
initiator_account_id="account-1",
|
||||
justification="Timed governed review",
|
||||
evidence=[],
|
||||
assignment_source="governance",
|
||||
required_steps=["holder"],
|
||||
completed_steps=[],
|
||||
policy_decision={
|
||||
"escalation_rules": [
|
||||
{
|
||||
"step": "holder",
|
||||
"target_function_id": "function-escalation",
|
||||
"timeout_hours": 4,
|
||||
}
|
||||
]
|
||||
},
|
||||
idempotency_key="grant-escalate-1",
|
||||
expires_at=boundary + timedelta(days=2),
|
||||
review_deadline_at=boundary - timedelta(seconds=1),
|
||||
escalation_target_function_id="function-escalation",
|
||||
metadata_={"step_approvals": {}},
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
events: list[PlatformEvent] = []
|
||||
audit_events: list[PlatformEvent] = []
|
||||
bus = EventBus()
|
||||
bus.subscribe("idm.function_change.escalated.v1", events.append)
|
||||
bus.subscribe(
|
||||
"idm.function_assignment_change.escalated",
|
||||
audit_events.append,
|
||||
)
|
||||
with self.database.SessionLocal() as session, event_bus_context(bus):
|
||||
result = self.lifecycle.process_expired(
|
||||
session,
|
||||
tenant_id="tenant-1",
|
||||
effective_at=boundary,
|
||||
)
|
||||
session.commit()
|
||||
repeated = self.lifecycle.process_expired(
|
||||
session,
|
||||
tenant_id="tenant-1",
|
||||
effective_at=boundary,
|
||||
)
|
||||
session.commit()
|
||||
|
||||
self.assertEqual(["change-escalate"], result["escalated_change_ids"])
|
||||
self.assertEqual(0, repeated["escalated_changes"])
|
||||
self.assertEqual(1, len(events))
|
||||
self.assertEqual(1, len(audit_events))
|
||||
with self.database.session() as session:
|
||||
change = session.get(IdmFunctionAssignmentChange, "change-escalate")
|
||||
self.assertEqual("escalated", change.state)
|
||||
self.assertEqual("awaiting_holder", change.escalation_from_state)
|
||||
self.assertEqual("function-escalation", change.escalation_target_function_id)
|
||||
self.assertEqual([], change.completed_steps)
|
||||
self.assertIsNone(change.resulting_assignment_id)
|
||||
history = session.query(IdmFunctionAssignmentChangeEvent).all()
|
||||
self.assertEqual(["escalated"], [item.action for item in history])
|
||||
self.assertFalse(
|
||||
history[0].details["automatic_approver_substitution"]
|
||||
)
|
||||
|
||||
def test_sweep_emits_relationship_expiry_once(self) -> None:
|
||||
boundary = datetime(2026, 8, 2, 12, tzinfo=timezone.utc)
|
||||
with self.database.session() as session:
|
||||
|
||||
Reference in New Issue
Block a user