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