migrate tests to pytest-asyncio

This commit is contained in:
2025-01-21 09:47:51 +08:00
committed by GitHub
parent 9e0456f1fc
commit b2557c367e
10 changed files with 55 additions and 23 deletions
+8 -2
View File
@@ -19,11 +19,16 @@ from app.main import app as fastapi_app
from app.models import Base, User
default_user_id = "b75365d9-7bf9-4f54-add5-aeab333a087b"
default_user_email = "geralt@wiedzmin.lol"
default_user_email = "geralt@wiedzmin.pl"
default_user_password = "geralt"
default_user_access_token = create_jwt_token(default_user_id).access_token
# @pytest.fixture(scope="session")
# def event_loop_policy():
# return uvloop.EventLoopPolicy()
# @pytest.fixture(scope="session")
# def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
# loop = asyncio.new_event_loop()
@@ -126,6 +131,7 @@ async def fixture_default_user(
hashed_password=default_hashed_password,
)
session.add(default_user)
await session.commit()
await session.refresh(default_user)
@@ -134,4 +140,4 @@ async def fixture_default_user(
@pytest_asyncio.fixture(name="default_user_headers", scope="function")
def fixture_default_user_headers(default_user: User) -> dict[str, str]:
return {"Authorization": f"Bearer {default_user_access_token}"}
return {"Authorization": f"Bearer {default_user_access_token}"}
+4 -13
View File
@@ -11,15 +11,12 @@ from app.core.security.jwt import create_jwt_token
from app.models import User
@pytest.mark.asyncio(loop_scope="session")
@pytest.mark.parametrize("api_route", api_router.routes)
async def test_api_routes_raise_401_on_jwt_decode_errors(
client: AsyncClient,
api_route: routing.APIRoute,
) -> None:
if not api_route.path.startswith("/users") or not api_route.path.startswith(
"/pets"
):
pytest.skip("This test is only for routes that require authentication")
for method in api_route.methods:
response = await client.request(
method=method,
@@ -30,16 +27,13 @@ async def test_api_routes_raise_401_on_jwt_decode_errors(
assert response.json() == {"detail": "Token invalid: Not enough segments"}
@pytest.mark.asyncio(loop_scope="session")
@pytest.mark.parametrize("api_route", api_router.routes)
async def test_api_routes_raise_401_on_jwt_expired_token(
client: AsyncClient,
default_user: User,
api_route: routing.APIRoute,
) -> None:
if not api_route.path.startswith("/users") or not api_route.path.startswith(
"/pets"
):
pytest.skip("This test is only for routes that require authentication")
with freeze_time("2023-01-01"):
jwt = create_jwt_token(default_user.user_id)
with freeze_time("2023-02-01"):
@@ -53,6 +47,7 @@ async def test_api_routes_raise_401_on_jwt_expired_token(
assert response.json() == {"detail": "Token invalid: Signature has expired"}
@pytest.mark.asyncio(loop_scope="session")
@pytest.mark.parametrize("api_route", api_router.routes)
async def test_api_routes_raise_401_on_jwt_user_deleted(
client: AsyncClient,
@@ -61,10 +56,6 @@ async def test_api_routes_raise_401_on_jwt_user_deleted(
api_route: routing.APIRoute,
session: AsyncSession,
) -> None:
if not api_route.path.startswith("/users") or not api_route.path.startswith(
"/pets"
):
pytest.skip("This test is only for routes that require authentication")
await session.execute(delete(User).where(User.user_id == default_user.user_id))
await session.commit()
@@ -75,4 +66,4 @@ async def test_api_routes_raise_401_on_jwt_user_deleted(
headers=default_user_headers,
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.json() == {"detail": api_messages.JWT_ERROR_USER_REMOVED}
assert response.json() == {"detail": api_messages.JWT_ERROR_USER_REMOVED}
+11 -1
View File
@@ -1,5 +1,6 @@
import time
import pytest
from fastapi import status
from freezegun import freeze_time
from httpx import AsyncClient
@@ -14,6 +15,7 @@ from app.models import RefreshToken, User
from app.tests.conftest import default_user_password
@pytest.mark.asyncio(loop_scope="session")
async def test_login_access_token_has_response_status_code(
client: AsyncClient,
default_user: User,
@@ -30,6 +32,7 @@ async def test_login_access_token_has_response_status_code(
assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio(loop_scope="session")
async def test_login_access_token_jwt_has_valid_token_type(
client: AsyncClient,
default_user: User,
@@ -47,6 +50,7 @@ async def test_login_access_token_jwt_has_valid_token_type(
assert token["token_type"] == "Bearer"
@pytest.mark.asyncio(loop_scope="session")
@freeze_time("2023-01-01")
async def test_login_access_token_jwt_has_valid_expire_time(
client: AsyncClient,
@@ -69,6 +73,7 @@ async def test_login_access_token_jwt_has_valid_expire_time(
)
@pytest.mark.asyncio(loop_scope="session")
@freeze_time("2023-01-01")
async def test_login_access_token_returns_valid_jwt_access_token(
client: AsyncClient,
@@ -92,6 +97,7 @@ async def test_login_access_token_returns_valid_jwt_access_token(
assert token_payload.exp == token["expires_at"]
@pytest.mark.asyncio(loop_scope="session")
async def test_login_access_token_refresh_token_has_valid_expire_time(
client: AsyncClient,
default_user: User,
@@ -113,6 +119,7 @@ async def test_login_access_token_refresh_token_has_valid_expire_time(
)
@pytest.mark.asyncio(loop_scope="session")
async def test_login_access_token_refresh_token_exists_in_db(
client: AsyncClient,
default_user: User,
@@ -135,6 +142,7 @@ async def test_login_access_token_refresh_token_exists_in_db(
assert token_db_count == 1
@pytest.mark.asyncio(loop_scope="session")
async def test_login_access_token_refresh_token_in_db_has_valid_fields(
client: AsyncClient,
default_user: User,
@@ -160,6 +168,7 @@ async def test_login_access_token_refresh_token_in_db_has_valid_fields(
assert not refresh_token.used
@pytest.mark.asyncio(loop_scope="session")
async def test_auth_access_token_fail_for_not_existing_user_with_message(
client: AsyncClient,
) -> None:
@@ -176,6 +185,7 @@ async def test_auth_access_token_fail_for_not_existing_user_with_message(
assert response.json() == {"detail": api_messages.PASSWORD_INVALID}
@pytest.mark.asyncio(loop_scope="session")
async def test_auth_access_token_fail_for_invalid_password_with_message(
client: AsyncClient,
default_user: User,
@@ -190,4 +200,4 @@ async def test_auth_access_token_fail_for_invalid_password_with_message(
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json() == {"detail": api_messages.PASSWORD_INVALID}
assert response.json() == {"detail": api_messages.PASSWORD_INVALID}
+13 -2
View File
@@ -1,5 +1,6 @@
import time
import pytest
from fastapi import status
from freezegun import freeze_time
from httpx import AsyncClient
@@ -13,6 +14,7 @@ from app.main import app
from app.models import RefreshToken, User
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_fails_with_message_when_token_does_not_exist(
client: AsyncClient,
) -> None:
@@ -27,6 +29,7 @@ async def test_refresh_token_fails_with_message_when_token_does_not_exist(
assert response.json() == {"detail": api_messages.REFRESH_TOKEN_NOT_FOUND}
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_fails_with_message_when_token_is_expired(
client: AsyncClient,
default_user: User,
@@ -51,6 +54,7 @@ async def test_refresh_token_fails_with_message_when_token_is_expired(
assert response.json() == {"detail": api_messages.REFRESH_TOKEN_EXPIRED}
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_fails_with_message_when_token_is_used(
client: AsyncClient,
default_user: User,
@@ -76,6 +80,7 @@ async def test_refresh_token_fails_with_message_when_token_is_used(
assert response.json() == {"detail": api_messages.REFRESH_TOKEN_ALREADY_USED}
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_success_response_status_code(
client: AsyncClient,
default_user: User,
@@ -100,6 +105,7 @@ async def test_refresh_token_success_response_status_code(
assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_success_old_token_is_used(
client: AsyncClient,
default_user: User,
@@ -128,6 +134,7 @@ async def test_refresh_token_success_old_token_is_used(
assert used_test_refresh_token.used
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_success_jwt_has_valid_token_type(
client: AsyncClient,
default_user: User,
@@ -153,6 +160,7 @@ async def test_refresh_token_success_jwt_has_valid_token_type(
assert token["token_type"] == "Bearer"
@pytest.mark.asyncio(loop_scope="session")
@freeze_time("2023-01-01")
async def test_refresh_token_success_jwt_has_valid_expire_time(
client: AsyncClient,
@@ -183,6 +191,7 @@ async def test_refresh_token_success_jwt_has_valid_expire_time(
)
@pytest.mark.asyncio(loop_scope="session")
@freeze_time("2023-01-01")
async def test_refresh_token_success_jwt_has_valid_access_token(
client: AsyncClient,
@@ -214,6 +223,7 @@ async def test_refresh_token_success_jwt_has_valid_access_token(
assert token_payload.exp == token["expires_at"]
@pytest.mark.asyncio(loop_scope="session")
@freeze_time("2023-01-01")
async def test_refresh_token_success_refresh_token_has_valid_expire_time(
client: AsyncClient,
@@ -229,7 +239,6 @@ async def test_refresh_token_success_refresh_token_has_valid_expire_time(
session.add(test_refresh_token)
await session.commit()
current_time = int(time.time())
response = await client.post(
app.url_path_for("refresh_token"),
json={
@@ -238,12 +247,14 @@ async def test_refresh_token_success_refresh_token_has_valid_expire_time(
)
token = response.json()
current_time = int(time.time())
assert (
token["refresh_token_expires_at"]
== current_time + get_settings().security.refresh_token_expire_secs
)
@pytest.mark.asyncio(loop_scope="session")
async def test_refresh_token_success_new_refresh_token_is_in_db(
client: AsyncClient,
default_user: User,
@@ -269,4 +280,4 @@ async def test_refresh_token_success_new_refresh_token_is_in_db(
token_db_count = await session.scalar(
select(func.count()).where(RefreshToken.refresh_token == token["refresh_token"])
)
assert token_db_count == 1
assert token_db_count == 1
@@ -1,3 +1,4 @@
import pytest
from fastapi import status
from httpx import AsyncClient
from sqlalchemy import func, select
@@ -8,6 +9,7 @@ from app.main import app
from app.models import User
@pytest.mark.asyncio(loop_scope="session")
async def test_register_new_user_status_code(
client: AsyncClient,
) -> None:
@@ -22,6 +24,7 @@ async def test_register_new_user_status_code(
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio(loop_scope="session")
async def test_register_new_user_creates_record_in_db(
client: AsyncClient,
session: AsyncSession,
@@ -40,6 +43,7 @@ async def test_register_new_user_creates_record_in_db(
assert user_count == 1
@pytest.mark.asyncio(loop_scope="session")
async def test_register_new_user_cannot_create_already_created_user(
client: AsyncClient,
session: AsyncSession,
@@ -60,4 +64,4 @@ async def test_register_new_user_cannot_create_already_created_user(
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json() == {"detail": api_messages.EMAIL_ADDRESS_ALREADY_USED}
assert response.json() == {"detail": api_messages.EMAIL_ADDRESS_ALREADY_USED}
+1 -1
View File
@@ -81,4 +81,4 @@ def test_jwt_error_with_invalid_secret_key() -> None:
with pytest.raises(HTTPException) as e:
jwt.verify_jwt_token(token=token.access_token)
assert e.value.detail == "Token invalid: Signature verification failed"
assert e.value.detail == "Token invalid: Signature verification failed"
@@ -1,3 +1,4 @@
import pytest
from fastapi import status
from httpx import AsyncClient
from sqlalchemy import select
@@ -7,6 +8,7 @@ from app.main import app
from app.models import User
@pytest.mark.asyncio(loop_scope="session")
async def test_delete_current_user_status_code(
client: AsyncClient,
default_user_headers: dict[str, str],
@@ -19,6 +21,7 @@ async def test_delete_current_user_status_code(
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio(loop_scope="session")
async def test_delete_current_user_is_deleted_in_db(
client: AsyncClient,
default_user_headers: dict[str, str],
@@ -33,4 +36,4 @@ async def test_delete_current_user_is_deleted_in_db(
user = await session.scalar(
select(User).where(User.user_id == default_user.user_id)
)
assert user is None
assert user is None
@@ -1,3 +1,4 @@
import pytest
from fastapi import status
from httpx import AsyncClient
@@ -8,6 +9,7 @@ from app.tests.conftest import (
)
@pytest.mark.asyncio(loop_scope="session")
async def test_read_current_user_status_code(
client: AsyncClient, default_user_headers: dict[str, str]
) -> None:
@@ -19,6 +21,7 @@ async def test_read_current_user_status_code(
assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio(loop_scope="session")
async def test_read_current_user_response(
client: AsyncClient, default_user_headers: dict[str, str]
) -> None:
@@ -30,4 +33,4 @@ async def test_read_current_user_response(
assert response.json() == {
"user_id": default_user_id,
"email": default_user_email,
}
}
+4 -1
View File
@@ -1,3 +1,4 @@
import pytest
from fastapi import status
from httpx import AsyncClient
from sqlalchemy import select
@@ -8,6 +9,7 @@ from app.main import app
from app.models import User
@pytest.mark.asyncio(loop_scope="session")
async def test_reset_current_user_password_status_code(
client: AsyncClient,
default_user_headers: dict[str, str],
@@ -21,6 +23,7 @@ async def test_reset_current_user_password_status_code(
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio(loop_scope="session")
async def test_reset_current_user_password_is_changed_in_db(
client: AsyncClient,
default_user_headers: dict[str, str],
@@ -37,4 +40,4 @@ async def test_reset_current_user_password_is_changed_in_db(
select(User).where(User.user_id == default_user.user_id)
)
assert user is not None
assert verify_password("test_pwd", user.hashed_password)
assert verify_password("test_pwd", user.hashed_password)
+1
View File
@@ -39,6 +39,7 @@ requires = ["poetry-core>=1.0.0"]
[tool.pytest.ini_options]
addopts = "-vv -n auto --cov --cov-report xml --cov-report term-missing --cov-fail-under=95"
asyncio_default_fixture_loop_scope = "session"
asyncio_mode = "auto"
testpaths = ["app/tests"]