Files
plex-playlist/backend/tests/test_basic.py
copilotcoder d865be8b12
All checks were successful
CICD / Build and Push CICD Images (push) Successful in 41m21s
CICD / Build CICD Image Failure Postmortem (push) Has been skipped
CICD / Source Checks (push) Successful in 7m13s
CICD / Source Lanes Failure Postmortem (push) Has been skipped
CICD / Dependency Audits (Informational) (push) Successful in 7m37s
CICD / CICD Tests Complete (push) Successful in 10s
CICD / Build Tester Images (push) Successful in 3m45s
CICD / Build Release Images (push) Successful in 31m50s
CICD / Production Image Failures Postmortem (push) Has been skipped
CICD / Production Images Complete (push) Successful in 2s
CICD / Runtime Black-Box Integration Tests (push) Successful in 53s
CICD / Integration Tests Failure Postmortem (push) Has been skipped
CICD / End-to-End Tests (push) Successful in 3m17s
CICD / E2E Tests Failure Postmortem (push) Has been skipped
CICD / Promote Staging Images To Release (push) Successful in 2m4s
Restore backend coverage for runtime pin helpers
2026-07-20 18:06:58 -04:00

281 lines
8.9 KiB
Python

"""Basic tests for the backend application."""
from importlib import metadata
from pathlib import Path
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 import main as backend_main
from backend.main import (
app,
compatibility_check,
compatibility_status,
get_api_session,
read_root,
validate_runtime_policy,
)
parse_exact_pin = backend_main.__dict__["_parse_exact_pin"]
required_package_pins = backend_main.__dict__["_required_package_pins"]
required_package_pins_from_dependencies = backend_main.__dict__[
"_required_package_pins_from_dependencies"
]
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")
required_pins = cast("dict[str, str]", compatibility_status()["required_packages"])
def installed_version(package_name: str) -> str:
return required_pins[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
def test_parse_exact_pin_handles_valid_and_invalid_requirements() -> None:
"""Exact pin parsing should normalize extras and reject malformed pins."""
assert parse_exact_pin("psycopg[binary]==3.2.12 ; python_version >= '3.14'") == (
"psycopg",
"3.2.12",
)
assert parse_exact_pin("uvicorn>=0.51.0") is None
assert parse_exact_pin("==0.51.0") is None
assert parse_exact_pin("uvicorn==") is None
def test_required_package_pins_from_dependencies_filters_non_exact_pins() -> None:
"""Only exact dependency pins should be included in runtime policy."""
dependencies = [
"fastapi==0.139.0",
"uvicorn>=0.51.0",
"psycopg[binary]==3.2.12",
]
assert required_package_pins_from_dependencies(dependencies) == {
"fastapi": "0.139.0",
"psycopg": "3.2.12",
}
def test_required_package_pins_uses_installed_metadata(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Installed project metadata should be the primary pin source."""
def fake_requires(_: str) -> list[str]:
return ["fastapi==0.139.0", "psycopg[binary]==3.2.12"]
def unexpected_read_text(_: Path, encoding: str = "utf-8") -> str:
del encoding
raise AssertionError("pyproject fallback should not run")
monkeypatch.setattr(backend_main.metadata, "requires", fake_requires)
monkeypatch.setattr(Path, "read_text", unexpected_read_text)
assert required_package_pins() == {
"fastapi": "0.139.0",
"psycopg": "3.2.12",
}
def test_required_package_pins_falls_back_to_pyproject(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Pyproject should be used when installed metadata is unavailable or unusable."""
def missing_requires(_: str) -> list[str]:
raise metadata.PackageNotFoundError("missing")
def fake_read_text(_: Path, encoding: str = "utf-8") -> str:
del encoding
return "[project]\ndependencies = []\n"
def fake_toml_loads(_: str) -> dict[str, object]:
return {
"project": {
"dependencies": [
"fastapi==0.139.0",
123,
"psycopg[binary]==3.2.12",
]
}
}
monkeypatch.setattr(backend_main.metadata, "requires", missing_requires)
monkeypatch.setattr(Path, "read_text", fake_read_text)
monkeypatch.setattr(backend_main.tomllib, "loads", fake_toml_loads)
assert required_package_pins() == {
"fastapi": "0.139.0",
"psycopg": "3.2.12",
}
def test_required_package_pins_handles_invalid_pyproject_shapes(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Invalid pyproject structures should safely produce no required pins."""
def missing_requires(_: str) -> list[str]:
raise metadata.PackageNotFoundError("missing")
def fake_read_text(_: Path, encoding: str = "utf-8") -> str:
del encoding
return "[project]\ndependencies = []\n"
def invalid_project_loads(_: str) -> dict[str, object]:
return {"project": []}
def missing_dependencies_loads(_: str) -> dict[str, object]:
return {"project": {}}
monkeypatch.setattr(backend_main.metadata, "requires", missing_requires)
monkeypatch.setattr(Path, "read_text", fake_read_text)
monkeypatch.setattr(backend_main.tomllib, "loads", invalid_project_loads)
assert required_package_pins() == {}
monkeypatch.setattr(backend_main.tomllib, "loads", missing_dependencies_loads)
assert required_package_pins() == {}
def test_compatibility_status_rejects_malformed_required_python(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Malformed Python policy strings should fail compatibility checks."""
def empty_required_package_pins() -> dict[str, str]:
return {}
monkeypatch.setenv("BACKEND_REQUIRED_PYTHON", "3.14.1")
monkeypatch.setattr(
backend_main,
"_required_package_pins",
empty_required_package_pins,
)
status = cast("dict[str, Any]", compatibility_status())
assert status["ok"] is False
assert status["python_policy_valid"] is False