feat: add unified asset retry endpoint
Some checks failed
Build and Push Docker Images / changes (push) Has been cancelled
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
Build and Push Docker Images / build-admin-frontend (push) Has been cancelled
Some checks failed
Build and Push Docker Images / changes (push) Has been cancelled
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
Build and Push Docker Images / build-admin-frontend (push) Has been cancelled
This commit is contained in:
@@ -264,7 +264,10 @@ class TestAudio:
|
||||
mock_tts_provider.assert_awaited_once()
|
||||
|
||||
def test_get_audio_failure_updates_status(self, auth_client: TestClient, test_story):
|
||||
with patch("app.services.provider_router.text_to_speech", new_callable=AsyncMock) as mock_tts:
|
||||
with patch(
|
||||
"app.services.provider_router.text_to_speech",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_tts:
|
||||
mock_tts.side_effect = Exception("TTS provider timeout")
|
||||
response = auth_client.get(f"/api/audio/{test_story.id}")
|
||||
assert response.status_code == 500
|
||||
@@ -383,12 +386,83 @@ class TestImageGenerateSuccess:
|
||||
assert data["last_error"] is None
|
||||
|
||||
|
||||
class TestAssetRetry:
|
||||
"""Tests for unified asset retry endpoint."""
|
||||
|
||||
def test_retry_cover_image_success(
|
||||
self,
|
||||
auth_client: TestClient,
|
||||
degraded_story_with_text,
|
||||
mock_image_provider,
|
||||
):
|
||||
response = auth_client.post(
|
||||
f"/api/stories/{degraded_story_with_text.id}/assets/retry",
|
||||
json={"assets": ["image"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["image_url"] == "https://example.com/image.png"
|
||||
assert data["generation_status"] == "completed"
|
||||
assert data["image_status"] == "ready"
|
||||
assert data["audio_status"] == "not_requested"
|
||||
assert data["last_error"] is None
|
||||
mock_image_provider.assert_awaited_once()
|
||||
|
||||
def test_retry_storybook_missing_page_image_success(
|
||||
self,
|
||||
auth_client: TestClient,
|
||||
storybook_story,
|
||||
):
|
||||
async def image_side_effect(prompt: str, **kwargs):
|
||||
return "https://example.com/retried-page.png"
|
||||
|
||||
with patch(
|
||||
"app.services.story_service.generate_image",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_image:
|
||||
mock_image.side_effect = image_side_effect
|
||||
|
||||
response = auth_client.post(
|
||||
f"/api/stories/{storybook_story.id}/assets/retry",
|
||||
json={"assets": ["image"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["generation_status"] == "completed"
|
||||
assert data["image_status"] == "ready"
|
||||
assert data["audio_status"] == "not_requested"
|
||||
assert data["last_error"] is None
|
||||
assert data["pages"][1]["image_url"] == "https://example.com/retried-page.png"
|
||||
mock_image.assert_awaited_once()
|
||||
|
||||
def test_retry_audio_on_storybook_is_rejected(
|
||||
self,
|
||||
auth_client: TestClient,
|
||||
storybook_story,
|
||||
):
|
||||
response = auth_client.post(
|
||||
f"/api/stories/{storybook_story.id}/assets/retry",
|
||||
json={"assets": ["audio"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json()["detail"] == "Story has no text"
|
||||
|
||||
|
||||
class TestStorybookGenerate:
|
||||
"""Tests for storybook generation status handling."""
|
||||
|
||||
def test_generate_storybook_success(self, auth_client: TestClient):
|
||||
with patch("app.services.story_service.generate_storybook", new_callable=AsyncMock) as mock_storybook:
|
||||
with patch("app.services.story_service.generate_image", new_callable=AsyncMock) as mock_image:
|
||||
with patch(
|
||||
"app.services.story_service.generate_storybook",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_storybook:
|
||||
with patch(
|
||||
"app.services.story_service.generate_image",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_image:
|
||||
mock_storybook.return_value = build_storybook_output()
|
||||
mock_image.side_effect = [
|
||||
"https://example.com/storybook-cover.png",
|
||||
@@ -422,8 +496,14 @@ class TestStorybookGenerate:
|
||||
slug = prompt.split()[0].lower()
|
||||
return f"https://example.com/{slug}.png"
|
||||
|
||||
with patch("app.services.story_service.generate_storybook", new_callable=AsyncMock) as mock_storybook:
|
||||
with patch("app.services.story_service.generate_image", new_callable=AsyncMock) as mock_image:
|
||||
with patch(
|
||||
"app.services.story_service.generate_storybook",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_storybook:
|
||||
with patch(
|
||||
"app.services.story_service.generate_image",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_image:
|
||||
mock_storybook.return_value = build_storybook_output()
|
||||
mock_image.side_effect = image_side_effect
|
||||
|
||||
|
||||
Reference in New Issue
Block a user