Skip to content

Commit

Permalink
M2-4180: Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ibogretsov committed Jan 23, 2024
1 parent 6e39d1b commit 0a8cdd1
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 34 deletions.
19 changes: 12 additions & 7 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from typing import Any, AsyncGenerator, cast
from typing import Any, AsyncGenerator, Generator, cast

import pytest
import taskiq_fastapi
Expand All @@ -8,6 +8,7 @@
from fastapi import FastAPI
from pytest import Parser
from pytest_asyncio import is_async_test
from pytest_mock import MockerFixture
from sqlalchemy import event
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, AsyncSession
from sqlalchemy.orm import Session, SessionTransaction
Expand All @@ -26,6 +27,7 @@
"apps.activities.tests.fixtures.items",
"apps.activities.tests.fixtures.conditional_logic",
"apps.activities.tests.fixtures.scores_reports",
"apps.users.tests.fixtures.users",
]


Expand Down Expand Up @@ -169,14 +171,13 @@ def remote_image() -> str:


@pytest.fixture
async def mock_kiq_report(mocker):
def mock_kiq_report(mocker: MockerFixture):
mock = mocker.patch("apps.answers.service.create_report.kiq")
yield mock
mock.stop_all()


@pytest.fixture
async def mock_report_server_response(mocker):
def mock_report_server_response(mocker: MockerFixture) -> Generator:
def json_():
return dict(
pdf="cGRmIGJvZHk=",
Expand All @@ -192,11 +193,15 @@ def json_():
mock.return_value.__aenter__.return_value.status = 200
mock.return_value.__aenter__.return_value.json.side_effect = json_
yield mock
mock.stop_all()


@pytest.fixture
def mock_reencrypt_kiq(mocker):
def mock_reencrypt_kiq(mocker: MockerFixture) -> Generator:
mock = mocker.patch("apps.users.api.password.reencrypt_answers.kiq")
yield mock
mock.stop_all()


@pytest.fixture
def mock_activity_log(mocker: MockerFixture) -> Generator:
mock = mocker.patch("apps.logs.services.UserActivityLogService.create_log")
yield mock
2 changes: 1 addition & 1 deletion src/apps/authentication/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
)
from apps.authentication.services.security import AuthenticationService
from apps.logs.domain.constants import UserActivityEvent, UserActivityEventType
from apps.logs.services.user_activity_log import UserActivityLogService
from apps.logs.services import UserActivityLogService
from apps.shared.domain.response import Response
from apps.shared.response import EmptyResponse
from apps.users import UsersCRUD
Expand Down
78 changes: 60 additions & 18 deletions src/apps/authentication/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import http
import uuid
from unittest.mock import ANY, AsyncMock

import pytest

from apps.authentication.domain.login import UserLoginRequest
from apps.authentication.domain.token import RefreshAccessTokenRequest
Expand All @@ -9,6 +13,12 @@
from apps.users import UsersCRUD
from apps.users.domain import UserCreateRequest
from apps.users.router import router as user_router
from infrastructure.http.domain import MindloggerContentSource


@pytest.fixture
def device_id() -> uuid.UUID:
return uuid.uuid4()


class TestAuthentication(BaseTest):
Expand Down Expand Up @@ -44,7 +54,7 @@ async def test_get_token(self, session, client):
email=self.create_request_user.dict()["email"]
)

assert response.status_code == 200
assert response.status_code == http.HTTPStatus.OK
data = response.json()["result"]
assert set(data.keys()) == {"user", "token"}
assert data["user"]["id"] == str(user.id)
Expand All @@ -68,7 +78,7 @@ async def test_delete_access_token(self, client):
url=self.delete_token_url,
)

assert response.status_code == 200
assert response.status_code == http.HTTPStatus.OK

async def test_refresh_access_token(self, client):
# Creating new user
Expand All @@ -91,24 +101,24 @@ async def test_refresh_access_token(self, client):
data=refresh_access_token_request.dict(),
)

assert response.status_code == 200
assert response.status_code == http.HTTPStatus.OK

async def test_login_and_logout_device(self, client):
async def test_login_and_logout_device(self, client, device_id):
await client.post(
self.user_create_url, data=self.create_request_user.dict()
)
device_id = str(uuid.uuid4())

login_request_user: UserLoginRequest = UserLoginRequest(
**self.create_request_user.dict(), device_id=device_id
**self.create_request_user.dict(), device_id=str(device_id)
)
response = await client.post(
url=self.login_url,
data=login_request_user.dict(),
)
assert response.status_code == 200
assert response.status_code == http.HTTPStatus.OK

await client.login(
self.login_url,
self.create_request_user.email,
self.create_request_user.password,
)
Expand All @@ -118,25 +128,57 @@ async def test_login_and_logout_device(self, client):
data=dict(device_id=device_id),
)

assert response.status_code == 200
assert response.status_code == http.HTTPStatus.OK

async def test_login_event_log_is_crated_after_login(self, client):
async def test_login_event_log_is_created_after_login(
self, mock_activity_log: AsyncMock, client
):
await client.post(
self.user_create_url, data=self.create_request_user.dict()
)
device_id = str(uuid.uuid4())

login_request_user: UserLoginRequest = UserLoginRequest(
**self.create_request_user.dict(), device_id=device_id
**self.create_request_user.dict()
)
response = await client.post(
url=self.login_url,
data=login_request_user.dict(),
)
assert response.status_code == 200

await client.login(
self.login_url,
self.create_request_user.email,
self.create_request_user.password,
assert response.status_code == http.HTTPStatus.OK
mock_activity_log.assert_awaited_once()

@pytest.mark.parametrize(
"header_value,dest_value",
(
(MindloggerContentSource.admin, MindloggerContentSource.admin),
(MindloggerContentSource.mobile, MindloggerContentSource.mobile),
(MindloggerContentSource.web, MindloggerContentSource.web),
(
MindloggerContentSource.undefined,
MindloggerContentSource.undefined,
),
("test", MindloggerContentSource.undefined),
),
)
async def test_login_if_default_mindollger_content_source_header_is_undefined( # noqa: E501
self,
mock_activity_log: AsyncMock,
client,
header_value: str,
dest_value: str,
):
await client.post(
self.user_create_url, data=self.create_request_user.dict()
)
login_request_user: UserLoginRequest = UserLoginRequest(
**self.create_request_user.dict()
)
response = await client.post(
url=self.login_url,
data=login_request_user.dict(),
headers={"Mindlogger-Content-Source": header_value},
)
assert response.status_code == http.HTTPStatus.OK
user_id = uuid.UUID(response.json()["result"]["user"]["id"])
mock_activity_log.assert_awaited_once_with(
user_id, None, ANY, ANY, ANY, dest_value
)
1 change: 0 additions & 1 deletion src/apps/logs/crud/user_activity_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ class UserActivityLogCRUD(BaseCRUD[UserActivityLogSchema]):
async def save(
self, schema: UserActivityLogSchema
) -> UserActivityLogSchema:
"""Return UserActivityLogSchema instance."""
return await self._create(schema)
2 changes: 1 addition & 1 deletion src/apps/logs/db/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class UserActivityLogSchema(Base):
user_agent = Column(String(), nullable=True)
mindlogger_content = Column(String(), nullable=False)

def __repr__(self):
def __repr__(self) -> str:
return (
f"UserActivityLogSchema(id='{self.id}', user_id='{self.user_id}',"
f" event_type='{self.event_type}', event='{self.event}')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ async def create_log(
user_agent: str | None,
mindlogger_content: str,
) -> UserActivityLogSchema:
# TODO: remove this remporary solution when mobile is ready
if (
mindlogger_content == MindloggerContentSource.undefined.name
and firebase_token_id
Expand Down
17 changes: 17 additions & 0 deletions src/apps/logs/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Any

import pytest

from apps.logs.domain.constants import UserActivityEvent, UserActivityEventType
from infrastructure.http.domain import MindloggerContentSource


@pytest.fixture
def base_log_data() -> dict[str, Any]:
return {
"firebase_token_id": None,
"event": UserActivityEvent.LOGIN,
"event_type": UserActivityEventType.LOGIN,
"user_agent": "test",
"mindlogger_content": MindloggerContentSource.undefined,
}
25 changes: 25 additions & 0 deletions src/apps/logs/tests/test_services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from apps.logs.services import UserActivityLogService
from apps.users.db.schemas import UserSchema
from infrastructure.http.domain import MindloggerContentSource


async def test_create_log_success(
session: AsyncSession, user_tom: UserSchema, base_log_data: dict[str, Any]
) -> None:
base_log_data["user_id"] = user_tom.id
log = await UserActivityLogService(session).create_log(**base_log_data)
assert log.user_id == user_tom.id


async def test_create_log_undefined_with_device_is_mobile_content(
session: AsyncSession, user_tom: UserSchema, base_log_data: dict[str, Any]
) -> None:
base_log_data["user_id"] = user_tom.id
base_log_data["firebase_token_id"] = "token"
base_log_data["mindlogger_content"] = MindloggerContentSource.undefined
log = await UserActivityLogService(session).create_log(**base_log_data)
assert log.mindlogger_content == MindloggerContentSource.mobile
File renamed without changes.
5 changes: 3 additions & 2 deletions src/apps/shared/test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ class BaseTest:
fixtures: list[str] = []

@pytest.fixture(scope="class", autouse=True)
async def initialize(self):
await truncate_tables()
async def initialize(self, request):
await self.populate_db()
yield
await truncate_tables()

@pytest.fixture(autouse=True)
async def clear_mails(self):
Expand Down
1 change: 1 addition & 0 deletions src/apps/users/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async def user_create(
raise PasswordHasSpacesError()
async with atomic(session):
email_hash = hash_sha224(user_create_schema.email)
# TODO: Move Logic to the service
user_schema = await UsersCRUD(session).save(
UserSchema(
email=email_hash,
Expand Down
Empty file.
42 changes: 42 additions & 0 deletions src/apps/users/tests/fixtures/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from typing import AsyncGenerator

import pytest
from sqlalchemy.ext.asyncio import AsyncSession

from apps.authentication.services import AuthenticationService
from apps.shared.hashing import hash_sha224
from apps.users.cruds.user import UsersCRUD
from apps.users.db.schemas import UserSchema
from apps.users.domain import UserCreateRequest


@pytest.fixture
def user_tom_create() -> UserCreateRequest:
# Use tom data for replacing json fixtures with pytest fixtures
# without failing tests
return UserCreateRequest(
email="[email protected]",
password="Test1234!",
first_name="Tom",
last_name="Isaak",
)


@pytest.fixture
async def user_tom(
user_tom_create: UserCreateRequest, session: AsyncSession
) -> AsyncGenerator:
email_hash = hash_sha224(user_tom_create.email)
hashed_password = AuthenticationService.get_password_hash(
user_tom_create.password
)
user = await UsersCRUD(session).save(
UserSchema(
email=email_hash,
email_encrypted=user_tom_create.email,
first_name=user_tom_create.first_name,
last_name=user_tom_create.last_name,
hashed_password=hashed_password,
)
)
yield user
1 change: 0 additions & 1 deletion src/infrastructure/database/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from sqlalchemy.ext.hybrid import hybrid_method
from sqlalchemy.orm import declarative_base, declarative_mixin


__all__ = ["Base", "MigratedMixin"]

meta = MetaData(
Expand Down
5 changes: 2 additions & 3 deletions src/infrastructure/http/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ async def get_mindlogger_content_source(

return getattr(
MindloggerContentSource,
request.headers.get(
"mindlogger-content-source", MindloggerContentSource.undefined.name
),
request.headers.get("mindlogger-content-source", ""),
MindloggerContentSource.undefined,
)


Expand Down

0 comments on commit 0a8cdd1

Please sign in to comment.