Expose provider-neutral identity reconciliation
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

This commit is contained in:
tegwick 2026-07-29 23:41:47 +02:00
parent de2c02dc6d
commit a336f594e7
4 changed files with 189 additions and 18 deletions

View file

@ -2,6 +2,7 @@ import io
import json
import unittest
from unittest.mock import patch
from urllib.error import URLError
from user_engine.adapters.provisioning import HTTPIdentityProvisioningAdapter
from user_engine.ports import ProvisioningRequest
@ -41,3 +42,66 @@ class ProvisioningAdapterTests(unittest.TestCase):
request = opener.call_args.args[0]
self.assertEqual("Bearer secret", request.headers["Authorization"])
self.assertIn(b"idem-1234567890123456", request.data)
@patch("user_engine.adapters.provisioning.urlopen")
def test_drift_and_reconcile_use_provider_neutral_desired_state(self, opener):
opener.side_effect = [
Response(json.dumps({
"provider": "netkingdom-lldap",
"external_subject": "person",
"status": "drifted",
"drift": ["status:mismatch"],
"changed": [],
}).encode()),
Response(json.dumps({
"provider": "netkingdom-lldap",
"external_subject": "person",
"status": "reconciled",
"drift": [],
"changed": ["group:removed:netkingdom-suspended"],
}).encode()),
]
adapter = HTTPIdentityProvisioningAdapter(
base_url="http://provisioner", bearer_token="secret"
)
request = ProvisioningRequest(
user_id="person",
tenant="tenant:friendly:binky",
primary_email="person@example.test",
display_name="Person",
idempotency_key="idem-1234567890123456",
correlation_id="corr-1",
roles=("user",),
)
self.assertEqual(
("status:mismatch",),
adapter.drift(request, external_subject="person").drift,
)
reconciled = adapter.reconcile(request, external_subject="person")
self.assertEqual("reconciled", reconciled.status)
self.assertEqual(
("group:removed:netkingdom-suspended",), reconciled.changed
)
@patch("user_engine.adapters.provisioning.urlopen")
def test_idempotent_request_retries_one_transient_provider_failure(self, opener):
opener.side_effect = [
URLError("temporary"),
Response(json.dumps({
"provider": "netkingdom-lldap",
"external_subject": "person",
"status": "active",
}).encode()),
]
adapter = HTTPIdentityProvisioningAdapter(
base_url="http://provisioner",
bearer_token="secret",
retry_delay=0,
)
result = adapter.reactivate(
external_subject="person",
idempotency_key="idem-1234567890123456",
correlation_id="corr-1",
)
self.assertEqual("active", result.status)
self.assertEqual(2, opener.call_count)