mirror of
https://github.com/sstent/FitTrack_ReportGenerator.git
synced 2026-01-26 00:52:03 +00:00
This commit introduces the initial version of the FitTrack Report Generator, a FastAPI application for analyzing workout files. Key features include: - Parsing of FIT, TCX, and GPX workout files. - Analysis of power, heart rate, speed, and elevation data. - Generation of summary reports and charts. - REST API for single and batch workout analysis. The project structure has been set up with a `src` directory for core logic, an `api` directory for the FastAPI application, and a `tests` directory for unit, integration, and contract tests. The development workflow is configured to use Docker and modern Python tooling.
102 lines
3.8 KiB
Python
102 lines
3.8 KiB
Python
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"
|