"""Basic tests for the backend application.""" from importlib import metadata from typing import Any, cast from unittest.mock import AsyncMock import pytest from fastapi.testclient import TestClient from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import AsyncSession from backend.main import ( app, compatibility_check, compatibility_status, get_api_session, read_root, validate_runtime_policy, ) def test_app_creation(): """Test that the FastAPI app is created properly.""" assert app is not None assert app.title == "Plex Playlist Backend" assert app.version == "0.1.0" def test_read_root(): """Test the root endpoint function.""" result = read_root() assert result == {"message": "Plex Playlist Backend API"} def test_health_check(): """Test the health check endpoint function.""" healthy_session = cast("AsyncSession", AsyncMock(spec=AsyncSession)) healthy_session.execute = AsyncMock(return_value=1) async def override_get_session(): """Provide a healthy session dependency override for tests.""" yield healthy_session app.dependency_overrides[get_api_session] = override_get_session try: with TestClient(app) as client: response = client.get("/health") assert response.status_code == 200 assert response.json() == {"status": "healthy", "database": "connected"} finally: app.dependency_overrides.clear() def test_typeguard_validation(): """Test that typeguard is working for type validation.""" # Test a function with type hints to ensure typeguard is active def typed_function(x: int, y: str) -> str: return f"{x}: {y}" # This should work fine result = typed_function(42, "test") assert result == "42: test" # This would fail with typeguard active, but we'll just test the happy path # to ensure coverage of our code without breaking the test def test_health_check_db_unavailable(): """Health endpoint should return unavailable when DB probe fails.""" unhealthy_session = cast("AsyncSession", AsyncMock(spec=AsyncSession)) unhealthy_session.execute = AsyncMock( side_effect=SQLAlchemyError("database unavailable") ) async def override_get_session(): """Provide an unhealthy session dependency override for tests.""" yield unhealthy_session app.dependency_overrides[get_api_session] = override_get_session try: with TestClient(app) as client: response = client.get("/health") assert response.status_code == 503 assert response.json() == { "status": "unhealthy", "database": "disconnected", } finally: app.dependency_overrides.clear() def test_compatibility_status_success(monkeypatch: pytest.MonkeyPatch): """Compatibility status should be healthy when all checks match policy.""" monkeypatch.setenv("BACKEND_REQUIRED_PYTHON", "3.14") def installed_version(package_name: str) -> str: versions = { "fastapi": "0.139.0", "psycopg": "3.2.12", "sqlalchemy": "2.0.44", "uvicorn": "0.51.0", } return versions[package_name] monkeypatch.setattr("backend.main._installed_version", installed_version) status = cast("dict[str, Any]", compatibility_status()) assert status["ok"] is True assert status["package_errors"] == {} def test_compatibility_status_missing_package(monkeypatch: pytest.MonkeyPatch): """Compatibility status should record metadata lookup failures.""" def missing_version(_: str) -> str: raise metadata.PackageNotFoundError("missing") monkeypatch.setattr("backend.main._installed_version", missing_version) status = cast("dict[str, Any]", compatibility_status()) package_checks = cast("dict[str, bool]", status["package_checks"]) package_errors = cast("dict[str, str]", status["package_errors"]) assert status["ok"] is False assert package_checks["fastapi"] is False assert "fastapi" in package_errors def test_validate_runtime_policy_raises(monkeypatch: pytest.MonkeyPatch): """Runtime policy validation should fail when compatibility is not ok.""" def invalid_status() -> dict[str, object]: return {"ok": False} monkeypatch.setattr("backend.main.compatibility_status", invalid_status) with pytest.raises(RuntimeError): validate_runtime_policy() def test_compatibility_check_returns_status() -> None: """Compatibility endpoint helper should return policy payload.""" payload = compatibility_check() assert "ok" in payload