-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Polyfactory for Creating Mock Users (#59)
* wip: trying to isolate users from one another in test scenarios. todo: generate mock users using polyfactory * upgraded packages * fixed all tests except test_user_creation_limit * added new factories.py file * updated mypy ignore comment
- Loading branch information
Showing
12 changed files
with
372 additions
and
290 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
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
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 |
---|---|---|
|
@@ -2,54 +2,64 @@ | |
import pathlib | ||
from collections.abc import Generator | ||
from datetime import datetime | ||
from typing import Any | ||
from typing import Any, Literal | ||
from zoneinfo import ZoneInfo | ||
|
||
import boto3 | ||
import pytest | ||
from httpx import ASGITransport, AsyncClient | ||
from moto import mock_aws | ||
from mypy_boto3_s3 import S3Client | ||
from polyfactory.pytest_plugin import register_fixture | ||
from sqlmodel import SQLModel, create_engine | ||
|
||
from smolvault.auth.models import NewUserDTO | ||
from smolvault.clients.database import ( | ||
DatabaseClient, | ||
FileMetadataRecord, | ||
) | ||
from smolvault.main import app | ||
from smolvault.models import FileMetadata | ||
|
||
from .factories import UserFactory | ||
|
||
user_factory_fixture = register_fixture(UserFactory, name="user_factory") | ||
|
||
|
||
class TestDatabaseClient(DatabaseClient): | ||
def __init__(self) -> None: | ||
self.engine = create_engine("sqlite:///test.db", echo=False, connect_args={"check_same_thread": False}) | ||
SQLModel.metadata.create_all(self.engine) | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def _user() -> None: | ||
client = TestDatabaseClient() | ||
user = NewUserDTO( | ||
username="testuser", | ||
password="testpassword", # type: ignore # noqa: S106 | ||
email="[email protected]", | ||
full_name="John Smith", | ||
) | ||
client.add_user(user) | ||
@pytest.fixture(scope="module") | ||
def anyio_backend() -> Literal["asyncio"]: | ||
return "asyncio" | ||
|
||
|
||
@pytest.fixture | ||
def db_client() -> TestDatabaseClient: | ||
return TestDatabaseClient() | ||
|
||
|
||
@pytest.fixture | ||
def user(user_factory: UserFactory, db_client: TestDatabaseClient) -> tuple[str, str]: | ||
user = user_factory.build() | ||
db_client.add_user(user) | ||
return user.username, user.password.get_secret_value() | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def client(_user: None) -> AsyncClient: | ||
def client() -> AsyncClient: | ||
app.dependency_overrides[DatabaseClient] = TestDatabaseClient | ||
return AsyncClient(transport=ASGITransport(app=app), base_url="http://testserver") # type: ignore | ||
|
||
|
||
@pytest.fixture | ||
async def access_token(client: AsyncClient) -> str: | ||
async def access_token(client: AsyncClient, user: tuple[str, str]) -> str: | ||
username, password = user | ||
response = await client.post( | ||
"/token", | ||
data={"username": "testuser", "password": "testpassword"}, | ||
data={"username": username, "password": password}, | ||
) | ||
return response.json()["access_token"] | ||
|
||
|
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 |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from polyfactory import Use | ||
from polyfactory.factories.pydantic_factory import ModelFactory | ||
|
||
from smolvault.auth.models import NewUserDTO | ||
|
||
|
||
class UserFactory(ModelFactory[NewUserDTO]): | ||
username = Use(lambda: ModelFactory.__faker__.user_name()) | ||
email = Use(lambda: ModelFactory.__faker__.email()) | ||
full_name = Use(lambda: ModelFactory.__faker__.name()) | ||
password = Use(lambda: ModelFactory.__faker__.password()) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,19 +9,7 @@ | |
from smolvault.models import FileMetadata | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_read_root(client: AsyncClient, access_token: str) -> None: | ||
response = await client.get("/", headers={"Authorization": f"Bearer {access_token}"}) | ||
assert response.status_code == 200 | ||
assert response.json() == { | ||
"email": "[email protected]", | ||
"full_name": "John Smith", | ||
"username": "testuser", | ||
"id": 1, | ||
} | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_test_bucket") | ||
async def test_list_files( | ||
client: AsyncClient, | ||
|
@@ -39,7 +27,7 @@ def mock_get_all_files(*args: Any, **kwargs: Any) -> Sequence[FileMetadataRecord | |
assert response.json() == [file_metadata.model_dump(by_alias=True)] | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_bucket_w_camera_img") | ||
async def test_get_file( | ||
client: AsyncClient, | ||
|
@@ -49,12 +37,13 @@ async def test_get_file( | |
access_token: str, | ||
) -> None: | ||
filename = f"{uuid4().hex[:6]}-camera.png" | ||
await client.post( | ||
response = await client.post( | ||
"/file/upload", | ||
files={"file": (filename, camera_img, "image/png")}, | ||
data={"tags": "camera,photo"}, | ||
headers={"Authorization": f"Bearer {access_token}"}, | ||
) | ||
assert response.status_code == 201 | ||
response = await client.get( | ||
"/file/original", | ||
params={"filename": filename}, | ||
|
@@ -64,7 +53,7 @@ async def test_get_file( | |
assert response.content == camera_img | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_test_bucket") | ||
async def test_get_file_not_found(client: AsyncClient, access_token: str) -> None: | ||
response = await client.get( | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
from os import environ | ||
from uuid import uuid4 | ||
|
||
import pytest | ||
from httpx import AsyncClient | ||
|
||
from tests.conftest import TestDatabaseClient | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
async def user_john(client: AsyncClient) -> str: | ||
|
@@ -76,7 +79,7 @@ async def user_jack(client: AsyncClient) -> str: | |
return response.json()["access_token"] | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_test_bucket") | ||
async def test_get_file(client: AsyncClient, camera_img: bytes, user_john: str, user_jane: str) -> None: | ||
""" | ||
|
@@ -115,7 +118,7 @@ async def _fully_populated_user_bucket( | |
img_size = len(camera_img) | ||
bytes_uploaded = 0 | ||
filenames: list[str] = [] | ||
while bytes_uploaded < 50000: | ||
while bytes_uploaded < int(environ["DAILY_UPLOAD_LIMIT_BYTES"]): | ||
# upload file as john | ||
filename = f"{uuid4().hex[:6]}-camera.png" | ||
filenames.append(filename) | ||
|
@@ -128,7 +131,7 @@ async def _fully_populated_user_bucket( | |
bytes_uploaded += img_size | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_fully_populated_user_bucket") | ||
async def test_user_over_daily_upload_limit(client: AsyncClient, camera_img: bytes, user_jack: str) -> None: | ||
""" | ||
|
@@ -144,3 +147,29 @@ async def test_user_over_daily_upload_limit(client: AsyncClient, camera_img: byt | |
) | ||
assert response.status_code == 400 | ||
assert response.json() == {"error": "Upload limit exceeded"} | ||
|
||
|
||
@pytest.mark.anyio | ||
@pytest.mark.usefixtures("_test_bucket") | ||
@pytest.mark.xfail(reason="Not implemented fully") | ||
async def test_user_creation_limit( | ||
client: AsyncClient, user_john: str, user_jane: str, user_jack: str, db_client: TestDatabaseClient | ||
) -> None: | ||
""" | ||
Test that the system blocks new user creation if the user limit has been reached. | ||
""" | ||
|
||
users_count = db_client.get_user_count() # noqa: F841 | ||
max_users = int(environ["USERS_LIMIT"]) # noqa: F841 | ||
|
||
user_data = { | ||
"username": "kate", | ||
"password": "testpassword", | ||
"email": "[email protected]", | ||
"full_name": "Kate Smith", | ||
} | ||
response = await client.post( | ||
"/users/new", | ||
json=user_data, | ||
) | ||
assert response.status_code == 400 |
Oops, something went wrong.