From 8bc3ff7b54f3b707d102aaee68aea0a67aa95a5a Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Wed, 19 Aug 2026 00:22:59 +0300 Subject: [PATCH 1/2] unit & interga tests --- configs/.e2e.env.example | 4 +++ tests/e2e/test_users_crud.py | 1 - tests/integrated/conftest.py | 8 +++++- tests/integrated/test_auth.py | 2 ++ tests/integrated/test_crud.py | 16 ++++++++++++ tests/unit/conftest.py | 7 +++++ tests/unit/test_crud.py | 48 +++++++++++++++++++++++++++++++++++ 7 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 configs/.e2e.env.example create mode 100644 tests/integrated/test_crud.py create mode 100644 tests/unit/test_crud.py diff --git a/configs/.e2e.env.example b/configs/.e2e.env.example new file mode 100644 index 0000000..d689630 --- /dev/null +++ b/configs/.e2e.env.example @@ -0,0 +1,4 @@ +HOST="change_me" +PORT="change_me" +TEST_USERNAME="change_me" +TEST_PASSWORD="change_me" \ No newline at end of file diff --git a/tests/e2e/test_users_crud.py b/tests/e2e/test_users_crud.py index d30ca64..6795bda 100644 --- a/tests/e2e/test_users_crud.py +++ b/tests/e2e/test_users_crud.py @@ -1,4 +1,3 @@ - from uuid import uuid4 import allure diff --git a/tests/integrated/conftest.py b/tests/integrated/conftest.py index 7d8a7d2..635927b 100644 --- a/tests/integrated/conftest.py +++ b/tests/integrated/conftest.py @@ -2,6 +2,7 @@ import pytest_asyncio from fastapi import Request from src.service.auth.auth import CurrentUserService +from src.service.users_crud.users_crud import CrudService @pytest_asyncio.fixture @@ -13,4 +14,9 @@ async def current_user_service()->CurrentUserService: async def requests(mocker): fake_request = mocker.MagicMock(spec=Request) fake_request.headers = {"user-agent": "pytest-agent", "x-forwarded-for":"127.0.0.1"} - return fake_request \ No newline at end of file + return fake_request + +@pytest_asyncio.fixture +async def crud_service()->CrudService: + crud_service=CrudService() + return crud_service \ No newline at end of file diff --git a/tests/integrated/test_auth.py b/tests/integrated/test_auth.py index d8efa94..46ec0a5 100644 --- a/tests/integrated/test_auth.py +++ b/tests/integrated/test_auth.py @@ -41,6 +41,8 @@ class TestAuth: assert test_result.email==user_data.email assert test_result.direct_permissions==user_data.direct_permissions assert test_result.group==user_data.group + assert not hasattr(test_result, "password") or not hasattr(test_result, "plain_password") or not hasattr(test_result, "hashed_password") + assert not hasattr(test_result, "status") @pytest.mark.parametrize("user_data, uuid, expected_exception,expected_status",[ diff --git a/tests/integrated/test_crud.py b/tests/integrated/test_crud.py new file mode 100644 index 0000000..48e887c --- /dev/null +++ b/tests/integrated/test_crud.py @@ -0,0 +1,16 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import allure +import pytest + +from src.service.users_crud.users_crud import CrudService + + +@pytest.mark.integra +class TestCrud: + + async def test_create_user_positive(self, crud_service:CrudService, monkeypatch)->None: + + with allure.step("Patching functions"): + pass \ No newline at end of file diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 139597f..3ee8c0e 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,2 +1,9 @@ +import pytest_asyncio + +from src.service.users_crud.users_crud import CrudService +@pytest_asyncio.fixture +async def crud_service()->CrudService: + crud_service=CrudService() + return crud_service \ No newline at end of file diff --git a/tests/unit/test_crud.py b/tests/unit/test_crud.py new file mode 100644 index 0000000..6198808 --- /dev/null +++ b/tests/unit/test_crud.py @@ -0,0 +1,48 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import allure +import pytest +from fastapi import HTTPException + +from src.service.users_crud.users_crud import CrudService + + +@pytest.mark.unit +class TestCrud: + + @pytest.mark.parametrize("user_data",[ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True),id="Get_user_by_email_positive") + ]) + async def test_get_user_by_email_positive(self, monkeypatch, user_data:SimpleNamespace, crud_service:CrudService)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "get_user_by_email",AsyncMock(return_value=user_data)) + + with allure.step("Test get_by_email"): + test_result = await crud_service.get_user_by_email(user_data.email) + + assert test_result.email==user_data.email + assert test_result.first_name==user_data.first_name + assert test_result.last_name==user_data.last_name + assert test_result.middle_name==user_data.middle_name + assert test_result.email==user_data.email + assert test_result.email==user_data.email + assert not hasattr(test_result, "password") or not hasattr(test_result, "plain_password") or not hasattr(test_result, "hashed_password") + assert test_result.direct_permissions==user_data.direct_permissions + assert test_result.group==user_data.group + assert not hasattr(test_result, "status") + + + @pytest.mark.parametrize("email, expected_exception, expected_status",[ + pytest.param("Wrong_email", HTTPException, 404,id="Wrong_email"), + pytest.param("",HTTPException, 404,id="Empty_email"), + ]) + async def test_get_user_by_email_negative(self, email, crud_service:CrudService, expected_exception, expected_status:int)->None: + + with allure.step("Test get_by_email"), pytest.raises(expected_exception) as exc_info: + + await crud_service.get_user_by_email(email) + + if expected_exception is HTTPException: + assert exc_info.value.status_code==expected_status \ No newline at end of file From 73e983c6a55e2d7e63c148c2920fe4936875613f Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Wed, 19 Aug 2026 11:08:33 +0300 Subject: [PATCH 2/2] all the remain tests --- src/service/users_crud/users_crud.py | 2 +- tests/e2e/conftest.py | 2 +- tests/e2e/test_users_crud.py | 10 ++--- tests/integrated/test_crud.py | 56 +++++++++++++++++++++++++++- tests/unit/test_crud.py | 55 +++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/service/users_crud/users_crud.py b/src/service/users_crud/users_crud.py index 2889337..ccbea5f 100644 --- a/src/service/users_crud/users_crud.py +++ b/src/service/users_crud/users_crud.py @@ -52,7 +52,7 @@ class CrudService: raise self.errors.not_found_error(detail="User not found") return user_entity - async def delete_user_hard(self, email:str, current_user:UserOut)->bool: + async def delete_user_hard(self, email:str, current_user)->bool: user_entity=await self.crud_db_actions.delete_user_hard(email) diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 917ae2a..9d112eb 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -27,7 +27,7 @@ class MySession(requests_async.AsyncSession): kwargs.setdefault('headers', self.headers) return await super().request(method, url, **kwargs) -@pytest_asyncio.fixture(scope="function", autouse=True) +@pytest_asyncio.fixture(scope="function") async def auth_fixture(): payload = {"username": e2e_settings.TEST_USERNAME, "password": e2e_settings.TEST_PASSWORD} diff --git a/tests/e2e/test_users_crud.py b/tests/e2e/test_users_crud.py index 6795bda..de95166 100644 --- a/tests/e2e/test_users_crud.py +++ b/tests/e2e/test_users_crud.py @@ -223,7 +223,7 @@ class TestCrud: @pytest.mark.parametrize("test_user_fixture", [ - ([], []) + (["admin"], ["admin_group"]) ], indirect=True) @pytest.mark.parametrize("user_record_to_update",[ pytest.param({"first_name": "Test_New"},id="Positive_user_update_partially") @@ -245,7 +245,7 @@ class TestCrud: response.raise_for_status() response=response.json() - actual_permissions = [item.get("direct_permissions") for item in response.get("direct_permissions")] #unpacking json like {group:[{},{}]} + actual_permissions = [item.get("permission") for item in response.get("direct_permissions")] #unpacking json like {group:[{},{}]} actual_groups =[item.get("group") for item in response.get("group")] @@ -253,8 +253,8 @@ class TestCrud: assert response.get("first_name")==user_record_to_update["first_name"] assert response.get("last_name")==new_user_record["last_name"] assert response.get("middle_name")==new_user_record["middle_name"] - assert actual_permissions == new_user_record["direct_permissions"] or actual_permissions == [None] - assert actual_groups == new_user_record["group"] or actual_groups == [None] + assert actual_permissions == new_user_record["direct_permissions"] + assert actual_groups == new_user_record["group"] assert not response.get("hashed_password") or not response.get("plain_password") or not response.get("password") @@ -265,7 +265,7 @@ class TestCrud: @pytest.mark.parametrize("user_record_to_update, expected_exception, expected_status",[ pytest.param({"plain_password": "Wrong_pass"},HTTPStatusError,422,id="Wrong_password"), pytest.param({"email": "Wrong_email"},HTTPStatusError,422,id="Wrong_email"), - pytest.param({},HTTPStatusError, 400,id="Positive_user_update_nothing") + pytest.param({},HTTPStatusError, 400,id="Negative_user_update_nothing") ]) async def test_user_update_partially_negative(self, test_user_fixture, user_record_to_update:dict, expected_exception, expected_status:int)->None: diff --git a/tests/integrated/test_crud.py b/tests/integrated/test_crud.py index 48e887c..666d7b2 100644 --- a/tests/integrated/test_crud.py +++ b/tests/integrated/test_crud.py @@ -3,14 +3,66 @@ from unittest.mock import AsyncMock import allure import pytest +from fastapi import HTTPException +from src.models.pydantic_models.model import UserCreate, UserUpdate from src.service.users_crud.users_crud import CrudService @pytest.mark.integra class TestCrud: - async def test_create_user_positive(self, crud_service:CrudService, monkeypatch)->None: + @pytest.mark.parametrize("user_data", [ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[], plain_password="Test1234!"),id="Create_user_positive") + ]) + async def test_create_user_positive(self, crud_service:CrudService, monkeypatch, user_data:SimpleNamespace)->None: with allure.step("Patching functions"): - pass \ No newline at end of file + monkeypatch.setattr(crud_service.crud_db_actions, "create_user", AsyncMock(return_value=user_data)) + + with allure.step("Test Create User"): + await crud_service.create_user(UserCreate.model_validate(user_data)) + + + @pytest.mark.parametrize("user_data,expected_exception,expected_status ", [ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[], plain_password="Test1234!"),HTTPException,422,id="Create_user_None") + ]) + async def test_create_user_negative(self, crud_service:CrudService, monkeypatch, user_data:SimpleNamespace, expected_exception, expected_status:int)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "create_user", AsyncMock(return_value=None)) + + with allure.step("Test Create User"), pytest.raises(expected_exception) as exc_info: + await crud_service.create_user(UserCreate.model_validate(user_data)) + + if expected_exception is HTTPException: + assert exc_info.value.status_code == expected_status + + @pytest.mark.parametrize("user_data", [ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[], plain_password="Test1234!"),id="Create_user_positive") + ]) + async def test_update_user_positive(self, crud_service:CrudService, monkeypatch, user_data:SimpleNamespace)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "update_user_partially", AsyncMock(return_value=user_data)) + + with allure.step("Test Update User"): + await crud_service.update_user(user_data.email,UserUpdate.model_validate(user_data)) + + + @pytest.mark.parametrize("user_data,expected_exception,expected_status ", [ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[], plain_password="Test1234!"),HTTPException,404,id="Update_user_None"), + pytest.param(SimpleNamespace(),HTTPException,400,id="Empty_user_data") + ]) + async def test_update_user_negative(self, crud_service:CrudService, monkeypatch, user_data:SimpleNamespace, expected_exception, expected_status:int)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "update_user_partially", AsyncMock(return_value=None)) + + with allure.step("Test Update User"), pytest.raises(expected_exception) as exc_info: + await crud_service.update_user("d@d.d",UserUpdate.model_validate(user_data)) + + if expected_exception is HTTPException: + assert exc_info.value.status_code == expected_status + + \ No newline at end of file diff --git a/tests/unit/test_crud.py b/tests/unit/test_crud.py index 6198808..612e2ee 100644 --- a/tests/unit/test_crud.py +++ b/tests/unit/test_crud.py @@ -44,5 +44,60 @@ class TestCrud: await crud_service.get_user_by_email(email) + if expected_exception is HTTPException: + assert exc_info.value.status_code==expected_status + + @pytest.mark.parametrize("user_data",[ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True),id="Get_user_by_email_positive") + ]) + async def test_delete_user_soft_positive(self, monkeypatch, user_data:SimpleNamespace, crud_service:CrudService)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "delete_user_soft",AsyncMock(return_value=True)) + + with allure.step("Test get_by_email"): + test_result = await crud_service.delete_user_soft(user_data.email) + + assert test_result == True + + + @pytest.mark.parametrize("email, expected_exception, expected_status",[ + pytest.param("Wrong_email", HTTPException, 404,id="Wrong_email"), + pytest.param("",HTTPException, 404,id="Empty_email"), + ]) + async def test_delete_user_soft_negative(self, email, crud_service:CrudService, expected_exception, expected_status:int)->None: + + with allure.step("Test get_by_email"), pytest.raises(expected_exception) as exc_info: + + await crud_service.delete_user_soft(email) + + if expected_exception is HTTPException: + assert exc_info.value.status_code==expected_status + + + @pytest.mark.parametrize("user_data",[ + pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True),id="Get_user_by_email_positive") + ]) + async def test_delete_user_hard_positive(self, monkeypatch, user_data:SimpleNamespace, crud_service:CrudService)->None: + + with allure.step("Patching functions"): + monkeypatch.setattr(crud_service.crud_db_actions, "delete_user_hard",AsyncMock(return_value=True)) + + with allure.step("Test get_by_email"): + test_result = await crud_service.delete_user_hard(user_data.email, "current_user") + + assert test_result==True + + + @pytest.mark.parametrize("email, expected_exception, expected_status",[ + pytest.param("Wrong_email", HTTPException, 404,id="Wrong_email"), + pytest.param("",HTTPException, 404,id="Empty_email"), + ]) + async def test_delete_user_hard_negative(self, email, crud_service:CrudService, expected_exception, expected_status:int)->None: + + with allure.step("Test get_by_email"), pytest.raises(expected_exception) as exc_info: + + await crud_service.delete_user_hard(email, "current_user") + if expected_exception is HTTPException: assert exc_info.value.status_code==expected_status \ No newline at end of file