"""Фабрики справочников и фикстуры `app` / `auth_client` / `seeded` (задача 1.3): проверка до API-тестов.""" import pytest from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from src.models.access import UserRole from src.models.enums import EntityType, RelatedGroupKind from tests.conftest import AuthClientFactory from tests.factories import ( make_company_group, make_legal_entity, make_membership, make_regional_office, make_related_group, make_user, ) HTTP_OK = 200 INN_LENGTH = 10 @pytest.mark.integration async def test_directory_factories_build_consistent_chain(db_session: AsyncSession) -> None: office = await make_regional_office(db_session) head = await make_regional_office(db_session, name="ГО Тест", is_head_office=True) group = await make_company_group(db_session, office=office, crm_id="CRM-1") entity = await make_legal_entity(db_session, group=group) other = await make_legal_entity(db_session, name="ИП Тест", entity_type=EntityType.ИП, office=head) gsk = await make_related_group(db_session, group=group) gsz = await make_related_group(db_session, group=group, kind=RelatedGroupKind.gsz, name="ГСЗ Тест") member = await make_membership(db_session, related_group=gsk, entity=entity) # v5.14: одно ЮЛ одновременно в ГСК и ГСЗ member_gsz = await make_membership(db_session, related_group=gsz, entity=entity) assert head.is_head_office is True assert office.is_head_office is False assert group.code.startswith("GK-T") assert group.base_regional_office_id == office.id assert group.valid_from is not None assert entity.regional_office_id == office.id # по умолчанию — филиал группы assert entity.entity_type == EntityType.ЮЛ assert len(entity.inn) == INN_LENGTH assert entity.inn != other.inn assert other.regional_office_id == head.id assert other.company_group_id is None assert gsk.code.startswith("GSK-T") assert gsz.code.startswith("GSZ-T") assert member.kind == RelatedGroupKind.gsk assert member.is_current is True assert member.valid_to is None assert member_gsz.kind == RelatedGroupKind.gsz @pytest.mark.integration async def test_membership_not_current_is_closed(db_session: AsyncSession) -> None: group = await make_company_group(db_session) entity = await make_legal_entity(db_session, group=group) related = await make_related_group(db_session, group=group) closed = await make_membership(db_session, related_group=related, entity=entity, is_current=False) again = await make_membership(db_session, related_group=related, entity=entity) # закрытая версия не мешает assert closed.is_current is False assert closed.valid_to is not None assert again.is_current is True @pytest.mark.integration async def test_second_current_membership_in_same_kind_is_rejected(db_session: AsyncSession) -> None: """Фабрики не обходят ограничения БД: ЮЛ в двух ГСК одновременно — нарушение partial UNIQUE (v5.14).""" group = await make_company_group(db_session) entity = await make_legal_entity(db_session, group=group) first = await make_related_group(db_session, group=group) second = await make_related_group(db_session, group=group, name="ГСК-2") await make_membership(db_session, related_group=first, entity=entity) with pytest.raises(IntegrityError): async with db_session.begin_nested(): await make_membership(db_session, related_group=second, entity=entity) @pytest.mark.integration async def test_make_user_assigns_seeded_roles(seeded: AsyncSession) -> None: office = await make_regional_office(seeded) user = await make_user(seeded, email="factory@armka.local", office=office, roles=["KA_RF", "EXPERT"]) plain = await make_user(seeded, email="plain@armka.local", office=office, is_active=False) count = len((await seeded.execute(select(UserRole).where(UserRole.user_id == user.id))).scalars().all()) assert count == 2 # noqa: PLR2004 assert user.regional_office_id == office.id assert plain.is_active is False assert not (await seeded.execute(select(UserRole).where(UserRole.user_id == plain.id))).scalars().all() @pytest.mark.integration async def test_auth_client_gets_me_for_dev_user(auth_client: AuthClientFactory) -> None: client = await auth_client("ka_rf@armka.local") response = await client.get("/me") assert response.status_code == HTTP_OK, response.text assert response.json()["email"] == "ka_rf@armka.local" assert response.json()["roles"] == ["KA_RF"] @pytest.mark.integration async def test_auth_client_sees_data_created_by_factories( db_session: AsyncSession, auth_client: AuthClientFactory ) -> None: """Пользователь, созданный фабрикой в той же сессии, получает токен и проходит `/me` (одна `db_session`).""" office = await make_regional_office(db_session) await make_user(db_session, email="made@armka.local", office=office, roles=["KA_RF"]) client = await auth_client("made@armka.local") response = await client.get("/me") assert response.status_code == HTTP_OK, response.text assert response.json()["regional_office"]["id"] == str(office.id)