Files
syndarix/backend/tests/crud/test_organization.py
Felipe Cardoso 9f88736d13 Add comprehensive tests for schemas, validators, and exception handlers
- **Schemas:** Introduce unit tests for `OrganizationBase`, `OrganizationCreate`, and `OrganizationUpdate` schemas. Validate edge cases for slug and name validation.
- **Validators:** Add tests for `validate_password_strength`, `validate_phone_number`, `validate_email_format`, and `validate_slug`. Cover edge cases, boundary conditions, and defensive code paths.
- **Exception Handlers:** Ensure proper error handling in organization, user, and session CRUD operations. Mock database errors and validate exception responses.
- Include test cases to verify robust behavior, normalization, and failure scenarios across schema and validation logic.
2025-11-02 07:56:23 +01:00

1136 lines
44 KiB
Python

# tests/crud/test_organization_async.py
"""
Comprehensive tests for async organization CRUD operations.
"""
import pytest
from uuid import uuid4
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from unittest.mock import patch, AsyncMock, MagicMock
from app.crud.organization import organization as organization_crud
from app.models.organization import Organization
from app.models.user_organization import UserOrganization, OrganizationRole
from app.models.user import User
from app.schemas.organizations import OrganizationCreate, OrganizationUpdate
class TestGetBySlug:
"""Tests for get_by_slug method."""
@pytest.mark.asyncio
async def test_get_by_slug_success(self, async_test_db):
"""Test successfully getting an organization by slug."""
test_engine, AsyncTestingSessionLocal = async_test_db
# Create test organization
async with AsyncTestingSessionLocal() as session:
org = Organization(
name="Test Org",
slug="test-org",
description="Test description"
)
session.add(org)
await session.commit()
org_id = org.id
# Get by slug
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.get_by_slug(session, slug="test-org")
assert result is not None
assert result.id == org_id
assert result.slug == "test-org"
@pytest.mark.asyncio
async def test_get_by_slug_not_found(self, async_test_db):
"""Test getting non-existent organization by slug."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.get_by_slug(session, slug="nonexistent")
assert result is None
class TestCreate:
"""Tests for create method."""
@pytest.mark.asyncio
async def test_create_success(self, async_test_db):
"""Test successfully creating an organization_crud."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org_in = OrganizationCreate(
name="New Org",
slug="new-org",
description="New organization",
is_active=True,
settings={"key": "value"}
)
result = await organization_crud.create(session, obj_in=org_in)
assert result.name == "New Org"
assert result.slug == "new-org"
assert result.description == "New organization"
assert result.is_active is True
assert result.settings == {"key": "value"}
@pytest.mark.asyncio
async def test_create_duplicate_slug(self, async_test_db):
"""Test creating organization with duplicate slug raises error."""
test_engine, AsyncTestingSessionLocal = async_test_db
# Create first org
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Org 1", slug="duplicate-slug")
session.add(org1)
await session.commit()
# Try to create second with same slug
async with AsyncTestingSessionLocal() as session:
org_in = OrganizationCreate(
name="Org 2",
slug="duplicate-slug"
)
with pytest.raises(ValueError, match="already exists"):
await organization_crud.create(session, obj_in=org_in)
@pytest.mark.asyncio
async def test_create_without_settings(self, async_test_db):
"""Test creating organization without settings (defaults to empty dict)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org_in = OrganizationCreate(
name="No Settings Org",
slug="no-settings"
)
result = await organization_crud.create(session, obj_in=org_in)
assert result.settings == {}
class TestGetMultiWithFilters:
"""Tests for get_multi_with_filters method."""
@pytest.mark.asyncio
async def test_get_multi_with_filters_no_filters(self, async_test_db):
"""Test getting organizations without any filters."""
test_engine, AsyncTestingSessionLocal = async_test_db
# Create test organizations
async with AsyncTestingSessionLocal() as session:
for i in range(5):
org = Organization(name=f"Org {i}", slug=f"org-{i}")
session.add(org)
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs, total = await organization_crud.get_multi_with_filters(session)
assert total == 5
assert len(orgs) == 5
@pytest.mark.asyncio
async def test_get_multi_with_filters_is_active(self, async_test_db):
"""Test filtering by is_active."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Active", slug="active", is_active=True)
org2 = Organization(name="Inactive", slug="inactive", is_active=False)
session.add_all([org1, org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs, total = await organization_crud.get_multi_with_filters(
session,
is_active=True
)
assert total == 1
assert orgs[0].name == "Active"
@pytest.mark.asyncio
async def test_get_multi_with_filters_search(self, async_test_db):
"""Test searching organizations."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Tech Corp", slug="tech-corp", description="Technology")
org2 = Organization(name="Food Inc", slug="food-inc", description="Restaurant")
session.add_all([org1, org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs, total = await organization_crud.get_multi_with_filters(
session,
search="tech"
)
assert total == 1
assert orgs[0].name == "Tech Corp"
@pytest.mark.asyncio
async def test_get_multi_with_filters_pagination(self, async_test_db):
"""Test pagination."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
for i in range(10):
org = Organization(name=f"Org {i}", slug=f"org-{i}")
session.add(org)
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs, total = await organization_crud.get_multi_with_filters(
session,
skip=2,
limit=3
)
assert total == 10
assert len(orgs) == 3
@pytest.mark.asyncio
async def test_get_multi_with_filters_sorting(self, async_test_db):
"""Test sorting."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="B Org", slug="b-org")
org2 = Organization(name="A Org", slug="a-org")
session.add_all([org1, org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs, total = await organization_crud.get_multi_with_filters(
session,
sort_by="name",
sort_order="asc"
)
assert orgs[0].name == "A Org"
assert orgs[1].name == "B Org"
class TestGetMemberCount:
"""Tests for get_member_count method."""
@pytest.mark.asyncio
async def test_get_member_count_success(self, async_test_db, async_test_user):
"""Test getting member count for organization_crud."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
# Add 1 active member
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
count = await organization_crud.get_member_count(session, organization_id=org_id)
assert count == 1
@pytest.mark.asyncio
async def test_get_member_count_no_members(self, async_test_db):
"""Test getting member count for organization with no members."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Empty Org", slug="empty-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
count = await organization_crud.get_member_count(session, organization_id=org_id)
assert count == 0
class TestAddUser:
"""Tests for add_user method."""
@pytest.mark.asyncio
async def test_add_user_success(self, async_test_db, async_test_user):
"""Test successfully adding a user to organization_crud."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.add_user(
session,
organization_id=org_id,
user_id=async_test_user.id,
role=OrganizationRole.ADMIN
)
assert result.user_id == async_test_user.id
assert result.organization_id == org_id
assert result.role == OrganizationRole.ADMIN
assert result.is_active is True
@pytest.mark.asyncio
async def test_add_user_already_active_member(self, async_test_db, async_test_user):
"""Test adding user who is already an active member raises error."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
with pytest.raises(ValueError, match="already a member"):
await organization_crud.add_user(
session,
organization_id=org_id,
user_id=async_test_user.id
)
@pytest.mark.asyncio
async def test_add_user_reactivate_inactive(self, async_test_db, async_test_user):
"""Test adding user who was previously inactive reactivates them."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=False
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.add_user(
session,
organization_id=org_id,
user_id=async_test_user.id,
role=OrganizationRole.ADMIN
)
assert result.is_active is True
assert result.role == OrganizationRole.ADMIN
class TestRemoveUser:
"""Tests for remove_user method."""
@pytest.mark.asyncio
async def test_remove_user_success(self, async_test_db, async_test_user):
"""Test successfully removing a user from organization_crud."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.remove_user(
session,
organization_id=org_id,
user_id=async_test_user.id
)
assert result is True
# Verify soft delete
async with AsyncTestingSessionLocal() as session:
stmt = select(UserOrganization).where(
UserOrganization.user_id == async_test_user.id,
UserOrganization.organization_id == org_id
)
result = await session.execute(stmt)
user_org = result.scalar_one_or_none()
assert user_org.is_active is False
@pytest.mark.asyncio
async def test_remove_user_not_found(self, async_test_db):
"""Test removing non-existent user returns False."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.remove_user(
session,
organization_id=org_id,
user_id=uuid4()
)
assert result is False
class TestUpdateUserRole:
"""Tests for update_user_role method."""
@pytest.mark.asyncio
async def test_update_user_role_success(self, async_test_db, async_test_user):
"""Test successfully updating user role."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.update_user_role(
session,
organization_id=org_id,
user_id=async_test_user.id,
role=OrganizationRole.ADMIN,
custom_permissions="custom"
)
assert result.role == OrganizationRole.ADMIN
assert result.custom_permissions == "custom"
@pytest.mark.asyncio
async def test_update_user_role_not_found(self, async_test_db):
"""Test updating role for non-existent user returns None."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
result = await organization_crud.update_user_role(
session,
organization_id=org_id,
user_id=uuid4(),
role=OrganizationRole.ADMIN
)
assert result is None
class TestGetOrganizationMembers:
"""Tests for get_organization_members method."""
@pytest.mark.asyncio
async def test_get_organization_members_success(self, async_test_db, async_test_user):
"""Test getting organization members."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.ADMIN,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
members, total = await organization_crud.get_organization_members(
session,
organization_id=org_id
)
assert total == 1
assert len(members) == 1
assert members[0]["user_id"] == async_test_user.id
assert members[0]["email"] == async_test_user.email
assert members[0]["role"] == OrganizationRole.ADMIN
@pytest.mark.asyncio
async def test_get_organization_members_with_pagination(self, async_test_db, async_test_user):
"""Test getting organization members with pagination."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
members, total = await organization_crud.get_organization_members(
session,
organization_id=org_id,
skip=0,
limit=10
)
assert total == 1
assert len(members) <= 10
class TestGetUserOrganizations:
"""Tests for get_user_organizations method."""
@pytest.mark.asyncio
async def test_get_user_organizations_success(self, async_test_db, async_test_user):
"""Test getting user's organizations."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs = await organization_crud.get_user_organizations(
session,
user_id=async_test_user.id
)
assert len(orgs) == 1
assert orgs[0].name == "Test Org"
@pytest.mark.asyncio
async def test_get_user_organizations_filter_inactive(self, async_test_db, async_test_user):
"""Test filtering inactive organizations."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Active Org", slug="active-org")
org2 = Organization(name="Inactive Org", slug="inactive-org")
session.add_all([org1, org2])
await session.commit()
user_org1 = UserOrganization(
user_id=async_test_user.id,
organization_id=org1.id,
role=OrganizationRole.MEMBER,
is_active=True
)
user_org2 = UserOrganization(
user_id=async_test_user.id,
organization_id=org2.id,
role=OrganizationRole.MEMBER,
is_active=False
)
session.add_all([user_org1, user_org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs = await organization_crud.get_user_organizations(
session,
user_id=async_test_user.id,
is_active=True
)
assert len(orgs) == 1
assert orgs[0].name == "Active Org"
class TestGetUserRole:
"""Tests for get_user_role_in_org method."""
@pytest.mark.asyncio
async def test_get_user_role_in_org_success(self, async_test_db, async_test_user):
"""Test getting user role in organization_crud."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.ADMIN,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
role = await organization_crud.get_user_role_in_org(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert role == OrganizationRole.ADMIN
@pytest.mark.asyncio
async def test_get_user_role_in_org_not_found(self, async_test_db):
"""Test getting role for non-member returns None."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
role = await organization_crud.get_user_role_in_org(
session,
user_id=uuid4(),
organization_id=org_id
)
assert role is None
class TestIsUserOrgOwner:
"""Tests for is_user_org_owner method."""
@pytest.mark.asyncio
async def test_is_user_org_owner_true(self, async_test_db, async_test_user):
"""Test checking if user is owner."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.OWNER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
is_owner = await organization_crud.is_user_org_owner(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert is_owner is True
@pytest.mark.asyncio
async def test_is_user_org_owner_false(self, async_test_db, async_test_user):
"""Test checking if non-owner user is owner."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
is_owner = await organization_crud.is_user_org_owner(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert is_owner is False
class TestGetMultiWithMemberCounts:
"""Tests for get_multi_with_member_counts method."""
@pytest.mark.asyncio
async def test_get_multi_with_member_counts_success(self, async_test_db, async_test_user):
"""Test getting organizations with member counts."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Org 1", slug="org-1")
org2 = Organization(name="Org 2", slug="org-2")
session.add_all([org1, org2])
await session.commit()
# Add members to org1
user_org1 = UserOrganization(
user_id=async_test_user.id,
organization_id=org1.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org1)
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs_with_counts, total = await organization_crud.get_multi_with_member_counts(session)
assert total == 2
assert len(orgs_with_counts) == 2
# Verify structure
assert 'organization' in orgs_with_counts[0]
assert 'member_count' in orgs_with_counts[0]
@pytest.mark.asyncio
async def test_get_multi_with_member_counts_with_filters(self, async_test_db):
"""Test getting organizations with member counts and filters."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Active Org", slug="active-org", is_active=True)
org2 = Organization(name="Inactive Org", slug="inactive-org", is_active=False)
session.add_all([org1, org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs_with_counts, total = await organization_crud.get_multi_with_member_counts(
session,
is_active=True
)
assert total == 1
assert orgs_with_counts[0]['organization'].name == "Active Org"
@pytest.mark.asyncio
async def test_get_multi_with_member_counts_with_search(self, async_test_db):
"""Test searching organizations with member counts."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Tech Corp", slug="tech-corp")
org2 = Organization(name="Food Inc", slug="food-inc")
session.add_all([org1, org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs_with_counts, total = await organization_crud.get_multi_with_member_counts(
session,
search="tech"
)
assert total == 1
assert orgs_with_counts[0]['organization'].name == "Tech Corp"
class TestGetUserOrganizationsWithDetails:
"""Tests for get_user_organizations_with_details method."""
@pytest.mark.asyncio
async def test_get_user_organizations_with_details_success(self, async_test_db, async_test_user):
"""Test getting user organizations with role and member count."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.ADMIN,
is_active=True
)
session.add(user_org)
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs_with_details = await organization_crud.get_user_organizations_with_details(
session,
user_id=async_test_user.id
)
assert len(orgs_with_details) == 1
assert orgs_with_details[0]['organization'].name == "Test Org"
assert orgs_with_details[0]['role'] == OrganizationRole.ADMIN
assert 'member_count' in orgs_with_details[0]
@pytest.mark.asyncio
async def test_get_user_organizations_with_details_filter_inactive(self, async_test_db, async_test_user):
"""Test filtering inactive organizations in user details."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org1 = Organization(name="Active Org", slug="active-org")
org2 = Organization(name="Inactive Org", slug="inactive-org")
session.add_all([org1, org2])
await session.commit()
user_org1 = UserOrganization(
user_id=async_test_user.id,
organization_id=org1.id,
role=OrganizationRole.MEMBER,
is_active=True
)
user_org2 = UserOrganization(
user_id=async_test_user.id,
organization_id=org2.id,
role=OrganizationRole.MEMBER,
is_active=False
)
session.add_all([user_org1, user_org2])
await session.commit()
async with AsyncTestingSessionLocal() as session:
orgs_with_details = await organization_crud.get_user_organizations_with_details(
session,
user_id=async_test_user.id,
is_active=True
)
assert len(orgs_with_details) == 1
assert orgs_with_details[0]['organization'].name == "Active Org"
class TestIsUserOrgAdmin:
"""Tests for is_user_org_admin method."""
@pytest.mark.asyncio
async def test_is_user_org_admin_owner(self, async_test_db, async_test_user):
"""Test checking if owner is admin (should be True)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.OWNER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
is_admin = await organization_crud.is_user_org_admin(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert is_admin is True
@pytest.mark.asyncio
async def test_is_user_org_admin_admin_role(self, async_test_db, async_test_user):
"""Test checking if admin role is admin."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.ADMIN,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
is_admin = await organization_crud.is_user_org_admin(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert is_admin is True
@pytest.mark.asyncio
async def test_is_user_org_admin_member_false(self, async_test_db, async_test_user):
"""Test checking if regular member is admin."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
user_org = UserOrganization(
user_id=async_test_user.id,
organization_id=org.id,
role=OrganizationRole.MEMBER,
is_active=True
)
session.add(user_org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
is_admin = await organization_crud.is_user_org_admin(
session,
user_id=async_test_user.id,
organization_id=org_id
)
assert is_admin is False
class TestOrganizationExceptionHandlers:
"""
Test exception handlers in organization CRUD methods.
Uses mocks to trigger database errors and verify proper error handling.
Covers lines: 33-35, 57-62, 114-116, 130-132, 207-209, 258-260, 291-294, 326-329, 385-387, 409-411, 466-468, 491-493
"""
@pytest.mark.asyncio
async def test_get_by_slug_database_error(self, async_test_db):
"""Test get_by_slug handles database errors (covers lines 33-35)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Database connection lost")):
with pytest.raises(Exception, match="Database connection lost"):
await organization_crud.get_by_slug(session, slug="test-slug")
@pytest.mark.asyncio
async def test_create_integrity_error_non_slug(self, async_test_db):
"""Test create with non-slug IntegrityError (covers lines 56-57)."""
from sqlalchemy.exc import IntegrityError
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
async def mock_commit():
error = IntegrityError("statement", {}, Exception("foreign key constraint failed"))
error.orig = Exception("foreign key constraint failed")
raise error
with patch.object(session, 'commit', side_effect=mock_commit):
with patch.object(session, 'rollback', new_callable=AsyncMock):
org_in = OrganizationCreate(name="Test", slug="test")
with pytest.raises(ValueError, match="Database integrity error"):
await organization_crud.create(session, obj_in=org_in)
@pytest.mark.asyncio
async def test_create_unexpected_error(self, async_test_db):
"""Test create with unexpected exception (covers lines 58-62)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'commit', side_effect=RuntimeError("Unexpected error")):
with patch.object(session, 'rollback', new_callable=AsyncMock):
org_in = OrganizationCreate(name="Test", slug="test")
with pytest.raises(RuntimeError, match="Unexpected error"):
await organization_crud.create(session, obj_in=org_in)
@pytest.mark.asyncio
async def test_get_multi_with_filters_database_error(self, async_test_db):
"""Test get_multi_with_filters handles database errors (covers lines 114-116)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Query timeout")):
with pytest.raises(Exception, match="Query timeout"):
await organization_crud.get_multi_with_filters(session)
@pytest.mark.asyncio
async def test_get_member_count_database_error(self, async_test_db):
"""Test get_member_count handles database errors (covers lines 130-132)."""
from uuid import uuid4
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Count query failed")):
with pytest.raises(Exception, match="Count query failed"):
await organization_crud.get_member_count(session, organization_id=uuid4())
@pytest.mark.asyncio
async def test_get_multi_with_member_counts_database_error(self, async_test_db):
"""Test get_multi_with_member_counts handles database errors (covers lines 207-209)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Complex query failed")):
with pytest.raises(Exception, match="Complex query failed"):
await organization_crud.get_multi_with_member_counts(session)
@pytest.mark.asyncio
async def test_add_user_integrity_error(self, async_test_db, async_test_user):
"""Test add_user with IntegrityError (covers lines 258-260)."""
from sqlalchemy.exc import IntegrityError
from unittest.mock import MagicMock
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
# First create org
org = Organization(name="Test Org", slug="test-org")
session.add(org)
await session.commit()
org_id = org.id
async with AsyncTestingSessionLocal() as session:
async def mock_commit():
raise IntegrityError("statement", {}, Exception("constraint failed"))
# Mock execute to return None (no existing relationship)
async def mock_execute(*args, **kwargs):
result = MagicMock()
result.scalar_one_or_none = MagicMock(return_value=None)
return result
with patch.object(session, 'execute', side_effect=mock_execute):
with patch.object(session, 'commit', side_effect=mock_commit):
with patch.object(session, 'rollback', new_callable=AsyncMock):
with pytest.raises(ValueError, match="Failed to add user to organization"):
await organization_crud.add_user(
session,
organization_id=org_id,
user_id=async_test_user.id
)
@pytest.mark.asyncio
async def test_remove_user_database_error(self, async_test_db, async_test_user):
"""Test remove_user handles database errors (covers lines 291-294)."""
from uuid import uuid4
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Delete failed")):
with pytest.raises(Exception, match="Delete failed"):
await organization_crud.remove_user(
session,
organization_id=uuid4(),
user_id=async_test_user.id
)
@pytest.mark.asyncio
async def test_update_user_role_database_error(self, async_test_db, async_test_user):
"""Test update_user_role handles database errors (covers lines 326-329)."""
from uuid import uuid4
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Update failed")):
with pytest.raises(Exception, match="Update failed"):
await organization_crud.update_user_role(
session,
organization_id=uuid4(),
user_id=async_test_user.id,
role=OrganizationRole.ADMIN
)
@pytest.mark.asyncio
async def test_get_organization_members_database_error(self, async_test_db):
"""Test get_organization_members handles database errors (covers lines 385-387)."""
from uuid import uuid4
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Members query failed")):
with pytest.raises(Exception, match="Members query failed"):
await organization_crud.get_organization_members(session, organization_id=uuid4())
@pytest.mark.asyncio
async def test_get_user_organizations_database_error(self, async_test_db, async_test_user):
"""Test get_user_organizations handles database errors (covers lines 409-411)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("User orgs query failed")):
with pytest.raises(Exception, match="User orgs query failed"):
await organization_crud.get_user_organizations(session, user_id=async_test_user.id)
@pytest.mark.asyncio
async def test_get_user_organizations_with_details_database_error(self, async_test_db, async_test_user):
"""Test get_user_organizations_with_details handles database errors (covers lines 466-468)."""
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Details query failed")):
with pytest.raises(Exception, match="Details query failed"):
await organization_crud.get_user_organizations_with_details(session, user_id=async_test_user.id)
@pytest.mark.asyncio
async def test_get_user_role_in_org_database_error(self, async_test_db, async_test_user):
"""Test get_user_role_in_org handles database errors (covers lines 491-493)."""
from uuid import uuid4
test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
with patch.object(session, 'execute', side_effect=Exception("Role query failed")):
with pytest.raises(Exception, match="Role query failed"):
await organization_crud.get_user_role_in_org(
session,
user_id=async_test_user.id,
organization_id=uuid4()
)