Skip to content

Commit

Permalink
tests/unit_tests: update user related tests
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 241 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docker/api/requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
190 changes: 132 additions & 58 deletions tests/unit_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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.
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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
9 changes: 3 additions & 6 deletions tests/unit_tests/test_listen_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from tests.unit_tests.conftest import BEARER_TOKEN


def test_listen_endpoint(mock_get_current_user,
mock_listen, test_client):
def test_listen_endpoint(mock_listen, test_client):
"""
Test Case : Test KernelCI API GET /listen endpoint for the
positive path
Expand All @@ -33,8 +32,7 @@ def test_listen_endpoint(mock_get_current_user,
assert response.status_code == 200


def test_listen_endpoint_not_found(mock_get_current_user,
test_client):
def test_listen_endpoint_not_found(test_client):
"""
Test Case : Test KernelCI API GET /listen endpoint for the
negative path
Expand All @@ -53,8 +51,7 @@ def test_listen_endpoint_not_found(mock_get_current_user,
assert 'detail' in response.json()


def test_listen_endpoint_without_token(mock_get_current_user,
test_client):
def test_listen_endpoint_without_token(test_client):
"""
Test Case : Test KernelCI API GET /listen endpoint for the
negative path
Expand Down
17 changes: 6 additions & 11 deletions tests/unit_tests/test_node_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
from api.paginator_models import PageModel


def test_create_node_endpoint(mock_get_current_user,
mock_db_create, mock_publish_cloudevent,
def test_create_node_endpoint(mock_db_create, mock_publish_cloudevent,
test_client):
"""
Test Case : Test KernelCI API /node endpoint
Expand Down Expand Up @@ -91,8 +90,7 @@ def test_create_node_endpoint(mock_get_current_user,
}


def test_get_nodes_by_attributes_endpoint(mock_get_current_user,
mock_db_find_by_attributes,
def test_get_nodes_by_attributes_endpoint(mock_db_find_by_attributes,
test_client):
"""
Test Case : Test KernelCI API GET /nodes?attribute_name=attribute_value
Expand Down Expand Up @@ -159,7 +157,6 @@ def test_get_nodes_by_attributes_endpoint(mock_get_current_user,


def test_get_nodes_by_attributes_endpoint_node_not_found(
mock_get_current_user,
mock_db_find_by_attributes,
test_client):
"""
Expand Down Expand Up @@ -190,7 +187,7 @@ def test_get_nodes_by_attributes_endpoint_node_not_found(
assert response.json().get('total') == 0


def test_get_node_by_id_endpoint(mock_get_current_user, mock_db_find_by_id,
def test_get_node_by_id_endpoint(mock_db_find_by_id,
test_client):
"""
Test Case : Test KernelCI API GET /node/{node_id} endpoint
Expand Down Expand Up @@ -244,8 +241,7 @@ def test_get_node_by_id_endpoint(mock_get_current_user, mock_db_find_by_id,
}


def test_get_node_by_id_endpoint_empty_response(mock_get_current_user,
mock_db_find_by_id,
def test_get_node_by_id_endpoint_empty_response(mock_db_find_by_id,
test_client):
"""
Test Case : Test KernelCI API GET /node/{node_id} endpoint
Expand All @@ -262,7 +258,7 @@ def test_get_node_by_id_endpoint_empty_response(mock_get_current_user,
assert response.json() is None


def test_get_all_nodes(mock_get_current_user, mock_db_find_by_attributes,
def test_get_all_nodes(mock_db_find_by_attributes,
test_client):
"""
Test Case : Test KernelCI API GET /nodes endpoint for the
Expand Down Expand Up @@ -343,8 +339,7 @@ def test_get_all_nodes(mock_get_current_user, mock_db_find_by_attributes,
assert len(response.json()) > 0


def test_get_all_nodes_empty_response(mock_get_current_user,
mock_db_find_by_attributes,
def test_get_all_nodes_empty_response(mock_db_find_by_attributes,
test_client):
"""
Test Case : Test KernelCI API GET /nodes endpoint for the
Expand Down
3 changes: 1 addition & 2 deletions tests/unit_tests/test_subscribe_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from api.pubsub import Subscription


def test_subscribe_endpoint(mock_get_current_user,
mock_subscribe, test_client):
def test_subscribe_endpoint(mock_subscribe, test_client):
"""
Test Case : Test KernelCI API /subscribe endpoint
Expected Result :
Expand Down
Loading

0 comments on commit 3cc1fc0

Please sign in to comment.