from unittest.mock import MagicMock import pytest from httpx import AsyncClient from porchlight.app import create_app from porchlight.config import Settings from porchlight.dependencies import ( get_credential_repo, get_magic_link_repo, get_user_repo, ) from porchlight.store.protocols import ( CredentialRepository, MagicLinkRepository, UserRepository, ) async def test_health_endpoint(client: AsyncClient) -> None: response = await client.get("/health") assert response.status_code == 200 data = response.json() assert data["status"] == "ok" async def test_app_has_title(client: AsyncClient) -> None: response = await client.get("/openapi.json") assert response.status_code == 200 data = response.json() assert data["info"]["title"] == "Porchlight" async def test_app_has_repos_on_state(client: AsyncClient) -> None: app = client._transport.app # type: ignore[union-attr] assert isinstance(app.state.user_repo, UserRepository) assert isinstance(app.state.credential_repo, CredentialRepository) assert isinstance(app.state.magic_link_repo, MagicLinkRepository) async def test_landing_page(client: AsyncClient) -> None: response = await client.get("/") assert response.status_code == 200 assert "text/html" in response.headers["content-type"] body = response.text assert "

Porchlight

" in body assert "/manage/profile" in body assert "/admin/users" in body assert "My Account" in body assert "Administration" in body async def test_dependency_functions() -> None: request = MagicMock() request.app.state.user_repo = "user_repo_sentinel" request.app.state.credential_repo = "credential_repo_sentinel" request.app.state.magic_link_repo = "magic_link_repo_sentinel" assert get_user_repo(request) == "user_repo_sentinel" assert get_credential_repo(request) == "credential_repo_sentinel" assert get_magic_link_repo(request) == "magic_link_repo_sentinel" def test_create_app_requires_session_secret_in_production() -> None: settings = Settings(issuer="https://op.example.com", sqlite_path=":memory:", debug=False) with pytest.raises(RuntimeError, match="SESSION_SECRET"): create_app(settings) def test_create_app_allows_missing_secret_on_localhost() -> None: settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:") assert create_app(settings) is not None def test_create_app_allows_missing_secret_in_debug() -> None: settings = Settings(issuer="https://op.example.com", sqlite_path=":memory:", debug=True) assert create_app(settings) is not None