-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests/unit_tests: update user related tests
Integrate `mongomock_motor` package to mock async mongo client for Beanie initialization. Add fixture to initialize Beanie on app startup. Remove fixtures and add methods to get current active user. Update `app.dependency_overrides` dictionary to mock dependency callable from `fastapi-users` to get users while creating `TestClient`. Update all the user related tests. Add environment variable to `pytest` command in Github actions workflow. Signed-off-by: Jeny Sadadia <[email protected]>
- Loading branch information
Jeny Sadadia
committed
Oct 31, 2023
1 parent
8e3700a
commit 3cc1fc0
Showing
11 changed files
with
306 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,13 +54,9 @@ jobs: | |
pylint api.user_models | ||
pylint tests/unit_tests | ||
- name: Export environment variables | ||
run: | | ||
echo "SECRET_KEY=$(openssl rand -hex 32)" >> $GITHUB_ENV | ||
- name: Run pytest | ||
run: | | ||
pytest -v tests/unit_tests | ||
SECRET_KEY=$(openssl rand -hex 32) SMTP_HOST=smtp.gmail.com SMTP_PORT=465 [email protected] EMAIL_PASSWORD=random pytest -vs tests/unit_tests/ | ||
lint: | ||
runs-on: ubuntu-22.04 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ pytest-dependency==0.5.1 | |
pytest-mock==3.6.1 | ||
pytest-order==1.0.1 | ||
httpx==0.23.3 | ||
mongomock_motor==0.0.21 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,10 +14,20 @@ | |
import asyncio | ||
import fakeredis.aioredis | ||
from fastapi.testclient import TestClient | ||
from fastapi import Request, HTTPException, status | ||
import pytest | ||
|
||
from api.main import app | ||
from api.models import User, UserGroup, UserProfile | ||
from mongomock_motor import AsyncMongoMockClient | ||
from beanie import init_beanie | ||
from httpx import AsyncClient | ||
|
||
from api.main import ( | ||
app, | ||
versioned_app, | ||
get_current_user, | ||
get_current_superuser, | ||
) | ||
from api.models import UserGroup | ||
from api.user_models import User | ||
from api.pubsub import PubSub | ||
|
||
BEARER_TOKEN = "Bearer \ | ||
|
@@ -33,13 +43,77 @@ | |
BASE_URL = f'http://testserver/{API_VERSION}/' | ||
|
||
|
||
def mock_get_current_user(request: Request): | ||
""" | ||
Get current active user | ||
""" | ||
token = request.headers.get('authorization') | ||
if not token: | ||
raise HTTPException( | ||
status_code=status.HTTP_401_UNAUTHORIZED, | ||
detail="Missing token", | ||
) | ||
return User( | ||
id='65265305c74695807499037f', | ||
username='bob', | ||
hashed_password='$2b$12$CpJZx5ooxM11bCFXT76/z.o6HWs2sPJy4iP8.' | ||
'xCZGmM8jWXUXJZ4L', | ||
email='[email protected]', | ||
is_active=True, | ||
is_superuser=False, | ||
is_verified=True | ||
) | ||
|
||
|
||
def mock_get_current_admin_user(request: Request): | ||
""" | ||
Get current active admin user | ||
""" | ||
token = request.headers.get('authorization') | ||
if not token: | ||
raise HTTPException( | ||
status_code=status.HTTP_401_UNAUTHORIZED, | ||
detail="Missing token", | ||
) | ||
if token != ADMIN_BEARER_TOKEN: | ||
raise HTTPException( | ||
status_code=status.HTTP_403_FORBIDDEN, | ||
detail="Forbidden", | ||
) | ||
return User( | ||
id='653a5e1a7e9312c86f8f86e1', | ||
username='admin', | ||
hashed_password='$2b$12$CpJZx5ooxM11bCFXT76/z.o6HWs2sPJy4iP8.' | ||
'xCZGmM8jWXUXJZ4K', | ||
email='[email protected]', | ||
groups=[UserGroup(name='admin')], | ||
is_active=True, | ||
is_superuser=True, | ||
is_verified=True | ||
) | ||
|
||
|
||
app.dependency_overrides[get_current_user] = mock_get_current_user | ||
app.dependency_overrides[get_current_superuser] = mock_get_current_admin_user | ||
|
||
|
||
@pytest.fixture | ||
def test_client(): | ||
"""Fixture to get FastAPI Test client instance""" | ||
with TestClient(app=app, base_url=BASE_URL) as client: | ||
# Mock dependency callables for getting current user | ||
with TestClient(app=versioned_app, base_url=BASE_URL) as client: | ||
return client | ||
|
||
|
||
@pytest.fixture | ||
async def test_async_client(): | ||
"""Fixture to get Test client for asynchronous tests""" | ||
async with AsyncClient(app=versioned_app, base_url=BASE_URL) as client: | ||
await versioned_app.router.startup() | ||
yield client | ||
await versioned_app.router.shutdown() | ||
|
||
|
||
@pytest.fixture | ||
def event_loop(): | ||
"""Create an instance of the default event loop for each test case. | ||
|
@@ -105,60 +179,6 @@ def mock_db_find_one(mocker): | |
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_db_find_one_by_attributes(mocker): | ||
""" | ||
Mocks async call to Database class method | ||
used to find an object with matching attributes | ||
""" | ||
async_mock = AsyncMock() | ||
mocker.patch('api.db.Database.find_one_by_attributes', | ||
side_effect=async_mock) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_get_current_user(mocker): | ||
""" | ||
Mocks async call to Authentication class method | ||
used to get current user | ||
""" | ||
async_mock = AsyncMock() | ||
profile = UserProfile( | ||
username='bob', | ||
hashed_password='$2b$12$CpJZx5ooxM11bCFXT76/z.o6HWs2sPJy4iP8.' | ||
'xCZGmM8jWXUXJZ4K', | ||
email='[email protected]' | ||
) | ||
user = User(profile=profile, active=True) | ||
mocker.patch('api.auth.Authentication.get_current_user', | ||
side_effect=async_mock) | ||
async_mock.return_value = user, None | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_get_current_admin_user(mocker): | ||
""" | ||
Mocks async call to Authentication class method | ||
used to get current user | ||
""" | ||
async_mock = AsyncMock() | ||
profile = UserProfile( | ||
username='admin', | ||
hashed_password='$2b$12$CpJZx5ooxM11bCFXT76/z.o6HWs2sPJy4iP8.' | ||
'xCZGmM8jWXUXJZ4K', | ||
email='[email protected]', | ||
groups=[UserGroup(name='admin')]) | ||
user = User( | ||
profile=profile, | ||
active=True) | ||
mocker.patch('api.auth.Authentication.get_current_user', | ||
side_effect=async_mock) | ||
async_mock.return_value = user, None | ||
return async_mock | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def mock_init_sub_id(mocker): | ||
"""Mocks async call to PubSub method to initialize subscription id""" | ||
|
@@ -238,3 +258,57 @@ def mock_unsubscribe(mocker): | |
mocker.patch('api.pubsub.PubSub.unsubscribe', | ||
side_effect=async_mock) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
async def mock_init_beanie(mocker): | ||
"""Mocks async call to Database method to initialize Beanie""" | ||
async_mock = AsyncMock() | ||
client = AsyncMongoMockClient() | ||
init = await init_beanie( | ||
document_models=[User], database=client.get_database(name="db")) | ||
mocker.patch('api.db.Database.initialize_beanie', | ||
side_effect=async_mock, return_value=init) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_db_update(mocker): | ||
""" | ||
Mocks async call to Database class method used to update object | ||
""" | ||
async_mock = AsyncMock() | ||
mocker.patch('api.db.Database.update', | ||
side_effect=async_mock) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
async def mock_beanie_get_user_by_id(mocker): | ||
"""Mocks async call to external method to get model by id""" | ||
async_mock = AsyncMock() | ||
mocker.patch('fastapi_users_db_beanie.BeanieUserDatabase.get', | ||
side_effect=async_mock) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_auth_current_user(mocker): | ||
""" | ||
Mocks async call to external method to get authenticated user | ||
""" | ||
async_mock = AsyncMock() | ||
mocker.patch('fastapi_users.authentication.Authenticator._authenticate', | ||
side_effect=async_mock) | ||
return async_mock | ||
|
||
|
||
@pytest.fixture | ||
def mock_user_find(mocker): | ||
""" | ||
Mocks async call to external method to find user model using Beanie | ||
""" | ||
async_mock = AsyncMock() | ||
mocker.patch('api.user_models.User.find_one', | ||
side_effect=async_mock) | ||
return async_mock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.