Add pytest suite, unlimited daily cap, and vocal batch generator.
Tests cover curation, Lyria, queue, and API routes. Setting max_new_songs_per_day to 0 disables the limit; generate-batch runs 20 curated multilingual vocal directions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from google.genai import errors as genai_errors
|
||||
|
||||
from ozan_radio.dj import TrackPlan
|
||||
from ozan_radio.lyria import (
|
||||
_block_reason,
|
||||
_build_lyria_prompt,
|
||||
_extract_audio,
|
||||
_extract_lyrics,
|
||||
_response_parts,
|
||||
_sanitize_for_retry,
|
||||
_wants_vocals,
|
||||
friendly_lyria_error,
|
||||
)
|
||||
from ozan_radio.settings import LyriaSettings
|
||||
|
||||
|
||||
def test_wants_vocals_explicit_no():
|
||||
assert _wants_vocals("Instrumental. No vocals. Build slowly.") is False
|
||||
|
||||
|
||||
def test_wants_vocals_whispered_texture():
|
||||
assert _wants_vocals("whispered vocal textures drift in and out") is True
|
||||
|
||||
|
||||
def test_wants_vocals_griot_request():
|
||||
assert _wants_vocals("West African griot vocals over desert dub") is True
|
||||
|
||||
|
||||
def test_build_lyria_prompt_instrumental(sample_plan, lyria_instrumental):
|
||||
out = _build_lyria_prompt(sample_plan, lyria_instrumental)
|
||||
assert "Instrumental only, no vocals" in out
|
||||
assert sample_plan.lyria_prompt in out
|
||||
|
||||
|
||||
def test_build_lyria_prompt_mix(sample_plan, lyria_mix):
|
||||
out = _build_lyria_prompt(sample_plan, lyria_mix)
|
||||
assert "Wordless vocal textures" in out
|
||||
assert "Instrumental only" not in out
|
||||
|
||||
|
||||
def test_build_lyria_prompt_vocals_override_mode(sample_plan, lyria_instrumental):
|
||||
plan = TrackPlan(
|
||||
id="v1",
|
||||
title="Griot Night",
|
||||
mood="",
|
||||
dj_line="",
|
||||
lyria_prompt="Sahel griot male vocal chants over dub.",
|
||||
)
|
||||
out = _build_lyria_prompt(plan, lyria_instrumental)
|
||||
assert "Include lead vocals" in out
|
||||
|
||||
|
||||
def test_build_lyria_prompt_hindi_language(sample_plan):
|
||||
cfg = LyriaSettings(
|
||||
model="lyria-3-pro-preview",
|
||||
vocal_mode="vocals",
|
||||
language="hi",
|
||||
singer_profile="male_baritone",
|
||||
output_format="mp3",
|
||||
prefer_instrumental=False,
|
||||
)
|
||||
plan = TrackPlan(
|
||||
id="h1",
|
||||
title="Devotional",
|
||||
mood="",
|
||||
dj_line="",
|
||||
lyria_prompt="Hindi devotional desert dub.",
|
||||
)
|
||||
out = _build_lyria_prompt(plan, cfg)
|
||||
assert "Hindi" in out
|
||||
assert "Male Baritone" in out
|
||||
|
||||
|
||||
def test_sanitize_for_retry_softens_griot():
|
||||
raw = "West African griot vocal sample over dub"
|
||||
soft = _sanitize_for_retry(raw)
|
||||
assert "griot" not in soft.lower() or "Sahel" in soft
|
||||
assert soft != raw
|
||||
|
||||
|
||||
def test_response_parts_from_candidates():
|
||||
part = SimpleNamespace(text=None, inline_data=SimpleNamespace(data=b"audio"))
|
||||
content = SimpleNamespace(parts=[part])
|
||||
candidate = SimpleNamespace(content=content, finish_reason="STOP")
|
||||
response = SimpleNamespace(parts=None, candidates=[candidate])
|
||||
parts = _response_parts(response)
|
||||
assert len(parts) == 1
|
||||
assert _extract_audio(parts) == b"audio"
|
||||
|
||||
|
||||
def test_response_parts_empty_candidates():
|
||||
response = SimpleNamespace(parts=None, candidates=[])
|
||||
assert _response_parts(response) == []
|
||||
|
||||
|
||||
def test_extract_lyrics_joins_text():
|
||||
parts = [
|
||||
SimpleNamespace(text="[[A0]]", inline_data=None),
|
||||
SimpleNamespace(text="line two", inline_data=None),
|
||||
]
|
||||
assert "line two" in _extract_lyrics(parts)
|
||||
|
||||
|
||||
def test_block_reason_strips_enum_prefix():
|
||||
feedback = SimpleNamespace(block_reason="BlockedReason.PROHIBITED_CONTENT")
|
||||
response = SimpleNamespace(prompt_feedback=feedback)
|
||||
assert _block_reason(response) == "PROHIBITED_CONTENT"
|
||||
|
||||
|
||||
def test_friendly_lyria_error_quota():
|
||||
exc = genai_errors.ClientError(
|
||||
429,
|
||||
{"error": {"message": "resource_exhausted", "status": "RESOURCE_EXHAUSTED"}},
|
||||
None,
|
||||
)
|
||||
msg = friendly_lyria_error(exc)
|
||||
assert "quota" in msg.lower() or "resource" in msg.lower()
|
||||
|
||||
|
||||
def test_friendly_lyria_error_runtime_passthrough():
|
||||
assert friendly_lyria_error(RuntimeError("custom")) == "custom"
|
||||
Reference in New Issue
Block a user