Skip to content

Commit

Permalink
httpx and pytest added and lockfile regenerated, test cases init setup
Browse files Browse the repository at this point in the history
  • Loading branch information
prabinoid committed Mar 4, 2025
1 parent 52f2c3e commit 9c9bd76
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,6 @@ postgres_data/

# Variables
scripts/aws/infra/staging/variables.py

# Files
tests/test_db_dump/tm_sample_db.sql
40 changes: 22 additions & 18 deletions backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def assemble_db_connection(
OAUTH_CLIENT_SECRET: str = os.getenv("TM_CLIENT_SECRET", None)
OAUTH_SCOPE: str = os.getenv("TM_SCOPE", "read_prefs write_api")
OAUTH_REDIRECT_URI: str = os.getenv("TM_REDIRECT_URI", None)
DB_NAME: str = os.getenv("TM_DB", "tasking-manager")

if os.getenv("OAUTH2_APP_CREDENTIALS", False):
"""
Expand Down Expand Up @@ -269,28 +270,31 @@ def assemble_db_connection(
OHSOME_STATS_TOKEN: str = os.getenv("OHSOME_STATS_TOKEN", None)


@lru_cache
def get_settings():
"""Cache settings when accessed throughout app."""
_settings = Settings()
if _settings.DEBUG:
print(f"Loaded settings: {_settings.model_dump()}")
return _settings


settings = get_settings()


class TestEnvironmentConfig(Settings):
POSTGRES_TEST_DB: str = os.getenv("POSTGRES_TEST_DB", None)

ENVIRONMENT: str = "test"

SQLALCHEMY_DATABASE_URI: str = (
f"postgresql://{settings.POSTGRES_USER}"
+ f":{settings.POSTGRES_PASSWORD}"
+ f"@{settings.POSTGRES_ENDPOINT}:"
+ f"{settings.POSTGRES_PORT}"
+ f"/{POSTGRES_TEST_DB}"
SQLALCHEMY_DATABASE_URI: PostgresDsn = PostgresDsn.build(
scheme="postgresql+asyncpg",
username=Settings().POSTGRES_USER,
password=Settings().POSTGRES_PASSWORD,
host=Settings().POSTGRES_ENDPOINT,
port=int(Settings().POSTGRES_PORT),
path=f"{POSTGRES_TEST_DB}",
)

LOG_LEVEL: str = "DEBUG"


@lru_cache
def get_settings(env_type: str = "default"):
"""Cache settings when accessed throughout app."""
_settings = Settings()
if env_type == "test":
_settings = TestEnvironmentConfig()
return _settings


settings = get_settings(env_type="default")
test_settings = get_settings(env_type="test")
4 changes: 2 additions & 2 deletions backend/cron_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ async def setup_cron_jobs():
)
scheduler.add_job(
update_all_project_stats,
CronTrigger(hour=0, minute=0),
CronTrigger(hour=0, minute=0, misfire_grace_time=300),
id="update_project_stats",
replace_existing=True,
)
scheduler.add_job(
update_recent_updated_project_stats,
CronTrigger(minute=0),
IntervalTrigger(minutes=60),
id="update_recent_updated_project_stats",
replace_existing=True,
)
Expand Down
75 changes: 73 additions & 2 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ dependencies = [
"databases>=0.9.0",
"fastapi-mail==1.4.1",
"aiocache>=0.12.3",
"httpx>=0.28.1",
"pytest>=7.4.0",
]
requires-python = ">=3.9,<=3.11"
readme = "README.md"
Expand Down
87 changes: 87 additions & 0 deletions tests/api/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import logging
import os
import pytest
import asyncpg
import subprocess
import shlex
from databases import Database
from sqlalchemy.ext.asyncio import create_async_engine
from httpx import ASGITransport, AsyncClient
from backend.config import test_settings as settings
from backend.db import Base, db_connection
from backend.main import api as fastapi_app

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

ASYNC_TEST_DATABASE_URL = settings.SQLALCHEMY_DATABASE_URI.unicode_string()
ASYNCPG_DNS_URL = str(settings.SQLALCHEMY_DATABASE_URI).replace(
"postgresql+asyncpg://", "postgresql://"
)


@pytest.fixture(scope="session")
def anyio_backend():
return "asyncio"


@pytest.fixture(scope="session")
async def create_test_database():
logger.info("Creating test database.")
conn = await asyncpg.connect(dsn=ASYNCPG_DNS_URL)
await conn.execute(f"DROP DATABASE IF EXISTS {settings.POSTGRES_TEST_DB}_test")
await conn.execute(f"CREATE DATABASE {settings.POSTGRES_TEST_DB}_test")
await conn.close()

logger.info("Test database created. Now creating tables in test database.")
engine = create_async_engine(ASYNC_TEST_DATABASE_URL)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await engine.dispose()
logger.info("Tables created in test database.")

dump_path = os.path.join("tests", "test_db_dump", "tm_sample_db.sql")
logger.info("Restoring SQL dump from %s", dump_path)

command = f"psql {ASYNCPG_DNS_URL} -f {dump_path}"

result = subprocess.run(shlex.split(command), capture_output=True, text=True)
if result.returncode != 0:
logger.error("psql restore failed: %s", result.stderr)
raise Exception("SQL restore failed")
else:
logger.info("SQL dump restored successfully using psql.")

yield

logger.info("Cleaning up: Dropping test database.")
conn = await asyncpg.connect(dsn=ASYNCPG_DNS_URL)
await conn.execute(f"DROP DATABASE IF EXISTS {settings.POSTGRES_TEST_DB}_test")
await conn.close()
logger.info("Test database dropped.")


@pytest.fixture(scope="session")
async def app(create_test_database):
logger.info("Setting up the FastAPI app for testing.")
test_db = Database(
ASYNC_TEST_DATABASE_URL, min_size=4, max_size=8, force_rollback=True
)
await test_db.connect()
db_connection.database = test_db
logger.info("FastAPI app setup complete. Yielding app instance for tests.")
yield fastapi_app

logger.info("Disconnecting test database.")
await test_db.disconnect()


@pytest.fixture
async def client(app):
logger.info("Creating test client for FastAPI app.")
async with AsyncClient(
transport=ASGITransport(app=app), base_url="https://test"
) as ac:
logger.info("Test client created; yielding client for test execution.")
yield ac
logger.info("Test client closed.")
18 changes: 18 additions & 0 deletions tests/api/test_hearbeat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest
import logging

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


@pytest.mark.anyio
class TestSystemHealth:
async def test_heartbeat(self, client):
logger.info("Starting test: heartbeat without release version data.")
response = await client.get("/api/v2/system/heartbeat/")
logger.info("Response status code: %s", response.status_code)
assert response.status_code == 200
data = response.json()
logger.info("Response JSON: %s", data)
assert data["status"] == "Fastapi healthy"
logger.info("Completed test: heartbeat without release version data.")

0 comments on commit 9c9bd76

Please sign in to comment.