Files
web-page-backend/music-lab/tests/test_metadata_generation.py

155 lines
5.9 KiB
Python

import pytest
import respx
from httpx import Response
from app.pipeline import metadata
@pytest.mark.asyncio
@respx.mock
async def test_metadata_calls_claude_and_parses_json(monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "test-key")
payload = {
"content": [{"type": "text", "text": '{"title":"[Lo-fi] Drive | 85BPM",'
'"description":"chill","tags":["lofi","85bpm"],'
'"category_id":10}'}]
}
respx.post("https://api.anthropic.com/v1/messages").mock(
return_value=Response(200, json=payload)
)
result = await metadata.generate(
track={"title": "Drive", "genre": "lo-fi", "bpm": 85, "key": "C", "scale": "minor",
"moods": ["chill"], "instruments": ["piano"]},
template={"title": "[{genre}] {title} | {bpm}BPM",
"description": "{title}\n", "tags": [], "category_id": 10},
trend_keywords=["lofi", "study"],
feedback="",
)
assert result["title"].startswith("[Lo-fi]")
assert "lofi" in result["tags"]
assert result["used_fallback"] is False
@pytest.mark.asyncio
async def test_metadata_fallback_when_no_api_key(monkeypatch):
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
result = await metadata.generate(
track={"title": "Drive", "genre": "lo-fi", "bpm": 85, "key": "C", "scale": "minor",
"moods": [], "instruments": []},
template={"title": "[{genre}] {title} | {bpm}BPM",
"description": "{title}", "tags": ["lofi"], "category_id": 10},
trend_keywords=[],
)
# 템플릿 변수 그대로 치환된 폴백
assert result["title"] == "[lo-fi] Drive | 85BPM"
assert result["used_fallback"] is True
@pytest.mark.asyncio
@respx.mock
async def test_metadata_includes_feedback_in_prompt(monkeypatch):
import json
monkeypatch.setenv("ANTHROPIC_API_KEY", "test-key")
captured = {}
def hook(req):
captured["body"] = json.loads(req.content)
return Response(200, json={"content": [{"type": "text",
"text": '{"title":"x","description":"y","tags":[],"category_id":10}'}]})
respx.post("https://api.anthropic.com/v1/messages").mock(side_effect=hook)
await metadata.generate(
track={"title": "X", "genre": "lo-fi", "bpm": 85, "key": "C", "scale": "minor",
"moods": [], "instruments": []},
template={"title": "{title}", "description": "{title}", "tags": [], "category_id": 10},
trend_keywords=[],
feedback="제목을 짧게",
)
assert "제목을 짧게" in str(captured["body"])
@pytest.mark.asyncio
@respx.mock
async def test_metadata_falls_back_on_api_error(monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "test-key")
respx.post("https://api.anthropic.com/v1/messages").mock(
return_value=Response(500)
)
result = await metadata.generate(
track={"title": "Drive", "genre": "lo-fi", "bpm": 85, "key": "C", "scale": "minor",
"moods": [], "instruments": []},
template={"title": "[{genre}] {title}", "description": "x", "tags": ["lofi"], "category_id": 10},
trend_keywords=[],
)
assert result["used_fallback"] is True
assert "Drive" in result["title"]
@pytest.mark.asyncio
@respx.mock
async def test_metadata_with_tracks_includes_chapter_format(monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "k")
captured = {}
def hook(req):
import json as _json
captured["body"] = _json.loads(req.content)
return Response(200, json={"content": [{"type": "text", "text":
'{"title":"Lo-Fi Mix 3 Tracks","description":"Track 1: [00:00] T1\\nTrack 2: [03:00] T2",'
'"tags":["lofi","mix"],"category_id":10}'}]})
respx.post("https://api.anthropic.com/v1/messages").mock(side_effect=hook)
result = await metadata.generate(
track={"title": "Mix", "genre": "mix", "duration_sec": 600,
"moods": []},
template={"title": "{title}", "description": "{title}",
"tags": [], "category_id": 10},
trend_keywords=[],
tracks=[
{"id": 1, "title": "T1", "start_offset_sec": 0, "duration_sec": 180},
{"id": 2, "title": "T2", "start_offset_sec": 180, "duration_sec": 200},
{"id": 3, "title": "T3", "start_offset_sec": 380, "duration_sec": 220},
],
)
body_str = str(captured["body"])
assert "T1" in body_str and "T2" in body_str and "T3" in body_str
assert "00:00" in body_str
assert result["used_fallback"] is False
@pytest.mark.asyncio
async def test_metadata_fallback_with_tracks(monkeypatch):
"""API 키 없을 때 폴백에서도 트랙 챕터 포함."""
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
result = await metadata.generate(
track={"title": "Mix", "genre": "mix", "duration_sec": 600, "moods": []},
template={"title": "{title}", "description": "{title}",
"tags": [], "category_id": 10},
trend_keywords=[],
tracks=[
{"id": 1, "title": "T1", "start_offset_sec": 0, "duration_sec": 180},
{"id": 2, "title": "T2", "start_offset_sec": 180, "duration_sec": 200},
],
)
assert result["used_fallback"] is True
assert "00:00" in result["description"]
assert "T1" in result["description"]
assert "T2" in result["description"]
def test_format_chapters_under_hour():
from app.pipeline.metadata import _format_chapters
out = _format_chapters([
{"start_offset_sec": 0, "title": "T1"},
{"start_offset_sec": 180, "title": "T2"},
])
assert "00:00 T1" in out
assert "03:00 T2" in out
def test_format_chapters_over_hour():
from app.pipeline.metadata import _format_chapters
out = _format_chapters([
{"start_offset_sec": 0, "title": "T1"},
{"start_offset_sec": 3700, "title": "T2"},
])
assert "00:00 T1" in out
assert "01:01:40 T2" in out