from datetime import date from unittest.mock import AsyncMock import pytest from pydantic import ValidationError from src.domain.schemas import MembershipRowCreate, MembershipRowResponse, MembershipRowUpdate from src.services.membership_service import MembershipService def _membership_row_dict( membership_id: int = 1, inn: str = "7701234567", group_id: str = "GK001", crm_group_id: str = "CRM001", group_name: str = "ГК Тест", organization_name: str = "ООО Тест", is_gsk: bool = True, is_gsz: bool = False, org_type: str | None = "ЮЛ", membership_from: date | None = None, membership_to: date | None = None, ) -> dict: return { "membership_id": membership_id, "inn": inn, "group_id": group_id, "crm_group_id": crm_group_id, "group_name": group_name, "organization_name": organization_name, "is_gsk": is_gsk, "is_gsz": is_gsz, "org_type": org_type, "membership_from": membership_from, "membership_to": membership_to, } @pytest.fixture def service() -> MembershipService: svc = MembershipService(AsyncMock()) svc.repository = AsyncMock() return svc @pytest.mark.asyncio async def test_get_returns_none_when_row_not_found(service: MembershipService): service.repository.get.return_value = None result = await service.get(123) service.repository.get.assert_awaited_once_with(123) assert result is None @pytest.mark.asyncio async def test_get_maps_repository_dict_to_response_schema(service: MembershipService): expected_row = _membership_row_dict(membership_id=10, membership_from=date(2026, 1, 1)) service.repository.get.return_value = expected_row result = await service.get(10) assert isinstance(result, MembershipRowResponse) assert result.membership_id == 10 assert result.inn == "7701234567" assert result.crm_group_id == "CRM001" assert result.membership_from == date(2026, 1, 1) @pytest.mark.asyncio async def test_get_raises_validation_error_on_invalid_repository_payload(service: MembershipService): # Реальный защитный кейс: сервис не должен молча пропускать некорректную структуру данных. service.repository.get.return_value = {"membership_id": 10} with pytest.raises(ValidationError): await service.get(10) @pytest.mark.asyncio async def test_list_passes_filters_and_maps_rows(service: MembershipService): service.repository.list.return_value = [ _membership_row_dict(membership_id=1), _membership_row_dict(membership_id=2, inn="7700000001", is_gsz=True), ] result = await service.list(skip=5, limit=25, group_id="GK001", inn="7701234567", active_only=False) service.repository.list.assert_awaited_once_with( skip=5, limit=25, group_id="GK001", inn="7701234567", active_only=False, ) assert [row.membership_id for row in result] == [1, 2] assert result[1].is_gsz is True @pytest.mark.asyncio async def test_create_sends_model_dump_to_repository_and_returns_response(service: MembershipService): payload = MembershipRowCreate( group_id="GK001", org_id=77, is_gsk=True, is_gsz=False, control_type="CTRL", comment="Создание связи", ) created_row = _membership_row_dict(membership_id=5) service.repository.create.return_value = created_row result = await service.create(payload) service.repository.create.assert_awaited_once_with(payload.model_dump()) assert isinstance(result, MembershipRowResponse) assert result.membership_id == 5 @pytest.mark.asyncio async def test_create_propagates_repository_error(service: MembershipService): payload = MembershipRowCreate(group_id="GK001", org_id=77, is_gsk=False, is_gsz=False) service.repository.create.side_effect = RuntimeError("db error") with pytest.raises(RuntimeError, match="db error"): await service.create(payload) @pytest.mark.asyncio async def test_update_uses_exclude_none_payload_and_returns_none_for_missing_entity( service: MembershipService, ): payload = MembershipRowUpdate(is_gsz=True, comment=None, control_type=None) service.repository.update.return_value = None result = await service.update(99, payload) service.repository.update.assert_awaited_once_with( 99, {"is_gsz": True}, ) assert result is None @pytest.mark.asyncio async def test_update_maps_result_when_row_exists(service: MembershipService): payload = MembershipRowUpdate(is_gsz=True, comment="Обновлено") service.repository.update.return_value = _membership_row_dict( membership_id=7, is_gsz=True, membership_to=None, ) result = await service.update(7, payload) assert isinstance(result, MembershipRowResponse) assert result.membership_id == 7 assert result.is_gsz is True @pytest.mark.asyncio async def test_deactivate_returns_repository_flag(service: MembershipService): service.repository.deactivate.return_value = True result = await service.deactivate(15) service.repository.deactivate.assert_awaited_once_with(15) assert result is True