Fix testing
This commit is contained in:
@@ -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,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,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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"'
|
||||
|
||||
Reference in New Issue
Block a user