From 828f50cc023e71a5815fc21ae7842f0a4d787a88 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Wed, 23 Sep 2026 18:56:02 +0300 Subject: [PATCH 1/6] test(file repository): test file repository methods --- .../test_repositories/__init__.py | 0 .../test_repositories/conftest.py | 9 +++ .../test_repositories/test_file.py | 59 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 backend/tests/test_infrastructure/test_repositories/__init__.py create mode 100644 backend/tests/test_infrastructure/test_repositories/conftest.py create mode 100644 backend/tests/test_infrastructure/test_repositories/test_file.py diff --git a/backend/tests/test_infrastructure/test_repositories/__init__.py b/backend/tests/test_infrastructure/test_repositories/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/test_infrastructure/test_repositories/conftest.py b/backend/tests/test_infrastructure/test_repositories/conftest.py new file mode 100644 index 0000000..6b84733 --- /dev/null +++ b/backend/tests/test_infrastructure/test_repositories/conftest.py @@ -0,0 +1,9 @@ +import pytest +from sqlalchemy.ext.asyncio import AsyncSession + +from infrastructure.database.repositories import FileRepository + + +@pytest.fixture(scope="function") +def file_repository(session: AsyncSession) -> FileRepository: + return FileRepository(session) diff --git a/backend/tests/test_infrastructure/test_repositories/test_file.py b/backend/tests/test_infrastructure/test_repositories/test_file.py new file mode 100644 index 0000000..aadae6b --- /dev/null +++ b/backend/tests/test_infrastructure/test_repositories/test_file.py @@ -0,0 +1,59 @@ +from sqlalchemy.ext.asyncio import AsyncSession + +from infrastructure.database.repositories import FileRepository +from schemas.file import FileCreate +from tests.test_infrastructure.test_database.test_models.factories import create_file + + +class TestFileRepository: + async def test_file_repository_get_by_id( + self, + session: AsyncSession, + file_repository: FileRepository, + ) -> None: + file = await create_file(session) + session.expunge(file) + selected_file = await file_repository.get_by_id(file.id) + assert selected_file.id == file.id + assert selected_file.name == file.name + assert selected_file.size == file.size + assert selected_file.bucket == file.bucket + assert selected_file.key == file.key + + async def test_file_repository_get_by_id_not_exists_file( + self, + file_repository: FileRepository, + ) -> None: + selected_file = await file_repository.get_by_id(-1) + assert selected_file is None + + async def test_file_repository_create_file( + self, + session: AsyncSession, + file_repository: FileRepository, + ) -> None: + create_file_data = FileCreate( + name="file_name", + size=1024, + bucket="bucket", + key="key", + ) + created_file = await file_repository.create_file(create_file_data) + session.expunge(created_file) + selected_file = await file_repository.get_by_id(created_file.id) + assert selected_file.id is not None + assert selected_file.name == create_file_data.name + assert selected_file.size == create_file_data.size + assert selected_file.bucket == create_file_data.bucket + assert selected_file.key == create_file_data.key + + async def test_file_repository_delete_by_id( + self, + session: AsyncSession, + file_repository: FileRepository, + ) -> None: + file = await create_file(session) + await file_repository.delete_by_id(file.id) + session.expunge(file) + deleted_file = await file_repository.get_by_id(file.id) + assert deleted_file is None From 5bda05a84d73b6f097d36465a949d809ba0f6517 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Thu, 24 Sep 2026 18:51:21 +0300 Subject: [PATCH 2/6] test(outbox repository): test outbox repository methods --- .../test_repositories/conftest.py | 8 +- .../test_repositories/test_file.py | 14 ++-- .../test_repositories/test_outbox.py | 84 +++++++++++++++++++ 3 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 backend/tests/test_infrastructure/test_repositories/test_outbox.py diff --git a/backend/tests/test_infrastructure/test_repositories/conftest.py b/backend/tests/test_infrastructure/test_repositories/conftest.py index 6b84733..5cdd74c 100644 --- a/backend/tests/test_infrastructure/test_repositories/conftest.py +++ b/backend/tests/test_infrastructure/test_repositories/conftest.py @@ -1,9 +1,13 @@ import pytest +from infrastructure.database.repositories import FileRepository, OutboxRepository from sqlalchemy.ext.asyncio import AsyncSession -from infrastructure.database.repositories import FileRepository - @pytest.fixture(scope="function") def file_repository(session: AsyncSession) -> FileRepository: return FileRepository(session) + + +@pytest.fixture(scope="function") +def outbox_repository(session: AsyncSession) -> OutboxRepository: + return OutboxRepository(session) diff --git a/backend/tests/test_infrastructure/test_repositories/test_file.py b/backend/tests/test_infrastructure/test_repositories/test_file.py index aadae6b..f3eed52 100644 --- a/backend/tests/test_infrastructure/test_repositories/test_file.py +++ b/backend/tests/test_infrastructure/test_repositories/test_file.py @@ -1,12 +1,12 @@ -from sqlalchemy.ext.asyncio import AsyncSession - from infrastructure.database.repositories import FileRepository from schemas.file import FileCreate +from sqlalchemy.ext.asyncio import AsyncSession + from tests.test_infrastructure.test_database.test_models.factories import create_file class TestFileRepository: - async def test_file_repository_get_by_id( + async def test_get_by_id( self, session: AsyncSession, file_repository: FileRepository, @@ -20,14 +20,14 @@ async def test_file_repository_get_by_id( assert selected_file.bucket == file.bucket assert selected_file.key == file.key - async def test_file_repository_get_by_id_not_exists_file( + async def test_get_by_id_not_exists_file( self, file_repository: FileRepository, ) -> None: selected_file = await file_repository.get_by_id(-1) assert selected_file is None - async def test_file_repository_create_file( + async def test_create_file( self, session: AsyncSession, file_repository: FileRepository, @@ -41,13 +41,13 @@ async def test_file_repository_create_file( created_file = await file_repository.create_file(create_file_data) session.expunge(created_file) selected_file = await file_repository.get_by_id(created_file.id) - assert selected_file.id is not None + assert selected_file is not None assert selected_file.name == create_file_data.name assert selected_file.size == create_file_data.size assert selected_file.bucket == create_file_data.bucket assert selected_file.key == create_file_data.key - async def test_file_repository_delete_by_id( + async def test_delete_by_id( self, session: AsyncSession, file_repository: FileRepository, diff --git a/backend/tests/test_infrastructure/test_repositories/test_outbox.py b/backend/tests/test_infrastructure/test_repositories/test_outbox.py new file mode 100644 index 0000000..d5cdf9c --- /dev/null +++ b/backend/tests/test_infrastructure/test_repositories/test_outbox.py @@ -0,0 +1,84 @@ +from core.constants import EventStatus +from infrastructure.database.repositories import OutboxRepository +from schemas.event import EventCreate +from sqlalchemy.ext.asyncio import AsyncSession + +from tests.test_infrastructure.test_database.test_models.factories import create_outbox + + +class TestOutboxRepository: + async def test_get_by_id( + self, + session: AsyncSession, + outbox_repository: OutboxRepository, + ) -> None: + event = await create_outbox(session) + session.expunge(event) + selected_event = await outbox_repository.get_by_id(event.id) + assert selected_event.id == event.id + assert selected_event.topic == event.topic + assert selected_event.message == event.message + assert selected_event.status == event.status + assert selected_event.event_date == event.event_date + + async def test_get_not_exist_event_by_id( + self, + outbox_repository: OutboxRepository, + ) -> None: + selected_event = await outbox_repository.get_by_id(-1) + assert selected_event is None + + async def test_create_event( + self, + session: AsyncSession, + outbox_repository: OutboxRepository, + ) -> None: + create_event_data = EventCreate( + topic="topic", + message={ + "field1": "value1", + "field2": "value2", + }, + ) + created_event = await outbox_repository.create_event(create_event_data) + session.expunge(created_event) + selected_event = await outbox_repository.get_by_id(created_event.id) + assert selected_event is not None + assert selected_event.topic == create_event_data.topic + assert selected_event.message == create_event_data.message + assert selected_event.status == EventStatus.pending + assert selected_event.event_date is not None + + async def test_mark_event_as_sent( + self, + session: AsyncSession, + outbox_repository: OutboxRepository, + ) -> None: + event = await create_outbox(session) + await outbox_repository.mark_event_as_sent(event.id) + selected_event = await outbox_repository.get_by_id(event.id) + assert selected_event is not None + assert selected_event.topic == event.topic + assert selected_event.message == event.message + assert selected_event.status == EventStatus.sent + assert selected_event.event_date is not None + + async def test_get_pending_event( + self, + session: AsyncSession, + outbox_repository: OutboxRepository, + ) -> None: + event = await create_outbox(session) + selected_event = await outbox_repository.get_pending_event() + assert selected_event is not None + assert selected_event.topic == event.topic + assert selected_event.message == event.message + assert selected_event.status == EventStatus.pending + assert selected_event.event_date is not None + + async def test_get_pending_not_exist_event( + self, + outbox_repository: OutboxRepository, + ) -> None: + selected_event = await outbox_repository.get_pending_event() + assert selected_event is None From 65f999f7b13a6c59818fb5c44820ef001fd3c305 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Thu, 24 Sep 2026 19:17:02 +0300 Subject: [PATCH 3/6] test(user repository): test user repository methods --- .../test_repositories/conftest.py | 11 +- .../test_repositories/test_user.py | 132 ++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 backend/tests/test_infrastructure/test_repositories/test_user.py diff --git a/backend/tests/test_infrastructure/test_repositories/conftest.py b/backend/tests/test_infrastructure/test_repositories/conftest.py index 5cdd74c..95e4e91 100644 --- a/backend/tests/test_infrastructure/test_repositories/conftest.py +++ b/backend/tests/test_infrastructure/test_repositories/conftest.py @@ -1,5 +1,9 @@ import pytest -from infrastructure.database.repositories import FileRepository, OutboxRepository +from infrastructure.database.repositories import ( + FileRepository, + OutboxRepository, + UserRepository, +) from sqlalchemy.ext.asyncio import AsyncSession @@ -11,3 +15,8 @@ def file_repository(session: AsyncSession) -> FileRepository: @pytest.fixture(scope="function") def outbox_repository(session: AsyncSession) -> OutboxRepository: return OutboxRepository(session) + + +@pytest.fixture(scope="function") +def user_repository(session: AsyncSession) -> UserRepository: + return UserRepository(session) diff --git a/backend/tests/test_infrastructure/test_repositories/test_user.py b/backend/tests/test_infrastructure/test_repositories/test_user.py new file mode 100644 index 0000000..27d2846 --- /dev/null +++ b/backend/tests/test_infrastructure/test_repositories/test_user.py @@ -0,0 +1,132 @@ +from typing import cast + +from infrastructure.database.repositories import UserRepository +from pydantic import EmailStr +from schemas.user import UserCreate, UserUpdate +from sqlalchemy.ext.asyncio import AsyncSession + +from tests.test_infrastructure.test_database.test_models.factories import create_user + + +class TestUserRepository: + async def test_get_by_id( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user = await create_user(session) + session.expunge(user) + selected_user = await user_repository.get_by_id(user.id) + assert selected_user.id == user.id + assert selected_user.surname == user.surname + assert selected_user.name == user.name + assert selected_user.username == user.username + assert selected_user.email == user.email + assert selected_user.encrypted_password == user.encrypted_password + + async def test_get_by_id_not_exists( + self, + user_repository: UserRepository, + ) -> None: + selected_user = await user_repository.get_by_id(-1) + assert selected_user is None + + async def test_get_by_email( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user = await create_user(session) + session.expunge(user) + email = cast(EmailStr, user.email) + selected_user = await user_repository.get_by_email(email) + assert selected_user.id == user.id + assert selected_user.surname == user.surname + assert selected_user.name == user.name + assert selected_user.username == user.username + assert selected_user.email == user.email + assert selected_user.encrypted_password == user.encrypted_password + + async def test_get_by_email_not_exists( + self, + user_repository: UserRepository, + ) -> None: + selected_user = await user_repository.get_by_email("bot_exists_email@mail.ru") + assert selected_user is None + + async def test_get_by_username( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user = await create_user(session) + session.expunge(user) + selected_user = await user_repository.get_by_username(user.username) + assert selected_user.id == user.id + assert selected_user.surname == user.surname + assert selected_user.name == user.name + assert selected_user.username == user.username + assert selected_user.email == user.email + assert selected_user.encrypted_password == user.encrypted_password + + async def test_get_by_username_not_exists( + self, + user_repository: UserRepository, + ) -> None: + selected_user = await user_repository.get_by_username("not_exists_username") + assert selected_user is None + + async def test_create_user( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user_create_data = UserCreate( + surname="surname", + name="name", + username="username", + email="email@email.com", + encrypted_password="encrypted_password", # noqa: S106 + ) + created_user = await user_repository.create_user(user_create_data) + session.expunge(created_user) + selected_user = await user_repository.get_by_id(created_user.id) + assert selected_user.id is not None + assert selected_user.surname == user_create_data.surname + assert selected_user.name == user_create_data.name + assert selected_user.username == user_create_data.username + assert selected_user.email == user_create_data.email + assert selected_user.encrypted_password == user_create_data.encrypted_password + + async def test_update_user( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user = await create_user(session) + updated_user_data = UserUpdate( + surname="updated_surname", + name="updated_name", + username="updated_username", + email="updated_email@email.com", + ) + await user_repository.update_user(user.id, updated_user_data) + session.expunge(user) + selected_user = await user_repository.get_by_id(user.id) + assert selected_user.id is not None + assert selected_user.surname == updated_user_data.surname + assert selected_user.name == updated_user_data.name + assert selected_user.username == updated_user_data.username + assert selected_user.email == updated_user_data.email + assert selected_user.encrypted_password == user.encrypted_password + + async def test_delete_by_id( + self, + session: AsyncSession, + user_repository: UserRepository, + ) -> None: + user = await create_user(session) + await user_repository.delete_by_id(user.id) + session.expunge(user) + selected_user = await user_repository.get_by_id(user.id) + assert selected_user is None From 35a82721c7cab5f4406f4a6a66b36e250ee431ba Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Thu, 24 Sep 2026 19:45:23 +0300 Subject: [PATCH 4/6] test(tag repository): test tag repository methods --- .../test_repositories/conftest.py | 18 +++++ .../test_repositories/test_tag.py | 79 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 backend/tests/test_infrastructure/test_repositories/test_tag.py diff --git a/backend/tests/test_infrastructure/test_repositories/conftest.py b/backend/tests/test_infrastructure/test_repositories/conftest.py index 95e4e91..6c639ba 100644 --- a/backend/tests/test_infrastructure/test_repositories/conftest.py +++ b/backend/tests/test_infrastructure/test_repositories/conftest.py @@ -2,6 +2,9 @@ from infrastructure.database.repositories import ( FileRepository, OutboxRepository, + ProjectRepository, + RenderRepository, + TagRepository, UserRepository, ) from sqlalchemy.ext.asyncio import AsyncSession @@ -20,3 +23,18 @@ def outbox_repository(session: AsyncSession) -> OutboxRepository: @pytest.fixture(scope="function") def user_repository(session: AsyncSession) -> UserRepository: return UserRepository(session) + + +@pytest.fixture(scope="function") +def tag_repository(session: AsyncSession) -> TagRepository: + return TagRepository(session) + + +@pytest.fixture(scope="function") +def render_repository(session: AsyncSession) -> RenderRepository: + return RenderRepository(session) + + +@pytest.fixture(scope="function") +def project_repository(session: AsyncSession) -> ProjectRepository: + return ProjectRepository(session) diff --git a/backend/tests/test_infrastructure/test_repositories/test_tag.py b/backend/tests/test_infrastructure/test_repositories/test_tag.py new file mode 100644 index 0000000..a65daa3 --- /dev/null +++ b/backend/tests/test_infrastructure/test_repositories/test_tag.py @@ -0,0 +1,79 @@ +from infrastructure.database.repositories import TagRepository +from schemas.tag import TagCreate +from sqlalchemy.ext.asyncio import AsyncSession + +from tests.test_infrastructure.test_database.test_models.factories import ( + create_default_project, + create_default_tag, + create_tag, +) + + +class TestTagRepository: + async def test_get_by_id( + self, + session: AsyncSession, + tag_repository: TagRepository, + ) -> None: + tag = await create_default_tag(session) + selected_tag = await tag_repository.get_by_id(tag.id) + session.expunge(tag) + assert selected_tag.id == tag.id + assert selected_tag.name == tag.name + assert selected_tag.project_id == tag.project_id + + async def test_get_by_id_not_exists( + self, + tag_repository: TagRepository, + ) -> None: + selected_tag = await tag_repository.get_by_id(-1) + assert selected_tag is None + + async def test_get_project_tags( + self, + session: AsyncSession, + tag_repository: TagRepository, + ) -> None: + project = await create_default_project(session) + tag1 = await create_tag(session, project=project, name="tag1") + tag2 = await create_tag(session, project=project, name="tag2") + session.expunge(tag1) + session.expunge(tag2) + tags = await tag_repository.get_project_tags(project.id) + project_tags = sorted(tags, key=lambda tag: tag.id) + expected_tags = sorted([tag1, tag2], key=lambda tag: tag.id) + + assert project_tags[0].id == expected_tags[0].id + assert project_tags[0].name == expected_tags[0].name + assert project_tags[0].project_id == expected_tags[0].project_id + + assert project_tags[1].id == expected_tags[1].id + assert project_tags[1].name == expected_tags[1].name + assert project_tags[1].project_id == expected_tags[1].project_id + + async def test_create_tag( + self, + session: AsyncSession, + tag_repository: TagRepository, + ) -> None: + project = await create_default_project(session) + create_tag_data = TagCreate( + name="tag", + project_id=project.id, + ) + created_tag = await tag_repository.create_tag(create_tag_data) + selected_tag = await tag_repository.get_by_id(created_tag.id) + assert selected_tag.id is not None + assert selected_tag.name == created_tag.name + assert selected_tag.project_id == created_tag.project_id + + async def test_delete_by_id( + self, + session: AsyncSession, + tag_repository: TagRepository, + ) -> None: + tag = await create_default_tag(session) + await tag_repository.delete_by_id(tag.id) + session.expunge(tag) + selected_tag = await tag_repository.get_by_id(tag.id) + assert selected_tag is None From 59e37e3ce8f1f867d784f85b472c722dcdc1a82a Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Thu, 24 Sep 2026 21:27:39 +0300 Subject: [PATCH 5/6] test(render repository): test render repository methods --- .../database/repositories/render.py | 4 +- .../test_repositories/__init__.py | 0 .../test_repositories/conftest.py | 0 .../test_repositories/test_file.py | 0 .../test_repositories/test_outbox.py | 0 .../test_repositories/test_render.py | 72 +++++++++++++++++++ .../test_repositories/test_tag.py | 0 .../test_repositories/test_user.py | 0 8 files changed, 74 insertions(+), 2 deletions(-) rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/__init__.py (100%) rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/conftest.py (100%) rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/test_file.py (100%) rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/test_outbox.py (100%) create mode 100644 backend/tests/test_infrastructure/test_database/test_repositories/test_render.py rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/test_tag.py (100%) rename backend/tests/test_infrastructure/{ => test_database}/test_repositories/test_user.py (100%) diff --git a/backend/infrastructure/database/repositories/render.py b/backend/infrastructure/database/repositories/render.py index d9b96dd..b8bb5f1 100644 --- a/backend/infrastructure/database/repositories/render.py +++ b/backend/infrastructure/database/repositories/render.py @@ -16,7 +16,7 @@ class RenderRepository(AbstractRenderRepository): def __init__(self, session: AsyncSession) -> None: self.session = session - async def get_render(self, render_id: int) -> Render | None: + async def get_by_id(self, render_id: int) -> Render | None: stmt = ( select(Render) .options(joinedload(Render.file)) @@ -42,7 +42,7 @@ async def create_render(self, create_render_data: RenderCreate) -> Render: return render async def add_render_file(self, render_id: int, file_id: int) -> Render: - render = cast(Render, await self.get_render(render_id)) + render = cast(Render, await self.get_by_id(render_id)) render.file_id = file_id await self.session.flush() logger.debug( diff --git a/backend/tests/test_infrastructure/test_repositories/__init__.py b/backend/tests/test_infrastructure/test_database/test_repositories/__init__.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/__init__.py rename to backend/tests/test_infrastructure/test_database/test_repositories/__init__.py diff --git a/backend/tests/test_infrastructure/test_repositories/conftest.py b/backend/tests/test_infrastructure/test_database/test_repositories/conftest.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/conftest.py rename to backend/tests/test_infrastructure/test_database/test_repositories/conftest.py diff --git a/backend/tests/test_infrastructure/test_repositories/test_file.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_file.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/test_file.py rename to backend/tests/test_infrastructure/test_database/test_repositories/test_file.py diff --git a/backend/tests/test_infrastructure/test_repositories/test_outbox.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_outbox.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/test_outbox.py rename to backend/tests/test_infrastructure/test_database/test_repositories/test_outbox.py diff --git a/backend/tests/test_infrastructure/test_database/test_repositories/test_render.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_render.py new file mode 100644 index 0000000..c42abf8 --- /dev/null +++ b/backend/tests/test_infrastructure/test_database/test_repositories/test_render.py @@ -0,0 +1,72 @@ +from infrastructure.database.repositories import RenderRepository +from schemas.render import RenderCreate +from sqlalchemy.ext.asyncio import AsyncSession + +from tests.test_infrastructure.test_database.test_models.factories import ( + create_file, + create_render, +) + + +class TestRenderRepository: + async def test_get_by_id( + self, + session: AsyncSession, + render_repository: RenderRepository, + ) -> None: + render = await create_render(session) + session.expunge(render) + selected_render = await render_repository.get_by_id(render.id) + assert selected_render.id == render.id + assert selected_render.width == render.width + assert selected_render.height == render.height + assert selected_render.samples == render.samples + assert selected_render.denoiser == render.denoiser + assert selected_render.gpu == render.gpu + assert selected_render.file_id == render.file_id + + async def test_get_by_id_not_exist( + self, + render_repository: RenderRepository, + ) -> None: + selected_render = await render_repository.get_by_id(-1) + assert selected_render is None + + async def test_create_render( + self, + render_repository: RenderRepository, + ) -> None: + create_render_data = RenderCreate( + width=1000, + height=1000, + samples=100, + denoiser=False, + gpu=False, + ) + created_render = await render_repository.create_render(create_render_data) + selected_render = await render_repository.get_by_id(created_render.id) + assert selected_render.id == created_render.id + assert selected_render.width == create_render_data.width + assert selected_render.height == create_render_data.height + assert selected_render.samples == create_render_data.samples + assert selected_render.denoiser == create_render_data.denoiser + assert selected_render.gpu == created_render.gpu + assert selected_render.file_id == created_render.file_id + + async def test_add_render_file( + self, + session: AsyncSession, + render_repository: RenderRepository, + ) -> None: + file = await create_file(session) + render = await create_render(session) + await render_repository.add_render_file(render.id, file.id) + session.expunge(render) + selected_render = await render_repository.get_by_id(render.id) + assert selected_render.id == render.id + assert selected_render.width == render.width + assert selected_render.height == render.height + assert selected_render.samples == render.samples + assert selected_render.denoiser == render.denoiser + assert selected_render.gpu == render.gpu + assert selected_render.file_id == file.id diff --git a/backend/tests/test_infrastructure/test_repositories/test_tag.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_tag.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/test_tag.py rename to backend/tests/test_infrastructure/test_database/test_repositories/test_tag.py diff --git a/backend/tests/test_infrastructure/test_repositories/test_user.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_user.py similarity index 100% rename from backend/tests/test_infrastructure/test_repositories/test_user.py rename to backend/tests/test_infrastructure/test_database/test_repositories/test_user.py From 9b5243cbb6db875a0e76c693a1702d30ff2cc194 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokov Date: Thu, 24 Sep 2026 23:53:08 +0300 Subject: [PATCH 6/6] test(project repository): test project repository methods --- backend/tests/helpers.py | 7 + .../test_repositories/test_project.py | 272 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 backend/tests/test_infrastructure/test_database/test_repositories/test_project.py diff --git a/backend/tests/helpers.py b/backend/tests/helpers.py index fa7e7d0..d867357 100644 --- a/backend/tests/helpers.py +++ b/backend/tests/helpers.py @@ -3,6 +3,7 @@ from enum import StrEnum from _pytest._code import ExceptionInfo +from sqlalchemy.orm import DeclarativeBase from testcontainers.community.postgres import PostgresContainer @@ -39,3 +40,9 @@ def run_migrations(database_container: PostgresContainer) -> None: env=environment, check=True, ) + + +def model_to_dictionary(model: DeclarativeBase) -> dict: + return { + column.name: getattr(model, column.name) for column in model.__table__.columns + } diff --git a/backend/tests/test_infrastructure/test_database/test_repositories/test_project.py b/backend/tests/test_infrastructure/test_database/test_repositories/test_project.py new file mode 100644 index 0000000..a253272 --- /dev/null +++ b/backend/tests/test_infrastructure/test_database/test_repositories/test_project.py @@ -0,0 +1,272 @@ +import pytest +from core.constants import ProjectVisibility, RenderStatus +from infrastructure.database.repositories import ProjectRepository +from schemas.project import ProjectCreate, ProjectPartialUpdate +from sqlalchemy.ext.asyncio import AsyncSession + +from tests.helpers import model_to_dictionary +from tests.test_infrastructure.test_database.test_models.factories import ( + create_default_project, + create_file, + create_project, + create_render, + create_user, +) + + +class TestProjectRepository: + async def test_get_by_id( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + user = await create_user(session) + source_file = await create_file( + session, + bucket="fsource_file_bucket", + key="source_file_key", + ) + file = await create_file( + session, + bucket="file_bucket", + key="file_key", + ) + render = await create_render(session, file=file) + project = await create_project( + session, + user=user, + source_file=source_file, + render=render, + ) + selected_project = await project_repository.get_by_id(project.id) + session.expunge_all() + assert model_to_dictionary(selected_project) == model_to_dictionary(project) + assert model_to_dictionary(selected_project.render) == model_to_dictionary( + render, + ) + assert model_to_dictionary(selected_project.render.file) == model_to_dictionary( + file, + ) + assert model_to_dictionary(selected_project.source_file) == model_to_dictionary( + source_file, + ) + + async def test_get_by_id_not_exist( + self, + project_repository: ProjectRepository, + ) -> None: + selected_project = await project_repository.get_by_id(-1) + assert selected_project is None + + async def test_get_project_owner( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project_owner = await create_user(session) + source_file = await create_file( + session, + bucket="fsource_file_bucket", + key="source_file_key", + ) + render = await create_render(session) + project = await create_project( + session, + user=project_owner, + source_file=source_file, + render=render, + ) + selected_project_owner = await project_repository.get_project_owner(project.id) + assert model_to_dictionary(selected_project_owner) == model_to_dictionary( + project_owner, + ) + + async def test_get_project_owner_not_exist( + self, + project_repository: ProjectRepository, + ) -> None: + selected_project_owner = await project_repository.get_project_owner(-1) + assert selected_project_owner is None + + async def test_get_public_projects( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project = await create_default_project( + session, + visibility=ProjectVisibility.public, + ) + selected_public_projects = await project_repository.get_public_projects( + size=10, + page=1, + ) + assert len(selected_public_projects) == 1 + assert model_to_dictionary(selected_public_projects[0]) == model_to_dictionary( + project, + ) + + async def test_get_public_projects_not_exists( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + await create_default_project(session, visibility=ProjectVisibility.private) + selected_public_projects = await project_repository.get_public_projects( + size=10, + page=1, + ) + assert len(selected_public_projects) == 0 + + @pytest.mark.parametrize( + "visibility", + [ + ProjectVisibility.public, + ProjectVisibility.private, + ], + ) + async def test_get_user_projects( + self, + session: AsyncSession, + project_repository: ProjectRepository, + visibility: str, + ) -> None: + project_owner = await create_user(session) + source_file = await create_file( + session, + bucket="fsource_file_bucket", + key="source_file_key", + ) + render = await create_render(session) + project = await create_project( + session, + user=project_owner, + source_file=source_file, + render=render, + visibility=visibility, + ) + selected_user_projects = await project_repository.get_user_projects( + project_owner.id, + size=10, + page=1, + ) + assert len(selected_user_projects) == 1 + assert model_to_dictionary(selected_user_projects[0]) == model_to_dictionary( + project, + ) + + async def test_get_user_public_projects( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project_owner = await create_user(session) + source_file = await create_file( + session, + bucket="fsource_file_bucket", + key="source_file_key", + ) + render = await create_render(session) + public_project = await create_project( + session, + user=project_owner, + source_file=source_file, + render=render, + visibility="public", + ) + await create_project( + session, + user=project_owner, + source_file=source_file, + render=render, + visibility="private", + ) + selected_user_public_projects = ( + await project_repository.get_user_public_projects( + project_owner.id, + size=10, + page=1, + ) + ) + assert len(selected_user_public_projects) == 1 + assert model_to_dictionary( + selected_user_public_projects[0], + ) == model_to_dictionary( + public_project, + ) + + async def test_update_project_status( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project = await create_default_project(session) + await project_repository.update_project_status(project.id) + expected_project_data = model_to_dictionary(project) + expected_project_data["status"] = RenderStatus.completed + assert model_to_dictionary(project) == expected_project_data + + async def test_create_project( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + user = await create_user(session) + source_file = await create_file(session) + render = await create_render(session) + create_project_data = ProjectCreate( + name="project_name", + description="description", + source_file_id=source_file.id, + visibility=ProjectVisibility.public, + ) + created_project = await project_repository.create_project( + user.id, + render.id, + create_project_data, + ) + session.expunge(created_project) + selected_project = await project_repository.get_by_id(created_project.id) + assert selected_project.name == create_project_data.name + assert selected_project.description == create_project_data.description + assert selected_project.visibility == create_project_data.visibility + assert selected_project.user_id == user.id + assert selected_project.source_file_id == source_file.id + assert selected_project.render_id == render.id + assert selected_project.status == RenderStatus.rendering + + async def test_partial_update_project( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project = await create_default_project(session) + partial_update_project_data = ProjectPartialUpdate( + name="updated_project_name", + description="updated_description", + visibility=ProjectVisibility.private, + ) + await project_repository.partial_update_project( + project.id, + partial_update_project_data, + ) + session.expunge(project) + selected_project = await project_repository.get_by_id(project.id) + assert selected_project.name == partial_update_project_data.name + assert selected_project.description == partial_update_project_data.description + assert selected_project.visibility == partial_update_project_data.visibility + assert selected_project.user_id == project.user_id + assert selected_project.source_file_id == project.source_file_id + assert selected_project.render_id == project.render_id + assert selected_project.status == project.status + + async def test_delete_by_id( + self, + session: AsyncSession, + project_repository: ProjectRepository, + ) -> None: + project = await create_default_project(session) + await project_repository.delete_by_id(project.id) + session.expunge(project) + deleted_project = await project_repository.get_by_id(project.id) + assert deleted_project is None