generated from coulomb/repo-seed
feat: add registration access ui contracts
This commit is contained in:
286
tests/test_registration_access_ui.py
Normal file
286
tests/test_registration_access_ui.py
Normal file
@@ -0,0 +1,286 @@
|
||||
import unittest
|
||||
|
||||
from user_engine.adapters.local import InMemoryUserEngineStore, LocalAuthorizationCheckPort
|
||||
from user_engine.domain import (
|
||||
AccessMembershipRequirement,
|
||||
AccessProfile,
|
||||
AccessScopeType,
|
||||
FactorVerification,
|
||||
IdentityFactorType,
|
||||
OnboardingTriggerType,
|
||||
PreparedEntitlement,
|
||||
PreparedEntitlementKind,
|
||||
PreparedFactorRequirement,
|
||||
WelcomeProtocol,
|
||||
WelcomeProtocolStep,
|
||||
)
|
||||
from user_engine.service import UserEngineService
|
||||
from user_engine.testing.fixtures import FixtureIdentityClaimsAdapter, human_actor_claims
|
||||
from user_engine.ui import RegistrationAccessManagementUi, UiViewport
|
||||
|
||||
|
||||
class RegistrationAccessUiTests(unittest.TestCase):
|
||||
def test_information_architecture_and_api_contract_expose_expected_routes(self):
|
||||
ui, _, _ = _ui()
|
||||
|
||||
ia = ui.information_architecture()
|
||||
contract = ui.api_contract()
|
||||
route_ids = {route.route_id for route in contract.routes}
|
||||
|
||||
self.assertIn("registration", ia.primary_navigation)
|
||||
self.assertIn("prepared_account.review", route_ids)
|
||||
self.assertIn("access_profile.select_hat", route_ids)
|
||||
self.assertIn("admin.dashboard", route_ids)
|
||||
self.assertIn("authorization decisions", contract.adapter_boundaries)
|
||||
self.assertEqual(ia.breakpoints["mobile"]["columns"], 1)
|
||||
self.assertEqual(ia.breakpoints["desktop"]["columns"], 2)
|
||||
|
||||
def test_self_service_registration_flow_requires_terms_and_redacts_factor_values(self):
|
||||
ui, service, _ = _ui()
|
||||
actor = _actor()
|
||||
|
||||
started = ui.start_registration(
|
||||
actor,
|
||||
required_factor_types=(IdentityFactorType.EMAIL,),
|
||||
viewport=UiViewport.MOBILE,
|
||||
correlation_id="corr-ui-start",
|
||||
)
|
||||
ui.attach_factor(
|
||||
actor,
|
||||
started.session.registration_id,
|
||||
_verified_email(),
|
||||
viewport=UiViewport.MOBILE,
|
||||
correlation_id="corr-ui-factor",
|
||||
)
|
||||
blocked = ui.complete_registration(
|
||||
actor,
|
||||
started.session.registration_id,
|
||||
terms_accepted=False,
|
||||
viewport=UiViewport.MOBILE,
|
||||
correlation_id="corr-ui-blocked",
|
||||
)
|
||||
completed = ui.complete_registration(
|
||||
actor,
|
||||
started.session.registration_id,
|
||||
terms_accepted=True,
|
||||
viewport=UiViewport.MOBILE,
|
||||
correlation_id="corr-ui-complete",
|
||||
)
|
||||
html = ui.render_html(completed.screen)
|
||||
|
||||
self.assertIn("Terms and consent", blocked.screen.alerts[0])
|
||||
self.assertEqual(completed.completion.netkingdom_id, completed.completion.user.user_id)
|
||||
self.assertEqual(completed.screen.layout["min_touch_target"], 44)
|
||||
self.assertIn("role='main'", html)
|
||||
self.assertIn("data-viewport='mobile'", html)
|
||||
self.assertNotIn("sample.user@example.test", html)
|
||||
self.assertNotIn(
|
||||
"sample.user@example.test",
|
||||
repr([event.payload for event in service.outbox_events()]),
|
||||
)
|
||||
|
||||
def test_prepared_rights_can_be_reviewed_accepted_or_dismissed(self):
|
||||
ui, service, _ = _ui()
|
||||
actor = _actor()
|
||||
prepared = service.prepare_account(
|
||||
actor,
|
||||
tenant="tenant:coulomb",
|
||||
required_factor_matches=(
|
||||
PreparedFactorRequirement(
|
||||
factor_type=IdentityFactorType.EMAIL,
|
||||
normalized_value="sample.user@example.test",
|
||||
),
|
||||
),
|
||||
entitlements=(
|
||||
PreparedEntitlement(
|
||||
kind=PreparedEntitlementKind.MEMBERSHIP,
|
||||
tenant="tenant:coulomb",
|
||||
scope_type="realm",
|
||||
scope_id="realm:citadel",
|
||||
role="member",
|
||||
),
|
||||
),
|
||||
display_name="Prepared Member",
|
||||
primary_email="sample.user@example.test",
|
||||
correlation_id="corr-ui-prepare",
|
||||
)
|
||||
registration = _complete_registration(service, actor)
|
||||
|
||||
review = ui.prepared_rights_review(
|
||||
actor,
|
||||
registration.session.registration_id,
|
||||
viewport=UiViewport.DESKTOP,
|
||||
)
|
||||
dismissed = ui.deny_prepared_claim(
|
||||
prepared.prepared_account_id,
|
||||
viewport=UiViewport.DESKTOP,
|
||||
)
|
||||
accepted = ui.accept_prepared_claim(
|
||||
actor,
|
||||
registration.session.registration_id,
|
||||
prepared.prepared_account_id,
|
||||
viewport=UiViewport.DESKTOP,
|
||||
correlation_id="corr-ui-claim",
|
||||
)
|
||||
html = ui.render_html(review)
|
||||
|
||||
self.assertIn("denied_by_user", repr(dismissed))
|
||||
self.assertEqual(accepted.claim.prepared_account.claimed_by_user_id, accepted.claim.user.user_id)
|
||||
self.assertIn("Prepared Member", html)
|
||||
self.assertIn("<redacted>", html)
|
||||
self.assertNotIn("sample.user@example.test", html)
|
||||
|
||||
def test_hat_selection_view_selects_active_context_without_policy_details(self):
|
||||
ui, service, store = _ui()
|
||||
actor = _actor()
|
||||
registration = _complete_registration(service, actor)
|
||||
service.add_membership(
|
||||
actor,
|
||||
registration.user.user_id,
|
||||
tenant="tenant:coulomb",
|
||||
scope_type="realm",
|
||||
scope_id="realm:citadel",
|
||||
kind="operator",
|
||||
correlation_id="corr-ui-membership",
|
||||
)
|
||||
profile = service.register_access_profile(
|
||||
actor,
|
||||
AccessProfile(
|
||||
tenant="tenant:coulomb",
|
||||
display_name="Realm Operator",
|
||||
hat="operator",
|
||||
scope_type=AccessScopeType.REALM,
|
||||
scope_id="realm:citadel",
|
||||
realm_id="realm:citadel",
|
||||
service_id="app.demo",
|
||||
membership_requirements=(
|
||||
AccessMembershipRequirement(
|
||||
scope_type="realm",
|
||||
scope_id="realm:citadel",
|
||||
kind="operator",
|
||||
),
|
||||
),
|
||||
required_factor_types=(IdentityFactorType.EMAIL,),
|
||||
claims={"internal_policy_hint": "do-not-render"},
|
||||
),
|
||||
correlation_id="corr-ui-profile",
|
||||
)
|
||||
|
||||
before = ui.hat_selection_view(
|
||||
actor,
|
||||
registration.user.user_id,
|
||||
tenant="tenant:coulomb",
|
||||
viewport=UiViewport.DESKTOP,
|
||||
)
|
||||
selected = ui.select_hat(
|
||||
actor,
|
||||
registration.user.user_id,
|
||||
profile.access_profile_id,
|
||||
viewport=UiViewport.DESKTOP,
|
||||
correlation_id="corr-ui-select-hat",
|
||||
)
|
||||
active = store.active_access_context(registration.user.user_id, "tenant:coulomb")
|
||||
html = ui.render_html(selected)
|
||||
|
||||
self.assertIn("none", ui.render_html(before))
|
||||
self.assertEqual(active.hat, "operator")
|
||||
self.assertIn("Realm Operator", html)
|
||||
self.assertNotIn("do-not-render", html)
|
||||
|
||||
def test_admin_dashboard_redacts_sensitive_setup_details(self):
|
||||
ui, service, _ = _ui()
|
||||
actor = _actor()
|
||||
session = service.me(human_actor_claims(), correlation_id="corr-ui-me")
|
||||
service.register_welcome_protocol(
|
||||
session.actor,
|
||||
WelcomeProtocol(
|
||||
tenant="tenant:coulomb",
|
||||
name="Blocked Welcome",
|
||||
trigger_type=OnboardingTriggerType.MANUAL,
|
||||
steps=(
|
||||
WelcomeProtocolStep(
|
||||
step_key="external",
|
||||
title="External",
|
||||
subsystem="crm",
|
||||
requires_subsystem_callback=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
correlation_id="corr-ui-protocol",
|
||||
)
|
||||
service.prepare_account(
|
||||
actor,
|
||||
tenant="tenant:coulomb",
|
||||
required_factor_matches=(
|
||||
PreparedFactorRequirement(
|
||||
factor_type=IdentityFactorType.EMAIL,
|
||||
normalized_value="sample.user@example.test",
|
||||
),
|
||||
),
|
||||
entitlements=(
|
||||
PreparedEntitlement(
|
||||
kind=PreparedEntitlementKind.ONBOARDING_JOURNEY,
|
||||
tenant="tenant:coulomb",
|
||||
onboarding_journey="welcome-demo",
|
||||
),
|
||||
),
|
||||
primary_email="sample.user@example.test",
|
||||
correlation_id="corr-ui-prepare",
|
||||
)
|
||||
dashboard = ui.admin_dashboard(
|
||||
actor,
|
||||
tenant="tenant:coulomb",
|
||||
viewport=UiViewport.DESKTOP,
|
||||
)
|
||||
html = ui.render_html(dashboard)
|
||||
|
||||
self.assertEqual(dashboard.layout["columns"], 2)
|
||||
self.assertIn("Prepared Accounts", html)
|
||||
self.assertIn("Onboarding", html)
|
||||
self.assertNotIn("sample.user@example.test", html)
|
||||
self.assertIn("role='navigation'", html)
|
||||
self.assertIn("aria-label='Sections'", html)
|
||||
|
||||
|
||||
def _ui():
|
||||
store = InMemoryUserEngineStore()
|
||||
service = UserEngineService(
|
||||
store=store,
|
||||
identity_adapter=FixtureIdentityClaimsAdapter(),
|
||||
authorization=LocalAuthorizationCheckPort(),
|
||||
)
|
||||
return RegistrationAccessManagementUi(service), service, store
|
||||
|
||||
|
||||
def _actor():
|
||||
return FixtureIdentityClaimsAdapter().normalize(
|
||||
human_actor_claims(subject="sample-user", tenant="tenant:coulomb")
|
||||
)
|
||||
|
||||
|
||||
def _verified_email() -> FactorVerification:
|
||||
return FactorVerification(
|
||||
factor_type=IdentityFactorType.EMAIL,
|
||||
normalized_value="sample.user@example.test",
|
||||
display_value="sample.user@example.test",
|
||||
source_system="fixture-email",
|
||||
)
|
||||
|
||||
|
||||
def _complete_registration(service: UserEngineService, actor):
|
||||
session = service.start_registration(actor, correlation_id="corr-ui-reg-start")
|
||||
service.attach_registration_factor(
|
||||
actor,
|
||||
session.registration_id,
|
||||
_verified_email(),
|
||||
correlation_id="corr-ui-reg-factor",
|
||||
)
|
||||
return service.complete_registration(
|
||||
actor,
|
||||
session.registration_id,
|
||||
correlation_id="corr-ui-reg-complete",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user