84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
# Copyright 2026 Umbra Tools (umbratools.com)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Regression tests for voice-queue speaker lookup.
|
|
|
|
Every queued item in a real project's voice_queue.json had failed with
|
|
``error: "'primary_name'"``. ``_speaker_info`` matched only an exact
|
|
``primary_name``, so any other spelling returned ``{}``, and
|
|
``build_profile_from_speaker`` subscripts that key directly - the job died
|
|
before a single sample was synthesised and the reviewer just saw an error.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from umbraaudio.voices.voice_queue import VoiceQueue
|
|
|
|
|
|
@pytest.fixture()
|
|
def queue(tmp_path: Path) -> VoiceQueue:
|
|
data_dir = tmp_path / ".umbra"
|
|
data_dir.mkdir(parents=True)
|
|
(data_dir / "speakers.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"speakers": [
|
|
{"primary_name": "Dealer", "aliases": ["Trader Sela"]},
|
|
{"primary_name": "The Sergeant", "aliases": []},
|
|
]
|
|
}
|
|
)
|
|
)
|
|
return VoiceQueue(data_dir, config=None)
|
|
|
|
|
|
def test_exact_primary_name_matches(queue: VoiceQueue) -> None:
|
|
assert queue._speaker_info("Dealer")["primary_name"] == "Dealer"
|
|
|
|
|
|
def test_case_insensitive_match(queue: VoiceQueue) -> None:
|
|
assert queue._speaker_info("dealer")["primary_name"] == "Dealer"
|
|
|
|
|
|
def test_surrounding_whitespace_is_ignored(queue: VoiceQueue) -> None:
|
|
assert queue._speaker_info(" Dealer ")["primary_name"] == "Dealer"
|
|
|
|
|
|
def test_alias_resolves_to_its_speaker(queue: VoiceQueue) -> None:
|
|
"""A stale label or a name the reviewer used should still find the voice."""
|
|
assert queue._speaker_info("Trader Sela")["primary_name"] == "Dealer"
|
|
|
|
|
|
def test_unknown_speaker_returns_empty(queue: VoiceQueue) -> None:
|
|
assert queue._speaker_info("Nobody At All") == {}
|
|
|
|
|
|
def test_missing_speakers_file_does_not_raise(tmp_path: Path) -> None:
|
|
data_dir = tmp_path / ".umbra"
|
|
data_dir.mkdir(parents=True)
|
|
q = VoiceQueue(data_dir, config=None)
|
|
assert q._speaker_info("Anyone") == {}
|
|
|
|
|
|
def test_corrupt_speakers_json_does_not_raise(tmp_path: Path) -> None:
|
|
data_dir = tmp_path / ".umbra"
|
|
data_dir.mkdir(parents=True)
|
|
(data_dir / "speakers.json").write_text("{ not json")
|
|
q = VoiceQueue(data_dir, config=None)
|
|
assert q._speaker_info("Anyone") == {}
|
|
|
|
|
|
def test_lookup_never_yields_a_dict_without_primary_name(queue: VoiceQueue) -> None:
|
|
"""The invariant that was violated: whatever we return must be buildable.
|
|
|
|
``build_profile_from_speaker`` subscripts ``primary_name``, so returning a
|
|
speaker-less dict turns into a KeyError inside the worker.
|
|
"""
|
|
for name in ("Dealer", "dealer", "Trader Sela", "The Sergeant"):
|
|
info = queue._speaker_info(name)
|
|
assert "primary_name" in info, f"{name!r} returned a dict with no primary_name"
|