import pytest from fastapi.testclient import TestClient from api.main import app from uuid import uuid4 from unittest.mock import patch import zipfile import io client = TestClient(app) def create_zip_file(file_names_and_content): zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: for name, content in file_names_and_content.items(): zf.writestr(name, content) zip_buffer.seek(0) return zip_buffer @patch('src.db.session.get_db') @patch('src.core.batch_processor.BatchProcessor') def test_analyze_batch_success(mock_batch_processor_cls, mock_get_db): mock_db_session = mock_get_db.return_value mock_batch_processor_instance = mock_batch_processor_cls.return_value mock_batch_processor_instance.process_zip_file.return_value = [ {"analysis_id": str(uuid4()), "file_name": "workout1.fit", "status": "completed"}, {"analysis_id": str(uuid4()), "file_name": "workout2.tcx", "status": "completed"} ] zip_content = create_zip_file({"workout1.fit": b"dummy_fit_content", "workout2.tcx": b"dummy_tcx_content"}) response = client.post( "/api/analyze/batch", files={"zip_file": ("workouts.zip", zip_content.getvalue(), "application/zip")}, data={ "user_id": str(uuid4()), "ftp_value": 250.0 } ) assert response.status_code == 200 response_json = response.json() assert "batch_id" in response_json assert response_json["status"] == "completed" assert response_json["total_files"] == 2 assert "results" in response_json assert len(response_json["results"]) == 2 assert mock_batch_processor_instance.process_zip_file.called @patch('src.db.session.get_db') @patch('src.core.batch_processor.BatchProcessor') def test_analyze_batch_empty_zip(mock_batch_processor_cls, mock_get_db): zip_content = create_zip_file({}) response = client.post( "/api/analyze/batch", files={"zip_file": ("empty.zip", zip_content.getvalue(), "application/zip")} ) assert response.status_code == 400 assert response.json()["code"] == "EMPTY_ZIP_FILE" @patch('src.db.session.get_db') @patch('src.core.batch_processor.BatchProcessor') def test_analyze_batch_partial_failure(mock_batch_processor_cls, mock_get_db): mock_db_session = mock_get_db.return_value mock_batch_processor_instance = mock_batch_processor_cls.return_value mock_batch_processor_instance.process_zip_file.return_value = [ {"analysis_id": str(uuid4()), "file_name": "workout1.fit", "status": "completed"}, {"file_name": "workout_bad.fit", "status": "failed", "error_message": "Corrupted file"} ] zip_content = create_zip_file({"workout1.fit": b"dummy_fit_content", "workout_bad.fit": b"bad_content"}) response = client.post( "/api/analyze/batch", files={"zip_file": ("workouts.zip", zip_content.getvalue(), "application/zip")} ) assert response.status_code == 200 response_json = response.json() assert response_json["status"] == "completed_with_errors" assert response_json["total_files"] == 2 assert len(response_json["results"]) == 2 assert any(r["status"] == "failed" for r in response_json["results"]) @patch('src.db.session.get_db') @patch('src.core.batch_processor.BatchProcessor') def test_analyze_batch_internal_error(mock_batch_processor_cls, mock_get_db): mock_batch_processor_cls.side_effect = Exception("Unexpected error") zip_content = create_zip_file({"workout1.fit": b"dummy_fit_content"}) response = client.post( "/api/analyze/batch", files={"zip_file": ("workouts.zip", zip_content.getvalue(), "application/zip")} ) assert response.status_code == 500 assert response.json()["code"] == "INTERNAL_SERVER_ERROR"