From 79d4278247e7428ef2087dda0613ac87ac50a7b9 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Wed, 7 Oct 2026 11:03:52 +0300 Subject: [PATCH 1/2] test(project service): add tests to project service --- backend/services/project.py | 13 +- backend/tests/conftest.py | 9 + backend/tests/test_services/conftest.py | 143 +++++- backend/tests/test_services/test_project.py | 461 ++++++++++++++++++++ 4 files changed, 619 insertions(+), 7 deletions(-) create mode 100644 backend/tests/test_services/test_project.py diff --git a/backend/services/project.py b/backend/services/project.py index 1b88b74..b06229e 100644 --- a/backend/services/project.py +++ b/backend/services/project.py @@ -1,5 +1,5 @@ from types import TracebackType -from typing import Self, cast +from typing import Self from core.config.application import settings from core.constants import ProjectVisibility @@ -9,7 +9,7 @@ from core.exceptions.user import UserIdNotFoundError from core.interfaces.clients import AbstractS3Client, AbstractUnitOfWorkClient from core.logging import get_logger -from infrastructure.database.models import Project, User +from infrastructure.database.models import Project from infrastructure.database.repositories import ( FileRepository, OutboxRepository, @@ -164,6 +164,9 @@ async def handle_render_generated( key = file_location.key project = await self.project_repository.get_by_id(project_id) + if project is None: + raise ProjectIdNotFoundError(project_id) + name = f"{project.name}.png" size = await s3_client.get_file_size(bucket, key) file = FileCreate( @@ -338,6 +341,8 @@ async def _validate_project_owner(self, project_id: int, user_id: int) -> None: get_owner_coroutine = self.project_repository.get_project_owner( project_id, ) - owner = cast(User, await get_owner_coroutine) - if owner.id != user_id: + project_owner = await get_owner_coroutine + if project_owner is None: + raise UserIdNotFoundError(user_id) + if project_owner.id != user_id: raise PermissionDeniedError diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index bb14b18..762912b 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -6,6 +6,7 @@ from infrastructure.minio.client import MinioClient from services.auth import AuthService from services.file_uploader import FileUploader +from services.project import ProjectService from services.tag import TagService from services.user import UserService from sqlalchemy.ext.asyncio import ( @@ -108,6 +109,14 @@ async def file_uploader( return file_uploader +@pytest.fixture(scope="function") +def project_service( + unit_of_work: UnitOfWork, +) -> ProjectService: + project_service = ProjectService(unit_of_work) + return project_service + + @pytest.fixture(scope="function") def tag_service( unit_of_work: UnitOfWork, diff --git a/backend/tests/test_services/conftest.py b/backend/tests/test_services/conftest.py index 5c5babc..aeaf1cb 100644 --- a/backend/tests/test_services/conftest.py +++ b/backend/tests/test_services/conftest.py @@ -1,6 +1,10 @@ import pytest -from core.constants import ProjectVisibility -from infrastructure.database.models import Project, Tag, User +from core.constants import ProjectVisibility, RenderStatus +from infrastructure.database.models import File, Project, Render, Tag, User +from schemas.event import RenderGeneratedEvent +from schemas.file import FileLocationCreate +from schemas.project import ProjectBase, ProjectCreate, ProjectWithRenderCreate +from schemas.render import RenderBase, RenderCreatePayload, SunInfo from schemas.tag import TagCreate @@ -21,16 +25,66 @@ def project_owner() -> User: def project(project_owner: User) -> Project: project = Project( id=1, - name="name", + name="project_name", description="", user_id=project_owner.id, source_file_id=1, render_id=1, + status=RenderStatus.rendering, visibility=ProjectVisibility.public, ) return project +@pytest.fixture +def source_file() -> File: + file = File( + id=2, + name="source_file_name", + size=1000, + bucket="source_bucket_name", + key="source_key_name", + ) + return file + + +@pytest.fixture +def render_file() -> File: + file = File( + id=1, + name="file_name", + size=1000, + bucket="bucket_name", + key="key_name", + ) + return file + + +@pytest.fixture +def render(render_file: File) -> Render: + render = Render( + id=1, + width=1000, + height=1000, + samples=100, + denoiser=True, + gpu=True, + file_id=render_file.id, + ) + render.file = render_file + return render + + +@pytest.fixture +def full_project(project: Project, render: Render, source_file: File) -> Project: + project.render_id = render.id + project.render = render + + project.source_file_id = source_file.id + project.source_file = source_file + return project + + @pytest.fixture def create_tag_data(project: Project) -> TagCreate: return TagCreate( @@ -58,3 +112,86 @@ def tag(project: Project) -> Tag: def tag_with_project(tag: Tag, project: Project) -> Tag: tag.project = project return tag + + +@pytest.fixture(scope="function") +def output(render_file: Render) -> FileLocationCreate: + output = FileLocationCreate( + bucket=render_file.bucket, + key=render_file.key, + ) + return output + + +@pytest.fixture(scope="function") +def render_generated_event( + full_project: Project, + output: FileLocationCreate, +) -> RenderGeneratedEvent: + render_generated_event = RenderGeneratedEvent( + project_id=full_project.id, + output=output, + ) + return render_generated_event + + +@pytest.fixture(scope="function") +def render_base_data() -> RenderBase: + return RenderBase( + width=1000, + height=1000, + samples=100, + denoiser=True, + gpu=True, + ) + + +@pytest.fixture(scope="function") +def sun() -> SunInfo: + return SunInfo( + direction=[1.0, 1.0, 1.0], + color=[1.0, 1.0, 1.0], + exponent=100, + ) + + +@pytest.fixture(scope="function") +def render_create_data( + render_base_data: RenderBase, + sun: SunInfo, +) -> RenderCreatePayload: + return RenderCreatePayload( + background=[1.0, 1.0, 1.0], + sun=sun, + **render_base_data.model_dump(), + ) + + +@pytest.fixture(scope="function") +def project_base_data() -> ProjectBase: + return ProjectBase( + name="project_name", + description="", + source_file_id=1, + ) + + +@pytest.fixture(scope="function") +def project_create_data( + project_base_data: ProjectBase, +) -> ProjectCreate: + return ProjectCreate( + visibility=ProjectVisibility.public, + **project_base_data.model_dump(), + ) + + +@pytest.fixture(scope="function") +def create_project_request( + render_create_data: RenderCreatePayload, + project_create_data: ProjectCreate, +) -> ProjectWithRenderCreate: + return ProjectWithRenderCreate( + render=render_create_data, + project=project_create_data, + ) diff --git a/backend/tests/test_services/test_project.py b/backend/tests/test_services/test_project.py new file mode 100644 index 0000000..e65af31 --- /dev/null +++ b/backend/tests/test_services/test_project.py @@ -0,0 +1,461 @@ +import pytest +from core.constants import ProjectVisibility +from core.exceptions.auth import PermissionDeniedError +from core.exceptions.file import FileIdNotFoundError +from core.exceptions.project import ProjectIdNotFoundError +from core.exceptions.user import UserIdNotFoundError +from infrastructure.database.models import File, Project, Render, User +from infrastructure.minio.client import MinioClient +from pytest_mock import MockerFixture +from schemas.event import RenderGeneratedEvent +from schemas.project import ( + ProjectPartialUpdate, + ProjectWithRenderCreate, +) +from services.project import ProjectService + + +class TestProjectService: + async def test_get_by_id_valid( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service, + "_validate_access_to_get_project", + return_value=full_project, + autospec=True, + ) + + mocker.patch( + "services.project.ProjectWithRenderFileFullResponse.get_from_database", + return_value=full_project, + autospec=True, + ) + + await project_service.get_by_id( + full_project.id, + project_owner.id, + minio_client, + ) + + async def test_get_by_id_project_not_exists( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + autospec=True, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.get_by_id( + full_project.id, + project_owner.id, + minio_client, + ) + + async def test_get_private_project_by_id_not_by_owner( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + full_project.visibility = ProjectVisibility.private + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + autospec=True, + ) + mocker.patch.object( + project_service.project_repository, + "get_project_owner", + return_value=project_owner, + autospec=True, + ) + with pytest.raises(PermissionDeniedError): + await project_service.get_by_id( + full_project.id, + project_owner.id + 1, + minio_client, + ) + + async def test_get_user_projects_valid( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=project_owner, + ) + mocker.patch.object( + project_service.project_repository, + "get_user_projects", + return_value=[full_project], + autospec=True, + ) + await project_service.get_user_projects( + project_owner.id, + size=10, + page=1, + ) + + async def test_get_user_projects_user_not_exists( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=None, + autospec=True, + ) + with pytest.raises(UserIdNotFoundError): + await project_service.get_user_projects( + project_owner.id, + size=10, + page=1, + ) + + async def test_get_public_projects_valid( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_public_projects", + return_value=[full_project], + autospec=True, + ) + await project_service.get_public_projects( + size=10, + page=1, + ) + + async def test_get_user_public_projects_valid( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=project_owner, + ) + await project_service.get_user_public_projects( + user_id=project_owner.id, + size=10, + page=1, + ) + + async def test_get_user_public_projects_user_not_exists( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=None, + ) + with pytest.raises(UserIdNotFoundError): + await project_service.get_user_public_projects( + user_id=project_owner.id, + size=10, + page=1, + ) + + async def test_handle_render_generated_valid( + self, + render_file: File, + full_project: Project, + render_generated_event: RenderGeneratedEvent, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + ) + mocker.patch.object( + minio_client, + "get_file_size", + return_value=1000, + ) + mocker.patch.object( + project_service.file_repository, + "create_file", + return_value=render_file, + ) + mocker.patch.object( + project_service.render_repository, + "add_render_file", + return_value=None, + ) + await project_service.handle_render_generated( + render_generated_event, + minio_client, + ) + + async def test_handle_render_generated_project_not_exists( + self, + render_generated_event: RenderGeneratedEvent, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.handle_render_generated( + render_generated_event, + minio_client, + ) + + async def test_update_project_status_valid( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "update_project_status", + return_value=full_project, + ) + await project_service.update_project_status(full_project.id) + + async def test_update_not_exists_project( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "update_project_status", + return_value=None, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.update_project_status(full_project.id) + + async def test_create_project_valid( + self, + create_project_request: ProjectWithRenderCreate, + render: Render, + source_file: File, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.file_repository, + "get_by_id", + return_value=source_file, + autospec=True, + ) + mocker.patch.object( + project_service.render_repository, + "create_render", + return_value=render, + autospec=True, + ) + mocker.patch.object( + project_service.project_repository, + "create_project", + return_value=full_project, + autospec=True, + ) + mocker.patch.object( + project_service.outbox_repository, + "create_event", + return_value=None, + autospec=True, + ) + + await project_service.create_project( + user_id=project_owner.id, + create_project=create_project_request, + ) + + async def test_create_project_file_not_exists( + self, + create_project_request: ProjectWithRenderCreate, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.file_repository, + "get_by_id", + return_value=None, + autospec=True, + ) + with pytest.raises(FileIdNotFoundError): + await project_service.create_project( + user_id=project_owner.id, + create_project=create_project_request, + ) + + async def test_partial_update_project_valid( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + autospec=True, + ) + + mocker.patch.object( + project_service.project_repository, + "get_project_owner", + return_value=project_owner, + autospec=True, + ) + partial_update_project_data = ProjectPartialUpdate( + visibility=ProjectVisibility.private, + ) + await project_service.partial_update_project( + full_project.id, + project_owner.id, + partial_update_project_data, + ) + + async def test_partial_update_project_not_exists( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + autospec=True, + ) + partial_update_project_data = ProjectPartialUpdate( + visibility=ProjectVisibility.private, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.partial_update_project( + full_project.id, + project_owner.id, + partial_update_project_data, + ) + + async def test_partial_update_project_not_by_owner( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + autospec=True, + ) + mocker.patch.object( + project_service.project_repository, + "get_project_owner", + return_value=project_owner, + autospec=True, + ) + partial_update_project_data = ProjectPartialUpdate( + description="new description", + ) + with pytest.raises(PermissionDeniedError): + await project_service.partial_update_project( + full_project.id, + project_owner.id + 1, + partial_update_project_data, + ) + + async def test_delete_by_id_valid( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + ) + mocker.patch.object( + project_service.project_repository, + "get_project_owner", + return_value=project_owner, + ) + await project_service.delete_by_id(full_project.id, project_owner.id) + + async def test_delete_by_id_project_not_exists( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.delete_by_id(full_project.id, project_owner.id) + + async def test_delete_by_id_not_by_owner( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + ) + mocker.patch.object( + project_service.project_repository, + "get_project_owner", + return_value=project_owner, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.delete_by_id(full_project.id, project_owner.id + 1) From 15dc3bc54f8de42c6f844ad4c3f16c9f1a18b818 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Wed, 7 Oct 2026 11:18:26 +0300 Subject: [PATCH 2/2] refactor(test project service): split project service tests on different modules --- backend/tests/test_services/conftest.py | 87 --------- .../test_services/test_project/__init__.py | 0 .../test_services/test_project/conftest.py | 90 +++++++++ .../test_project/test_project_pagination.py | 102 ++++++++++ .../test_project_service.py} | 178 ------------------ .../test_render_generate_handler.py | 88 +++++++++ 6 files changed, 280 insertions(+), 265 deletions(-) create mode 100644 backend/tests/test_services/test_project/__init__.py create mode 100644 backend/tests/test_services/test_project/conftest.py create mode 100644 backend/tests/test_services/test_project/test_project_pagination.py rename backend/tests/test_services/{test_project.py => test_project/test_project_service.py} (62%) create mode 100644 backend/tests/test_services/test_project/test_render_generate_handler.py diff --git a/backend/tests/test_services/conftest.py b/backend/tests/test_services/conftest.py index aeaf1cb..b99cd0d 100644 --- a/backend/tests/test_services/conftest.py +++ b/backend/tests/test_services/conftest.py @@ -1,10 +1,6 @@ import pytest from core.constants import ProjectVisibility, RenderStatus from infrastructure.database.models import File, Project, Render, Tag, User -from schemas.event import RenderGeneratedEvent -from schemas.file import FileLocationCreate -from schemas.project import ProjectBase, ProjectCreate, ProjectWithRenderCreate -from schemas.render import RenderBase, RenderCreatePayload, SunInfo from schemas.tag import TagCreate @@ -112,86 +108,3 @@ def tag(project: Project) -> Tag: def tag_with_project(tag: Tag, project: Project) -> Tag: tag.project = project return tag - - -@pytest.fixture(scope="function") -def output(render_file: Render) -> FileLocationCreate: - output = FileLocationCreate( - bucket=render_file.bucket, - key=render_file.key, - ) - return output - - -@pytest.fixture(scope="function") -def render_generated_event( - full_project: Project, - output: FileLocationCreate, -) -> RenderGeneratedEvent: - render_generated_event = RenderGeneratedEvent( - project_id=full_project.id, - output=output, - ) - return render_generated_event - - -@pytest.fixture(scope="function") -def render_base_data() -> RenderBase: - return RenderBase( - width=1000, - height=1000, - samples=100, - denoiser=True, - gpu=True, - ) - - -@pytest.fixture(scope="function") -def sun() -> SunInfo: - return SunInfo( - direction=[1.0, 1.0, 1.0], - color=[1.0, 1.0, 1.0], - exponent=100, - ) - - -@pytest.fixture(scope="function") -def render_create_data( - render_base_data: RenderBase, - sun: SunInfo, -) -> RenderCreatePayload: - return RenderCreatePayload( - background=[1.0, 1.0, 1.0], - sun=sun, - **render_base_data.model_dump(), - ) - - -@pytest.fixture(scope="function") -def project_base_data() -> ProjectBase: - return ProjectBase( - name="project_name", - description="", - source_file_id=1, - ) - - -@pytest.fixture(scope="function") -def project_create_data( - project_base_data: ProjectBase, -) -> ProjectCreate: - return ProjectCreate( - visibility=ProjectVisibility.public, - **project_base_data.model_dump(), - ) - - -@pytest.fixture(scope="function") -def create_project_request( - render_create_data: RenderCreatePayload, - project_create_data: ProjectCreate, -) -> ProjectWithRenderCreate: - return ProjectWithRenderCreate( - render=render_create_data, - project=project_create_data, - ) diff --git a/backend/tests/test_services/test_project/__init__.py b/backend/tests/test_services/test_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/test_services/test_project/conftest.py b/backend/tests/test_services/test_project/conftest.py new file mode 100644 index 0000000..43cb5c5 --- /dev/null +++ b/backend/tests/test_services/test_project/conftest.py @@ -0,0 +1,90 @@ +import pytest +from core.constants import ProjectVisibility +from infrastructure.database.models import Project, Render +from schemas.event import RenderGeneratedEvent +from schemas.file import FileLocationCreate +from schemas.project import ProjectBase, ProjectCreate, ProjectWithRenderCreate +from schemas.render import RenderBase, RenderCreatePayload, SunInfo + + +@pytest.fixture(scope="function") +def output(render_file: Render) -> FileLocationCreate: + output = FileLocationCreate( + bucket=render_file.bucket, + key=render_file.key, + ) + return output + + +@pytest.fixture(scope="function") +def render_generated_event( + full_project: Project, + output: FileLocationCreate, +) -> RenderGeneratedEvent: + render_generated_event = RenderGeneratedEvent( + project_id=full_project.id, + output=output, + ) + return render_generated_event + + +@pytest.fixture(scope="function") +def render_base_data() -> RenderBase: + return RenderBase( + width=1000, + height=1000, + samples=100, + denoiser=True, + gpu=True, + ) + + +@pytest.fixture(scope="function") +def sun() -> SunInfo: + return SunInfo( + direction=[1.0, 1.0, 1.0], + color=[1.0, 1.0, 1.0], + exponent=100, + ) + + +@pytest.fixture(scope="function") +def render_create_data( + render_base_data: RenderBase, + sun: SunInfo, +) -> RenderCreatePayload: + return RenderCreatePayload( + background=[1.0, 1.0, 1.0], + sun=sun, + **render_base_data.model_dump(), + ) + + +@pytest.fixture(scope="function") +def project_base_data() -> ProjectBase: + return ProjectBase( + name="project_name", + description="", + source_file_id=1, + ) + + +@pytest.fixture(scope="function") +def project_create_data( + project_base_data: ProjectBase, +) -> ProjectCreate: + return ProjectCreate( + visibility=ProjectVisibility.public, + **project_base_data.model_dump(), + ) + + +@pytest.fixture(scope="function") +def create_project_request( + render_create_data: RenderCreatePayload, + project_create_data: ProjectCreate, +) -> ProjectWithRenderCreate: + return ProjectWithRenderCreate( + render=render_create_data, + project=project_create_data, + ) diff --git a/backend/tests/test_services/test_project/test_project_pagination.py b/backend/tests/test_services/test_project/test_project_pagination.py new file mode 100644 index 0000000..bdfc971 --- /dev/null +++ b/backend/tests/test_services/test_project/test_project_pagination.py @@ -0,0 +1,102 @@ +import pytest +from core.exceptions.user import UserIdNotFoundError +from infrastructure.database.models import Project, User +from pytest_mock import MockerFixture +from services.project import ProjectService + + +class TestProjectPaginationService: + async def test_get_user_projects_valid( + self, + project_owner: User, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=project_owner, + ) + mocker.patch.object( + project_service.project_repository, + "get_user_projects", + return_value=[full_project], + autospec=True, + ) + await project_service.get_user_projects( + project_owner.id, + size=10, + page=1, + ) + + async def test_get_user_projects_user_not_exists( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=None, + autospec=True, + ) + with pytest.raises(UserIdNotFoundError): + await project_service.get_user_projects( + project_owner.id, + size=10, + page=1, + ) + + async def test_get_public_projects_valid( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_public_projects", + return_value=[full_project], + autospec=True, + ) + await project_service.get_public_projects( + size=10, + page=1, + ) + + async def test_get_user_public_projects_valid( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=project_owner, + ) + await project_service.get_user_public_projects( + user_id=project_owner.id, + size=10, + page=1, + ) + + async def test_get_user_public_projects_user_not_exists( + self, + project_owner: User, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.user_repository, + "get_by_id", + return_value=None, + ) + with pytest.raises(UserIdNotFoundError): + await project_service.get_user_public_projects( + user_id=project_owner.id, + size=10, + page=1, + ) diff --git a/backend/tests/test_services/test_project.py b/backend/tests/test_services/test_project/test_project_service.py similarity index 62% rename from backend/tests/test_services/test_project.py rename to backend/tests/test_services/test_project/test_project_service.py index e65af31..ab6b70e 100644 --- a/backend/tests/test_services/test_project.py +++ b/backend/tests/test_services/test_project/test_project_service.py @@ -3,11 +3,9 @@ from core.exceptions.auth import PermissionDeniedError from core.exceptions.file import FileIdNotFoundError from core.exceptions.project import ProjectIdNotFoundError -from core.exceptions.user import UserIdNotFoundError from infrastructure.database.models import File, Project, Render, User from infrastructure.minio.client import MinioClient from pytest_mock import MockerFixture -from schemas.event import RenderGeneratedEvent from schemas.project import ( ProjectPartialUpdate, ProjectWithRenderCreate, @@ -30,7 +28,6 @@ async def test_get_by_id_valid( return_value=full_project, autospec=True, ) - mocker.patch( "services.project.ProjectWithRenderFileFullResponse.get_from_database", return_value=full_project, @@ -92,180 +89,6 @@ async def test_get_private_project_by_id_not_by_owner( minio_client, ) - async def test_get_user_projects_valid( - self, - project_owner: User, - full_project: Project, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.user_repository, - "get_by_id", - return_value=project_owner, - ) - mocker.patch.object( - project_service.project_repository, - "get_user_projects", - return_value=[full_project], - autospec=True, - ) - await project_service.get_user_projects( - project_owner.id, - size=10, - page=1, - ) - - async def test_get_user_projects_user_not_exists( - self, - project_owner: User, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.user_repository, - "get_by_id", - return_value=None, - autospec=True, - ) - with pytest.raises(UserIdNotFoundError): - await project_service.get_user_projects( - project_owner.id, - size=10, - page=1, - ) - - async def test_get_public_projects_valid( - self, - full_project: Project, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.project_repository, - "get_public_projects", - return_value=[full_project], - autospec=True, - ) - await project_service.get_public_projects( - size=10, - page=1, - ) - - async def test_get_user_public_projects_valid( - self, - project_owner: User, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.user_repository, - "get_by_id", - return_value=project_owner, - ) - await project_service.get_user_public_projects( - user_id=project_owner.id, - size=10, - page=1, - ) - - async def test_get_user_public_projects_user_not_exists( - self, - project_owner: User, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.user_repository, - "get_by_id", - return_value=None, - ) - with pytest.raises(UserIdNotFoundError): - await project_service.get_user_public_projects( - user_id=project_owner.id, - size=10, - page=1, - ) - - async def test_handle_render_generated_valid( - self, - render_file: File, - full_project: Project, - render_generated_event: RenderGeneratedEvent, - mocker: MockerFixture, - minio_client: MinioClient, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.project_repository, - "get_by_id", - return_value=full_project, - ) - mocker.patch.object( - minio_client, - "get_file_size", - return_value=1000, - ) - mocker.patch.object( - project_service.file_repository, - "create_file", - return_value=render_file, - ) - mocker.patch.object( - project_service.render_repository, - "add_render_file", - return_value=None, - ) - await project_service.handle_render_generated( - render_generated_event, - minio_client, - ) - - async def test_handle_render_generated_project_not_exists( - self, - render_generated_event: RenderGeneratedEvent, - mocker: MockerFixture, - minio_client: MinioClient, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.project_repository, - "get_by_id", - return_value=None, - ) - with pytest.raises(ProjectIdNotFoundError): - await project_service.handle_render_generated( - render_generated_event, - minio_client, - ) - - async def test_update_project_status_valid( - self, - full_project: Project, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.project_repository, - "update_project_status", - return_value=full_project, - ) - await project_service.update_project_status(full_project.id) - - async def test_update_not_exists_project( - self, - full_project: Project, - mocker: MockerFixture, - project_service: ProjectService, - ) -> None: - mocker.patch.object( - project_service.project_repository, - "update_project_status", - return_value=None, - ) - with pytest.raises(ProjectIdNotFoundError): - await project_service.update_project_status(full_project.id) - async def test_create_project_valid( self, create_project_request: ProjectWithRenderCreate, @@ -338,7 +161,6 @@ async def test_partial_update_project_valid( return_value=full_project, autospec=True, ) - mocker.patch.object( project_service.project_repository, "get_project_owner", diff --git a/backend/tests/test_services/test_project/test_render_generate_handler.py b/backend/tests/test_services/test_project/test_render_generate_handler.py new file mode 100644 index 0000000..01445f5 --- /dev/null +++ b/backend/tests/test_services/test_project/test_render_generate_handler.py @@ -0,0 +1,88 @@ +import pytest +from core.exceptions.project import ProjectIdNotFoundError +from infrastructure.database.models import File, Project +from infrastructure.minio.client import MinioClient +from pytest_mock import MockerFixture +from schemas.event import RenderGeneratedEvent +from services.project import ProjectService + + +class TestRenderGenerateHandler: + async def test_handle_render_generated_valid( + self, + render_file: File, + full_project: Project, + render_generated_event: RenderGeneratedEvent, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=full_project, + ) + mocker.patch.object( + minio_client, + "get_file_size", + return_value=1000, + ) + mocker.patch.object( + project_service.file_repository, + "create_file", + return_value=render_file, + ) + mocker.patch.object( + project_service.render_repository, + "add_render_file", + return_value=None, + ) + await project_service.handle_render_generated( + render_generated_event, + minio_client, + ) + + async def test_handle_render_generated_project_not_exists( + self, + render_generated_event: RenderGeneratedEvent, + mocker: MockerFixture, + minio_client: MinioClient, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "get_by_id", + return_value=None, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.handle_render_generated( + render_generated_event, + minio_client, + ) + + async def test_update_project_status_valid( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "update_project_status", + return_value=full_project, + ) + await project_service.update_project_status(full_project.id) + + async def test_update_not_exists_project( + self, + full_project: Project, + mocker: MockerFixture, + project_service: ProjectService, + ) -> None: + mocker.patch.object( + project_service.project_repository, + "update_project_status", + return_value=None, + ) + with pytest.raises(ProjectIdNotFoundError): + await project_service.update_project_status(full_project.id)