test: add unit test suite for config, health checks, and lifespan wiring
This commit is contained in:
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
37
tests/conftest.py
Normal file
37
tests/conftest.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from collections.abc import AsyncIterator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
|
from asgi_lifespan import LifespanManager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
|
||||||
|
from src.config import Settings
|
||||||
|
from src.main import create_app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def settings() -> Settings:
|
||||||
|
return Settings(
|
||||||
|
postgres={"host": "127.0.0.1", "port": 1},
|
||||||
|
minio={"endpoint": "127.0.0.1:1"},
|
||||||
|
ingestion={"timeout_seconds": 1.0},
|
||||||
|
qdrant={"url": "http://127.0.0.1:1"},
|
||||||
|
app={"readiness_check_timeout_seconds": 0.5},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app(settings: Settings) -> FastAPI:
|
||||||
|
return create_app(settings)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture
|
||||||
|
async def client(app: FastAPI) -> AsyncIterator[AsyncClient]:
|
||||||
|
async with (
|
||||||
|
LifespanManager(app) as manager,
|
||||||
|
AsyncClient(
|
||||||
|
transport=ASGITransport(app=manager.app), base_url="http://test"
|
||||||
|
) as async_client,
|
||||||
|
):
|
||||||
|
yield async_client
|
||||||
0
tests/e2e/__init__.py
Normal file
0
tests/e2e/__init__.py
Normal file
5
tests/fakes.py
Normal file
5
tests/fakes.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Hand-written fakes for narrow application-owned ports.
|
||||||
|
|
||||||
|
No application ports exist yet (Phase 1 only wires infrastructure client
|
||||||
|
lifecycle). Fakes are added here as ports are introduced in later phases.
|
||||||
|
"""
|
||||||
0
tests/integration/__init__.py
Normal file
0
tests/integration/__init__.py
Normal file
0
tests/integration/minio/__init__.py
Normal file
0
tests/integration/minio/__init__.py
Normal file
0
tests/integration/postgres/__init__.py
Normal file
0
tests/integration/postgres/__init__.py
Normal file
0
tests/integration/qdrant/__init__.py
Normal file
0
tests/integration/qdrant/__init__.py
Normal file
0
tests/support/__init__.py
Normal file
0
tests/support/__init__.py
Normal file
1
tests/support/assertions.py
Normal file
1
tests/support/assertions.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Custom test assertions, populated as later phases need them."""
|
||||||
1
tests/support/factories.py
Normal file
1
tests/support/factories.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Object builders for test fixtures, populated as later phases need them."""
|
||||||
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
0
tests/unit/agent/__init__.py
Normal file
0
tests/unit/agent/__init__.py
Normal file
0
tests/unit/application/__init__.py
Normal file
0
tests/unit/application/__init__.py
Normal file
0
tests/unit/bootstrap/__init__.py
Normal file
0
tests/unit/bootstrap/__init__.py
Normal file
19
tests/unit/bootstrap/test_lifespan.py
Normal file
19
tests/unit/bootstrap/test_lifespan.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
from src.config import Settings
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.unit, pytest.mark.asyncio]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lifespan_starts_and_stops_without_docker(client: AsyncClient) -> None:
|
||||||
|
response = await client.get("/healthz")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lifespan_binds_resources_to_app_state(app: FastAPI, client: AsyncClient) -> None:
|
||||||
|
await client.get("/healthz")
|
||||||
|
resources = app.state.resources
|
||||||
|
assert isinstance(resources.settings, Settings)
|
||||||
|
assert resources.minio_client is not None
|
||||||
27
tests/unit/test_config.py
Normal file
27
tests/unit/test_config.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.config import Settings
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.unit
|
||||||
|
|
||||||
|
|
||||||
|
def test_settings_loads_defaults_without_env_file() -> None:
|
||||||
|
settings = Settings(_env_file=None)
|
||||||
|
|
||||||
|
assert settings.postgres.port == 5433
|
||||||
|
assert settings.minio.bucket == "chatbot-source-files"
|
||||||
|
assert settings.ingestion.max_concurrency == 4
|
||||||
|
assert settings.qdrant.url == "http://127.0.0.1:6343"
|
||||||
|
|
||||||
|
|
||||||
|
def test_settings_parses_env_example() -> None:
|
||||||
|
env_example = Path(__file__).parent.parent.parent / ".env.example"
|
||||||
|
|
||||||
|
settings = Settings(_env_file=env_example)
|
||||||
|
|
||||||
|
assert settings.postgres.host == "127.0.0.1"
|
||||||
|
assert settings.minio.endpoint == "127.0.0.1:9100"
|
||||||
|
assert settings.ingestion.embed_batch_size == 128
|
||||||
|
assert settings.qdrant.api_key is None
|
||||||
11
tests/unit/test_healthz.py
Normal file
11
tests/unit/test_healthz.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import pytest
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.unit, pytest.mark.asyncio]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_healthz_returns_ok_status(client: AsyncClient) -> None:
|
||||||
|
response = await client.get("/healthz")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"status": "ok"}
|
||||||
17
tests/unit/test_readyz.py
Normal file
17
tests/unit/test_readyz.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import pytest
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.unit, pytest.mark.asyncio]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_readyz_reports_all_dependencies_not_ready_without_docker(
|
||||||
|
client: AsyncClient,
|
||||||
|
) -> None:
|
||||||
|
response = await client.get("/readyz")
|
||||||
|
|
||||||
|
assert response.status_code == 503
|
||||||
|
assert response.json() == {
|
||||||
|
"postgres": False,
|
||||||
|
"minio": False,
|
||||||
|
"qdrant": False,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user