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()