Fix testing

This commit is contained in:
2025-01-30 19:31:00 +02:00
parent 985fa8ab25
commit cdbd972549
14 changed files with 148 additions and 77 deletions
+6 -1
View File
@@ -9,7 +9,12 @@ class Settings(BaseSettings):
PROJECT_SUMMARY: str = "Product API for StoneEdge."
SECRET_KEY: str | None = None
HASHING_SCHEME: str = "HS512"
PSQL_CONNECT_STR: str = "postgres://user:password@localhost:5432/stoneedge"
PSQL_USERNAME: str = "user"
PSQL_PASSWORD: str = "password"
PSQL_HOSTNAME: str = "localhost"
PSQL_PORT: int = 5432
PSQL_DB_NAME: str = "stoneedge"
PSQL_TEST_DB_NAME: str = "stoneedge_testing"
ACCESS_TOKEN_EXPIRE_MIN: int = 30
REFRESH_TOKEN_EXPIRE_MIN: int = 60
DEFAULT_TIMEZONE: str = pytz.UTC._tzname
+12 -1
View File
@@ -12,7 +12,18 @@ modules: dict[str, Any] = {
}
TORTOISE_ORM = {
"connections": {"default": settings.PSQL_CONNECT_STR},
"connections": {
"default": {
"engine": "tortoise.backends.asyncpg",
"credentials": {
"host": settings.PSQL_HOSTNAME,
"database": settings.PSQL_DB_NAME,
"user": settings.PSQL_USERNAME,
"password": settings.PSQL_PASSWORD,
"port": settings.PSQL_PORT,
},
}
},
"apps": {
"models": {
"models": modules.get("models", []) + ["aerich.models"],
+2
View File
@@ -2,6 +2,7 @@ from fastapi import FastAPI
from tortoise import run_async
from config import settings
from database import migrate_db
from responses import msgspec_jsonresponse
from router import router as root_router
from modules.assets.router import router as asset_router
@@ -13,6 +14,7 @@ app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.PROJECT_VERSION,
summary=settings.PROJECT_SUMMARY,
default_response_class=msgspec_jsonresponse
)
run_async(migrate_db())
+3 -3
View File
@@ -21,17 +21,17 @@ error: str = "E-Mail Address or password is incorrect"
crypt = settings.CRYPT
@router.post("/", status_code=200)
@router.post("/")
async def login(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
user: User | None = await User.filter(
Q(email=form.username)
).get_or_none()
if user is None:
return HTTPException(status_code=401, detail=error)
raise HTTPException(status_code=401, detail=error)
if user.check_against_password(form.password) is False:
return HTTPException(status_code=401, detail=error)
raise HTTPException(status_code=401, detail=error)
return JSONResponse(
await Token.create(
@@ -1,5 +1,7 @@
from fastapi import APIRouter
from modules.users.models import User
router = APIRouter(prefix="/api/v1/users", tags=["users"])
@@ -8,7 +10,6 @@ router = APIRouter(prefix="/api/v1/users", tags=["users"])
def get_all_users():
pass
@router.post("/")
def create_user():
pass
+1
View File
@@ -11,6 +11,7 @@ location = "./migrations"
src_folder = "./."
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
testpaths = [
"tests/",
@@ -8,10 +8,13 @@ joserfc>=1.0.1
passlib>=1.7.4
pytz>=2024.2
ptpython>=0.25
msgspec>=0.19.0
# Test Suite
httpx>=0.28.1
pytest>=8.3.4
mock>=5.1.0
pytest-mock>=3.14.0
anyio>=4.8.0
pytest-asyncio>=0.25.3
asyncio>=3.4.3
asgi-lifespan>=2.1.0
+16
View File
@@ -0,0 +1,16 @@
from typing import Any
from fastapi.responses import JSONResponse
try:
import msgspec # type: ignore
except ImportError: # pragma: nocover
msgspec = None # type: ignore
class msgspec_jsonresponse(JSONResponse):
"""
JSON Response using the high-performance msgspec lib to serialize data to JSON.
"""
def render(self, content: Any) -> bytes:
assert msgspec is not None, "msgspec must be installed to use msgspec_jsonresponse"
return msgspec.json.encode(content)
+1 -1
View File
@@ -5,7 +5,7 @@ from fastapi.responses import JSONResponse, RedirectResponse
router = APIRouter(prefix="/api/v1")
@router.get("/")
async def main():
async def main() -> RedirectResponse:
return RedirectResponse(url="/docs")
+85
View File
@@ -0,0 +1,85 @@
from typing import AsyncGenerator, Optional, Self
import httpx
from tortoise import Tortoise
import pytest # type: ignore
from database import modules
from config import settings
from asgi_lifespan import LifespanManager # type: ignore
try:
from main import app
except ImportError:
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent))
from main import app
TORTOISE_ORM = {
"connections": {
"default": {
"engine": "tortoise.backends.asyncpg",
"credentials": {
"host": settings.PSQL_HOSTNAME,
"database": settings.PSQL_TEST_DB_NAME,
"user": settings.PSQL_USERNAME,
"password": settings.PSQL_PASSWORD,
"port": settings.PSQL_PORT,
},
}
},
"apps": {
"models": {
"models": modules.get("models", []) + ["aerich.models"],
"default_connection": "default",
},
},
}
@pytest.fixture(scope="session")
def anyio_backend():
return "asyncio"
class TestClient(httpx.AsyncClient):
def __init__(self, app, base_url="http://localhost", mount_lifespan=True, **kw) -> None:
self.mount_lifespan = mount_lifespan
self._manager: Optional[LifespanManager] = None
super().__init__(transport=httpx.ASGITransport(app), base_url=base_url, **kw)
async def __aenter__(self) -> Self:
if self.mount_lifespan:
app = self._transport.app # type:ignore
self._manager = await LifespanManager(app).__aenter__()
self._transport = httpx.ASGITransport(app=self._manager.app)
return await super().__aenter__()
async def __aexit__(self, *args, **kw):
await super().__aexit__(*args, **kw)
if self._manager is not None:
await self._manager.__aexit__(*args, **kw)
async def init_db(create_db: bool = True, schemas: bool = True) -> None:
"""Initial database connection"""
await Tortoise.init(
config=TORTOISE_ORM, timezone="Europe/Helsinki"
)
if create_db:
print(f"Database created!")
if schemas:
await Tortoise.generate_schemas()
print("Success to generate schemas")
@pytest.fixture(scope="session", autouse=True)
async def initialize_tests():
await init_db()
yield
await Tortoise._drop_databases()
@pytest.fixture(scope="session")
async def client() -> AsyncGenerator[TestClient, None]:
async with TestClient(app) as c:
yield c
View File
-43
View File
@@ -1,43 +0,0 @@
import pytest
from httpx import AsyncClient
from tortoise import Tortoise
from database import modules
from main import app
DB_URL = "sqlite://:memory:"
async def init_db(db_url, create_db: bool = True, schemas: bool = True) -> None:
"""Initial database connection"""
await Tortoise.init(
db_url=db_url, modules={"models": modules}, _create_db=create_db
)
if create_db:
print(f"Database created! {db_url = }")
if schemas:
await Tortoise.generate_schemas()
print("Success to generate schemas")
async def init(db_url: str = DB_URL):
await init_db(db_url, True, True)
@pytest.fixture(scope="session")
def anyio_backend():
return "anyio"
@pytest.fixture(scope="session")
async def client():
async with AsyncClient(app=app, base_url="http://test") as client:
print("Client is ready")
yield client
@pytest.fixture(scope="session", autouse=True)
async def initialize_tests():
await init()
yield
await Tortoise._drop_databases()
@@ -1,32 +1,23 @@
from tests.fixtures.conftest import init_db
import pytest
from fastapi.testclient import TestClient
from httpx import AsyncClient
from modules.organizations.models import Organization
from modules.users.models import ACL, Membership, User
from main import app
from config import settings
client = TestClient(app)
crypt = settings.CRYPT
@pytest.mark.anyio
async def setup_function():
init_db()
org = await Organization.create(name="Admin's Organization", type="home")
user = await User.create(
email="admin@localhost.com",
username="admin",
name="admin",
surname="admin",
password=crypt.hash("password")
)
user.set_password("password")
user.save()
acl = await ACL.create(READ=True, WRITE=True, REPORT=True, MANAGE=True, ADMIN=True)
await Membership.create(organization=org, user=user, acl=acl)
print(org, user, acl)
# def teardown_function():
# Organization.all().delete()
@@ -34,10 +25,10 @@ async def setup_function():
# ACL.all().delete()
# Membership.all().delete()
def test_read_main():
response = client.post(
"/api/v1/auth",
async def test_read_main(client: AsyncClient):
print("start")
response = await client.post(
"http://localhost/api/v1/auth",
data={
"username": "admin@localhost.com",
"password": "password",
@@ -1,16 +1,15 @@
from fastapi.testclient import TestClient
from main import app
import pytest
from httpx import AsyncClient
client = TestClient(app)
def setup_function():
print("setting up")
@pytest.mark.anyio
async def test_read_main(client: AsyncClient):
response = await client.get("http://localhost:8000/api/v1/")
assert response.status_code == 307
def test_read_main():
response = client.get("/api/v1/")
assert response.status_code == 200
def test_get_pong():
response = client.get("/api/v1/ping")
@pytest.mark.anyio
async def test_get_pong(client: AsyncClient):
response = await client.get("http://localhost:8000/api/v1/ping")
assert response.status_code == 200
assert response.text == '"PONG"'