Add comprehensive API endpoint tests

This commit is contained in:
2025-10-19 18:23:23 +02:00
parent 68d83e2a39
commit a057432a3e
6 changed files with 424 additions and 33 deletions

View File

@@ -5,10 +5,11 @@ from pathlib import Path
from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
from httpx import ASGITransport, AsyncClient
from src.server.fastapi_app import app
from src.server.models.config import AppConfig
from src.server.services.auth_service import auth_service
from src.server.services.config_service import ConfigService
@@ -40,21 +41,42 @@ def mock_config_service(config_service):
@pytest.fixture
def client():
"""Create test client."""
return TestClient(app)
async def client():
"""Create async test client."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
def test_get_config_public(client, mock_config_service):
@pytest.fixture
async def authenticated_client():
"""Create authenticated async test client."""
# Setup auth if not configured
if not auth_service.is_configured():
auth_service.setup_master_password("TestPass123!")
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
# Login to get token
r = await ac.post("/api/auth/login", json={"password": "TestPass123!"})
if r.status_code == 200:
token = r.json()["access_token"]
ac.headers["Authorization"] = f"Bearer {token}"
yield ac
@pytest.mark.anyio
async def test_get_config_public(client, mock_config_service):
"""Test getting configuration."""
resp = client.get("/api/config")
resp = await client.get("/api/config")
assert resp.status_code == 200
data = resp.json()
assert "name" in data
assert "data_dir" in data
def test_validate_config(client, mock_config_service):
@pytest.mark.anyio
async def test_validate_config(authenticated_client, mock_config_service):
"""Test configuration validation."""
cfg = {
"name": "Aniworld",
@@ -64,40 +86,43 @@ def test_validate_config(client, mock_config_service):
"backup": {"enabled": False},
"other": {},
}
resp = client.post("/api/config/validate", json=cfg)
resp = await authenticated_client.post("/api/config/validate", json=cfg)
assert resp.status_code == 200
body = resp.json()
assert body.get("valid") is True
def test_validate_invalid_config(client, mock_config_service):
@pytest.mark.anyio
async def test_validate_invalid_config(authenticated_client, mock_config_service):
"""Test validation of invalid configuration."""
cfg = {
"name": "Aniworld",
"backup": {"enabled": True, "path": None}, # Invalid
}
resp = client.post("/api/config/validate", json=cfg)
resp = await authenticated_client.post("/api/config/validate", json=cfg)
assert resp.status_code == 200
body = resp.json()
assert body.get("valid") is False
assert len(body.get("errors", [])) > 0
def test_update_config_unauthorized(client):
@pytest.mark.anyio
async def test_update_config_unauthorized(client):
"""Test that update requires authentication."""
update = {"scheduler": {"enabled": False}}
resp = client.put("/api/config", json=update)
resp = await client.put("/api/config", json=update)
assert resp.status_code in (401, 422)
def test_list_backups(client, mock_config_service):
@pytest.mark.anyio
async def test_list_backups(authenticated_client, mock_config_service):
"""Test listing configuration backups."""
# Create a sample config first
sample_config = AppConfig(name="TestApp", data_dir="test_data")
mock_config_service.save_config(sample_config, create_backup=False)
mock_config_service.create_backup(name="test_backup")
resp = client.get("/api/config/backups")
resp = await authenticated_client.get("/api/config/backups")
assert resp.status_code == 200
backups = resp.json()
assert isinstance(backups, list)
@@ -107,20 +132,22 @@ def test_list_backups(client, mock_config_service):
assert "created_at" in backups[0]
def test_create_backup(client, mock_config_service):
@pytest.mark.anyio
async def test_create_backup(authenticated_client, mock_config_service):
"""Test creating a configuration backup."""
# Create a sample config first
sample_config = AppConfig(name="TestApp", data_dir="test_data")
mock_config_service.save_config(sample_config, create_backup=False)
resp = client.post("/api/config/backups")
resp = await authenticated_client.post("/api/config/backups")
assert resp.status_code == 200
data = resp.json()
assert "name" in data
assert "message" in data
def test_restore_backup(client, mock_config_service):
@pytest.mark.anyio
async def test_restore_backup(authenticated_client, mock_config_service):
"""Test restoring configuration from backup."""
# Create initial config and backup
sample_config = AppConfig(name="TestApp", data_dir="test_data")
@@ -132,33 +159,35 @@ def test_restore_backup(client, mock_config_service):
mock_config_service.save_config(sample_config, create_backup=False)
# Restore from backup
resp = client.post("/api/config/backups/restore_test.json/restore")
resp = await authenticated_client.post("/api/config/backups/restore_test.json/restore")
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "TestApp" # Original name restored
def test_delete_backup(client, mock_config_service):
@pytest.mark.anyio
async def test_delete_backup(authenticated_client, mock_config_service):
"""Test deleting a configuration backup."""
# Create a sample config and backup
sample_config = AppConfig(name="TestApp", data_dir="test_data")
mock_config_service.save_config(sample_config, create_backup=False)
mock_config_service.create_backup(name="delete_test")
resp = client.delete("/api/config/backups/delete_test.json")
resp = await authenticated_client.delete("/api/config/backups/delete_test.json")
assert resp.status_code == 200
data = resp.json()
assert "deleted successfully" in data["message"]
def test_config_persistence(client, mock_config_service):
@pytest.mark.anyio
async def test_config_persistence(client, mock_config_service):
"""Test end-to-end configuration persistence."""
# Get initial config
resp = client.get("/api/config")
resp = await client.get("/api/config")
assert resp.status_code == 200
initial = resp.json()
# Validate it can be loaded again
resp2 = client.get("/api/config")
resp2 = await client.get("/api/config")
assert resp2.status_code == 200
assert resp2.json() == initial