"""Multi-organization access integration tests. Covers cross-org isolation and role-based access control scenarios. """ import pytest import uuid from tests.integration.client.base import ApiError from gatehouse_app.utils.constants import OrganizationRole def assert_success(response: dict, message_contains: str = "") -> dict: data = response.get("data", {}) assert response.get("success") is not False if message_contains: assert message_contains.lower() in response.get("message", "").lower() return data def assert_error(exc: ApiError, expected_status: int, expected_error_type: str | None = None): assert exc.status_code == expected_status if expected_error_type: assert exc.error_type == expected_error_type class TestMultiOrgAccess: """Test users in multiple organizations.""" def test_user_in_multiple_orgs_positive(self, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: MULTIORG-01 — User in multiple orgs with different roles. WHAT: Create a user who is ADMIN in Org A and MEMBER in Org B, then GET /users/me/organizations. WHY: The org selector must show all orgs with correct roles. EXPECTED: 200 OK with both orgs and correct roles. """ user = create_test_user(password="MyPassword123!") org_a = create_test_org(name="Org A", slug=f"org-a-{uuid.uuid4().hex[:6]}") org_b = create_test_org(name="Org B", slug=f"org-b-{uuid.uuid4().hex[:6]}") create_test_membership(user["id"], org_a["id"], OrganizationRole.ADMIN) create_test_membership(user["id"], org_b["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=user["email"], password="MyPassword123!") result = integration_client.users.get_my_organizations() data = assert_success(result) orgs = data.get("organizations", []) assert len(orgs) == 2, f"Expected 2 orgs, got {len(orgs)}" def test_cross_org_admin_operation_blocked_negative(self, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: MULTIORG-02 — Cross-org admin operation blocked. WHAT: User is ADMIN in Org A and MEMBER in Org B. Attempt to perform an admin operation in Org B. WHY: Role scopes must be per-organization. EXPECTED: 403 Forbidden. """ user = create_test_user(password="MyPassword123!") org_a = create_test_org(name="Org A", slug=f"org-a-{uuid.uuid4().hex[:6]}") org_b = create_test_org(name="Org B", slug=f"org-b-{uuid.uuid4().hex[:6]}") create_test_membership(user["id"], org_a["id"], OrganizationRole.ADMIN) create_test_membership(user["id"], org_b["id"], OrganizationRole.MEMBER) victim = create_test_user(password="VictimPass123!") create_test_membership(victim["id"], org_b["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=user["email"], password="MyPassword123!") with pytest.raises(ApiError) as exc_info: integration_client.orgs.remove_member(org_b["id"], victim["id"]) assert exc_info.value.status_code == 403 def test_list_memberships_positive(self, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: MULTIORG-04 — List memberships across orgs. WHAT: User in multiple orgs calls GET /users/me/memberships. WHY: The memberships page shows orgs, departments, principals. EXPECTED: 200 OK with orgs array. """ user = create_test_user(password="MyPassword123!") org_a = create_test_org(name="Org A", slug=f"org-a-{uuid.uuid4().hex[:6]}") org_b = create_test_org(name="Org B", slug=f"org-b-{uuid.uuid4().hex[:6]}") create_test_membership(user["id"], org_a["id"], OrganizationRole.ADMIN) create_test_membership(user["id"], org_b["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=user["email"], password="MyPassword123!") result = integration_client.users.get_my_memberships() data = assert_success(result) assert "orgs" in data