mirror of
https://github.com/sstent/AICyclingCoach.git
synced 2026-01-25 16:41:58 +00:00
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, patch, MagicMock
|
|
from backend.app.services.ai_service import AIService, AIServiceError
|
|
from backend.app.models.workout import Workout
|
|
import json
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_analyze_workout_success():
|
|
"""Test successful workout analysis with valid API response"""
|
|
mock_db = MagicMock()
|
|
mock_prompt = MagicMock()
|
|
mock_prompt.format.return_value = "test prompt"
|
|
|
|
ai_service = AIService(mock_db)
|
|
ai_service.prompt_manager.get_active_prompt = AsyncMock(return_value=mock_prompt)
|
|
|
|
test_response = json.dumps({
|
|
"performance_summary": "Good workout",
|
|
"suggestions": ["More recovery"]
|
|
})
|
|
|
|
with patch('httpx.AsyncClient.post') as mock_post:
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {"choices": [{"message": {"content": test_response}}]}
|
|
mock_post.return_value = mock_response
|
|
|
|
workout = Workout(activity_type="cycling", duration_seconds=3600)
|
|
result = await ai_service.analyze_workout(workout)
|
|
|
|
assert "performance_summary" in result
|
|
assert len(result["suggestions"]) == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_plan_success():
|
|
"""Test plan generation with structured response"""
|
|
mock_db = AsyncMock()
|
|
ai_service = AIService(mock_db)
|
|
ai_service.prompt_manager.get_active_prompt = AsyncMock(return_value="Plan prompt: {rules_text} {goals}")
|
|
|
|
test_plan = {
|
|
"weeks": [{"workouts": ["ride"]}],
|
|
"focus": "endurance"
|
|
}
|
|
|
|
with patch('httpx.AsyncClient.post') as mock_post:
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {"choices": [{"message": {"content": json.dumps(test_plan)}}]}
|
|
mock_post.return_value = mock_response
|
|
|
|
result = await ai_service.generate_plan([], {})
|
|
assert "weeks" in result
|
|
assert result["focus"] == "endurance"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_retry_logic():
|
|
"""Test API request retries on failure"""
|
|
mock_db = MagicMock()
|
|
ai_service = AIService(mock_db)
|
|
|
|
with patch('httpx.AsyncClient.post') as mock_post:
|
|
mock_post.side_effect = Exception("API failure")
|
|
|
|
with pytest.raises(AIServiceError):
|
|
await ai_service._make_ai_request("test")
|
|
|
|
assert mock_post.call_count == 3
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_json_handling():
|
|
"""Test graceful handling of invalid JSON responses"""
|
|
mock_db = AsyncMock()
|
|
ai_service = AIService(mock_db)
|
|
|
|
with patch('httpx.AsyncClient.post') as mock_post:
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {"choices": [{"message": {"content": "invalid{json"}}]}
|
|
mock_post.return_value = mock_response
|
|
|
|
result = await ai_service.parse_rules_from_natural_language("test")
|
|
assert "raw_rules" in result
|
|
assert not result["structured"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_code_block_parsing():
|
|
"""Test extraction of JSON from code blocks"""
|
|
mock_db = AsyncMock()
|
|
ai_service = AIService(mock_db)
|
|
|
|
test_response = "```json\n" + json.dumps({"max_rides": 4}) + "\n```"
|
|
|
|
with patch('httpx.AsyncClient.post') as mock_post:
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {"choices": [{"message": {"content": test_response}}]}
|
|
mock_post.return_value = mock_response
|
|
|
|
result = await ai_service.evolve_plan({})
|
|
assert "max_rides" in result
|
|
assert result["max_rides"] == 4 |