kol-insight/backend/tests/test_error_handling.py
zfc d838a9bea2 feat(deploy): 完成 Phase 3 优化与测试
- T-013 错误处理: 增强 API 错误处理,添加日志记录
- T-015 视频链接: 已在 ResultTable 中实现点击跳转
- T-016 部署配置: 添加前后端 Dockerfile 和 docker-compose.yml
- 新增 11 个错误处理测试用例,共 55 个测试全部通过
- 测试覆盖率达到 93%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:42:40 +08:00

164 lines
5.9 KiB
Python

import pytest
from unittest.mock import patch, AsyncMock, MagicMock
from httpx import AsyncClient, ASGITransport
from sqlalchemy.exc import SQLAlchemyError
from app.main import app
from app.models import KolVideo
from app.api.v1.export import set_export_data, get_export_data
class TestErrorHandling:
"""Tests for error handling scenarios."""
@pytest.fixture
async def client(self, override_get_db):
"""Create test client with dependency override."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.fixture
async def seed_data(self, test_session, sample_video_data):
"""Seed test data."""
data = sample_video_data.copy()
data["item_id"] = "error_test_001"
data["star_id"] = "error_star_001"
video = KolVideo(**data)
test_session.add(video)
await test_session.commit()
return video
# Query API error handling tests
@patch("app.api.v1.query.query_videos")
@patch("app.api.v1.query.get_brand_names", new_callable=AsyncMock)
async def test_query_database_error(
self, mock_brand, mock_query, client
):
"""Test query returns error on database failure."""
mock_brand.return_value = {}
mock_query.side_effect = SQLAlchemyError("Database connection failed")
response = await client.post(
"/api/v1/query",
json={"type": "star_id", "values": ["test_id"]},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is False
assert "数据库连接失败" in data["error"]
@patch("app.api.v1.query.get_brand_names", new_callable=AsyncMock)
async def test_query_brand_api_failure_fallback(
self, mock_brand, client, test_session, seed_data
):
"""Test query continues with fallback when brand API fails."""
mock_brand.side_effect = Exception("Brand API timeout")
response = await client.post(
"/api/v1/query",
json={"type": "star_id", "values": ["error_star_001"]},
)
assert response.status_code == 200
data = response.json()
# Should still succeed, brand name falls back to brand_id
assert data["success"] is True
assert data["total"] >= 0
@patch("app.api.v1.query.query_videos")
@patch("app.api.v1.query.get_brand_names", new_callable=AsyncMock)
async def test_query_generic_error(
self, mock_brand, mock_query, client
):
"""Test query returns error on unexpected exception."""
mock_brand.return_value = {}
mock_query.side_effect = Exception("Unexpected error")
response = await client.post(
"/api/v1/query",
json={"type": "star_id", "values": ["test_id"]},
)
assert response.status_code == 200
data = response.json()
assert data["success"] is False
assert "Unexpected error" in data["error"]
# Export API error handling tests
async def test_export_no_data_error(self, client):
"""Test export returns error when no data is cached."""
# Clear cached data
set_export_data([])
response = await client.get("/api/v1/export?format=xlsx")
assert response.status_code == 400
data = response.json()
assert data["success"] is False
assert "无数据可导出" in data["error"]
async def test_export_with_data_success(self, client, sample_video_data):
"""Test export succeeds when data is cached."""
# Set cached data
set_export_data([sample_video_data])
response = await client.get("/api/v1/export?format=xlsx")
assert response.status_code == 200
assert "application/vnd.openxmlformats" in response.headers["content-type"]
async def test_export_csv_with_data_success(self, client, sample_video_data):
"""Test CSV export succeeds when data is cached."""
set_export_data([sample_video_data])
response = await client.get("/api/v1/export?format=csv")
assert response.status_code == 200
assert "text/csv" in response.headers["content-type"]
@patch("app.api.v1.export.generate_excel")
async def test_export_generation_error(
self, mock_generate, client, sample_video_data
):
"""Test export returns error when file generation fails."""
mock_generate.side_effect = Exception("Excel generation failed")
set_export_data([sample_video_data])
response = await client.get("/api/v1/export?format=xlsx")
assert response.status_code == 500
data = response.json()
assert data["success"] is False
assert "导出失败" in data["error"]
# Input validation tests
async def test_query_validation_empty_values(self, client):
"""Test query returns 422 for empty values."""
response = await client.post(
"/api/v1/query",
json={"type": "star_id", "values": []},
)
assert response.status_code == 422
async def test_query_validation_invalid_type(self, client):
"""Test query returns 422 for invalid query type."""
response = await client.post(
"/api/v1/query",
json={"type": "invalid_type", "values": ["test"]},
)
assert response.status_code == 422
async def test_query_validation_missing_type(self, client):
"""Test query returns 422 for missing type field."""
response = await client.post(
"/api/v1/query",
json={"values": ["test"]},
)
assert response.status_code == 422
async def test_query_validation_missing_values(self, client):
"""Test query returns 422 for missing values field."""
response = await client.post(
"/api/v1/query",
json={"type": "star_id"},
)
assert response.status_code == 422