"""Reusable conformance checks for user-engine store adapters.""" from __future__ import annotations from collections.abc import Callable from typing import Any from unittest import TestCase from user_engine.domain import ( Account, AccountStatus, AccessMembershipRequirement, AccessProfile, AccessScopeType, ActiveAccessContext, Actor, Application, ApplicationBinding, AttributeDefinition, AuditRecord, Catalog, CatalogLifecycle, ExternalIdentity, FamilyInvitation, IdentityFactor, IdentityFactorType, InvitationStatus, Membership, Mutability, OnboardingJourney, OnboardingStep, OnboardingTriggerType, OutboxEvent, PreparedAccount, PreparedEntitlement, PreparedEntitlementKind, PreparedFactorRequirement, PrincipalType, ProfileScope, ProfileValue, ProjectionType, RegistrationSession, RegistrationStatus, Sensitivity, TenantAccount, User, Visibility, WelcomeProtocol, WelcomeProtocolStep, ) from user_engine.migrations import ( LATEST_SCHEMA_VERSION, USER_ENGINE_RECORD_COUNT_KEYS, migration_manifest, validate_migration_manifest, ) from user_engine.ports import UserEngineStore StoreFactory = Callable[[], UserEngineStore] TENANT = "tenant:store-conformance" USER_ID = "usr_store_conformance" RAW_FACTOR_VALUE = "store.user@example.test" PROFILE_SECRET_VALUE = "quiet-secret-profile-value" def assert_user_engine_migration_contract(testcase: TestCase) -> None: """Assert the migration manifest is ordered and provider-safe.""" testcase.assertEqual(validate_migration_manifest(), ()) manifest = migration_manifest() testcase.assertGreaterEqual(len(manifest), 1) testcase.assertEqual(manifest[-1].version, LATEST_SCHEMA_VERSION) testcase.assertEqual( manifest[0].sql_path, "migrations/postgres/0001_user_engine_store.sql", ) def assert_user_engine_store_conformance( testcase: TestCase, store_factory: StoreFactory, ) -> None: """Run the core durable-store behavior contract for one store factory.""" assert_user_engine_migration_contract(testcase) _assert_readiness_contract(testcase, store_factory) _assert_save_read_and_query_contract(testcase, store_factory) _assert_transaction_rollback_contract(testcase, store_factory) _assert_outbox_ordering_contract(testcase, store_factory) _assert_diagnostics_contract(testcase, store_factory) def reference_store_records(store: UserEngineStore) -> dict[str, Any]: """Write and return a representative record for every store record type.""" return _write_reference_records(store) def _assert_readiness_contract(testcase: TestCase, store_factory: StoreFactory) -> None: store = store_factory() if store.schema_version is None: testcase.assertFalse(store.ready) store.migrate() testcase.assertTrue(store.ready) testcase.assertEqual(store.schema_version, LATEST_SCHEMA_VERSION) store.migrate() testcase.assertTrue(store.ready) testcase.assertEqual(store.schema_version, LATEST_SCHEMA_VERSION) def _assert_save_read_and_query_contract( testcase: TestCase, store_factory: StoreFactory, ) -> None: store = _migrated(store_factory) records = _write_reference_records(store) user = records["user"] account = records["account"] identity = records["identity"] tenant_account = records["tenant_account"] membership = records["membership"] application = records["application"] binding = records["binding"] catalog = records["catalog"] invitation = records["invitation"] registration = records["registration"] factor = records["factor"] prepared_account = records["prepared_account"] access_profile = records["access_profile"] access_context = records["access_context"] welcome_protocol = records["welcome_protocol"] onboarding_journey = records["onboarding_journey"] profile_value = records["profile_value"] testcase.assertEqual(store.user(USER_ID), user) testcase.assertEqual(store.user_account(USER_ID), account) testcase.assertEqual(store.find_identity(identity.issuer, identity.subject), identity) testcase.assertEqual(store.identities_for_user(USER_ID), (identity,)) testcase.assertEqual(store.tenant_account(TENANT, USER_ID), tenant_account) testcase.assertEqual(store.memberships_for_user(USER_ID), (membership,)) testcase.assertEqual(store.memberships_for_user(USER_ID, tenant=TENANT), (membership,)) testcase.assertEqual(store.memberships_for_tenant(TENANT), (membership,)) testcase.assertEqual(store.application(application.application_id), application) testcase.assertEqual(store.binding(binding.application_id), binding) testcase.assertEqual(store.catalog(catalog.catalog_id), catalog) testcase.assertEqual(store.all_catalogs(), (catalog,)) testcase.assertEqual(store.family_invitation(invitation.invitation_id), invitation) testcase.assertEqual(store.family_invitations_for_user(USER_ID), (invitation,)) testcase.assertEqual(store.registration_session(registration.registration_id), registration) testcase.assertEqual(store.all_registration_sessions(), (registration,)) testcase.assertEqual(store.identity_factor(factor.factor_id), factor) testcase.assertEqual(store.factors_for_registration(registration.registration_id), (factor,)) testcase.assertEqual(store.factors_for_user(USER_ID), (factor,)) testcase.assertEqual( store.prepared_account(prepared_account.prepared_account_id), prepared_account, ) testcase.assertEqual(store.prepared_accounts_for_tenant(TENANT), (prepared_account,)) testcase.assertEqual(store.access_profile(access_profile.access_profile_id), access_profile) testcase.assertEqual(store.access_profiles_for_tenant(TENANT), (access_profile,)) testcase.assertEqual(store.active_access_context(USER_ID, TENANT), access_context) testcase.assertEqual(store.active_access_contexts_for_tenant(TENANT), (access_context,)) testcase.assertEqual(store.welcome_protocol(welcome_protocol.protocol_id), welcome_protocol) testcase.assertEqual(store.welcome_protocols_for_tenant(TENANT), (welcome_protocol,)) testcase.assertEqual( store.onboarding_journey(onboarding_journey.journey_id), onboarding_journey, ) testcase.assertEqual(store.onboarding_journeys_for_user(USER_ID), (onboarding_journey,)) testcase.assertEqual( store.onboarding_journeys_for_user(USER_ID, tenant=TENANT), (onboarding_journey,), ) testcase.assertEqual(store.onboarding_journeys_for_tenant(TENANT), (onboarding_journey,)) testcase.assertEqual(store.values_for_user(USER_ID), (profile_value,)) replacement = User( user_id=USER_ID, display_name="Replacement User", primary_email=RAW_FACTOR_VALUE, created_at=user.created_at, ) store.save_user(replacement) testcase.assertEqual(store.user(USER_ID), replacement) def _assert_transaction_rollback_contract( testcase: TestCase, store_factory: StoreFactory, ) -> None: store = _migrated(store_factory) actor = _actor() with testcase.assertRaises(RuntimeError): with store.transaction(): store.save_user(User(user_id="usr_rollback", display_name="Rollback")) store.append_audit( AuditRecord( audit_id="aud_rollback", actor=actor, action="store.write", subject="usr_rollback", tenant=TENANT, correlation_id="corr-rollback", summary="rolled back audit", ) ) store.append_outbox( OutboxEvent( event_id="evt_rollback", event_type="store.rollback", aggregate_id="usr_rollback", payload={"result": "rollback"}, tenant=TENANT, correlation_id="corr-rollback", ) ) raise RuntimeError("force rollback") testcase.assertIsNone(store.user("usr_rollback")) testcase.assertEqual(store.audit_log(), ()) testcase.assertEqual(store.pending_outbox(), ()) def _assert_outbox_ordering_contract( testcase: TestCase, store_factory: StoreFactory, ) -> None: store = _migrated(store_factory) events = ( OutboxEvent( event_id="evt_order_1", event_type="store.first", aggregate_id=USER_ID, payload={"sequence": 1}, tenant=TENANT, correlation_id="corr-order", ), OutboxEvent( event_id="evt_order_2", event_type="store.second", aggregate_id=USER_ID, payload={"sequence": 2}, tenant=TENANT, correlation_id="corr-order", ), ) for event in events: store.append_outbox(event) testcase.assertEqual(store.pending_outbox(), events) def _assert_diagnostics_contract( testcase: TestCase, store_factory: StoreFactory, ) -> None: store = _migrated(store_factory) _write_reference_records(store) counts = dict(store.record_counts()) testcase.assertEqual(set(counts), set(USER_ENGINE_RECORD_COUNT_KEYS)) testcase.assertTrue(all(isinstance(value, int) for value in counts.values())) testcase.assertEqual(counts["bindings"], 1) testcase.assertEqual(counts["pending_outbox_events"], 1) diagnostics_text = repr(counts) testcase.assertNotIn(RAW_FACTOR_VALUE, diagnostics_text) testcase.assertNotIn(PROFILE_SECRET_VALUE, diagnostics_text) def _migrated(store_factory: StoreFactory) -> UserEngineStore: store = store_factory() store.migrate() return store def _write_reference_records(store: UserEngineStore) -> dict[str, Any]: actor = _actor() user = User( user_id=USER_ID, display_name="Store Conformance User", primary_email=RAW_FACTOR_VALUE, ) account = Account( account_id="acct_store_conformance", user_id=USER_ID, status=AccountStatus.ACTIVE, ) identity = ExternalIdentity( identity_id="eid_store_conformance", user_id=USER_ID, issuer="https://issuer.example.test", subject="store-conformance", provider="fixture", ) tenant_account = TenantAccount(user_id=USER_ID, tenant=TENANT) membership = Membership( membership_id="mbr_store_conformance", user_id=USER_ID, tenant=TENANT, scope_type="team", scope_id="team:store-conformance", kind="member", ) application = Application( application_id="app.store-conformance", display_name="Store Conformance", owner="team:store-conformance", allowed_profile_scopes=(ProfileScope.GLOBAL, ProfileScope.APPLICATION), allowed_projection_types=(ProjectionType.APPLICATION_RUNTIME,), ) binding = ApplicationBinding( application_id=application.application_id, oidc_client_id="store-conformance-client", protected_system_id="store-conformance.service", catalog_namespaces=("store",), event_source="store-conformance", deployment_ref="local", ) catalog = Catalog( catalog_id="cat_store_conformance", namespace="store", version="1.0.0", owning_application_id=application.application_id, lifecycle=CatalogLifecycle.ACTIVE, attributes=( AttributeDefinition( key="store.secret", value_type="string", scope=ProfileScope.GLOBAL, sensitivity=Sensitivity.SECRET, visibility=(Visibility.USER,), mutability=(Mutability.USER,), ), ), ) invitation = FamilyInvitation( invitation_id="finv_store_conformance", tenant=TENANT, family_scope_id="family:store-conformance", application_id=application.application_id, user_id=USER_ID, primary_email=RAW_FACTOR_VALUE, role="adult", status=InvitationStatus.PENDING, invited_by=actor.subject, ) registration = RegistrationSession( tenant=TENANT, registration_id="reg_store_conformance", status=RegistrationStatus.FACTOR_VERIFIED, required_factor_types=(IdentityFactorType.EMAIL,), verified_factor_ids=("fac_store_conformance",), user_id=USER_ID, netkingdom_id="nk-store-conformance", started_by_subject=actor.subject, correlation_id="corr-store", ) factor = IdentityFactor( factor_id="fac_store_conformance", factor_type=IdentityFactorType.EMAIL, normalized_value=RAW_FACTOR_VALUE, registration_id=registration.registration_id, user_id=USER_ID, display_value="s***@example.test", ) prepared_account = PreparedAccount( tenant=TENANT, prepared_account_id="pacct_store_conformance", required_factor_matches=( PreparedFactorRequirement( factor_type=IdentityFactorType.EMAIL, normalized_value=RAW_FACTOR_VALUE, ), ), entitlements=( PreparedEntitlement( kind=PreparedEntitlementKind.MEMBERSHIP, tenant=TENANT, scope_type="team", scope_id="team:store-conformance", role="member", ), ), display_name="Prepared Store User", primary_email=RAW_FACTOR_VALUE, prepared_by_subject=actor.subject, correlation_id="corr-store", ) access_profile = AccessProfile( tenant=TENANT, display_name="Store Member", hat="member", access_profile_id="apf_store_conformance", scope_type=AccessScopeType.TENANT, scope_id=TENANT, membership_requirements=( AccessMembershipRequirement( scope_type="team", scope_id="team:store-conformance", kind="member", ), ), required_factor_types=(IdentityFactorType.EMAIL,), profile_defaults={"store.secret": PROFILE_SECRET_VALUE}, claims={"role": "member"}, ) access_context = ActiveAccessContext( active_context_id="actx_store_conformance", user_id=USER_ID, tenant=TENANT, access_profile_id=access_profile.access_profile_id, hat=access_profile.hat, scope_type=AccessScopeType.TENANT, scope_id=TENANT, membership_ids=(membership.membership_id,), factor_ids=(factor.factor_id,), selected_by_subject=actor.subject, ) welcome_protocol = WelcomeProtocol( protocol_id="wpro_store_conformance", tenant=TENANT, name="Store Welcome", trigger_type=OnboardingTriggerType.REGISTRATION, steps=( WelcomeProtocolStep( step_key="profile", title="Complete profile", subsystem="user-account", ), ), ) onboarding_journey = OnboardingJourney( journey_id="ojrn_store_conformance", tenant=TENANT, user_id=USER_ID, protocol_id=welcome_protocol.protocol_id, trigger_type=OnboardingTriggerType.REGISTRATION, steps=( OnboardingStep( step_key="profile", title="Complete profile", subsystem="user-account", ), ), source_id=registration.registration_id, source_event_type="registration.completed", correlation_id="corr-store", ) profile_value = ProfileValue( user_id=USER_ID, attribute_key="store.secret", value=PROFILE_SECRET_VALUE, scope=ProfileScope.GLOBAL, ) audit_record = AuditRecord( audit_id="aud_store_conformance", actor=actor, action="store.write", subject=USER_ID, tenant=TENANT, correlation_id="corr-store", summary="store conformance", ) outbox_event = OutboxEvent( event_id="evt_store_conformance", event_type="store.changed", aggregate_id=USER_ID, payload={"kind": "store-conformance"}, tenant=TENANT, correlation_id="corr-store", ) store.save_user(user) store.save_account(account) store.save_identity(identity) store.save_tenant_account(tenant_account) store.save_membership(membership) assert membership.tenant in store.membership_tenants() store.save_application(application) store.save_binding(binding) store.save_catalog(catalog) store.save_family_invitation(invitation) store.save_registration_session(registration) store.save_identity_factor(factor) store.save_prepared_account(prepared_account) store.save_access_profile(access_profile) store.save_active_access_context(access_context) store.save_welcome_protocol(welcome_protocol) store.save_onboarding_journey(onboarding_journey) store.save_profile_value(profile_value) store.append_audit(audit_record) store.append_outbox(outbox_event) return { "user": user, "account": account, "identity": identity, "tenant_account": tenant_account, "membership": membership, "application": application, "binding": binding, "catalog": catalog, "invitation": invitation, "registration": registration, "factor": factor, "prepared_account": prepared_account, "access_profile": access_profile, "access_context": access_context, "welcome_protocol": welcome_protocol, "onboarding_journey": onboarding_journey, "profile_value": profile_value, "audit_record": audit_record, "outbox_event": outbox_event, } def _actor() -> Actor: return Actor( issuer="https://issuer.example.test", subject="store-conformance", tenant=TENANT, principal_type=PrincipalType.HUMAN, audience=("user-engine",), roles=("user",), scopes=("profile",), )