69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
# Copyright 2026 Umbra Tools (umbratools.com)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Guard tests for the character-discovery prompt's over-detection rules.
|
|
|
|
These rules exist because discovery was missing obvious speakers (characters
|
|
identified only by a comparative/ordinal designation, e.g. "the tallest",
|
|
"the oldest" in a crowd of six) and producing polluted names ("Walking tallest").
|
|
If someone rewrites CHARACTER_DISCOVERY_PROMPT.md and drops them, these fail.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from umbraaudio.analysis.character_discovery import (
|
|
PROMPT_PATH,
|
|
CharacterDiscoverer,
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def prompt() -> str:
|
|
d = CharacterDiscoverer.__new__(CharacterDiscoverer)
|
|
d._prompt_template = None
|
|
return d._load_prompt()
|
|
|
|
|
|
def test_prompt_loads_and_renders(prompt: str) -> None:
|
|
# The real code path uses .replace(), not .format(), because the JSON schema
|
|
# block contains literal braces.
|
|
rendered = prompt.replace("{known_speakers_section}", "(Aksenzo, Hallr)")
|
|
assert "{known_speakers_section}" not in rendered
|
|
assert len(rendered) > 5000
|
|
|
|
|
|
def test_rule_8_every_speaking_line_is_a_speaker(prompt: str) -> None:
|
|
assert "Rule 8: Every Speaking Line Is a Speaker" in prompt
|
|
# Must name comparative/ordinal designations explicitly.
|
|
for designation in ["the tallest", "the shortest", "the youngest", "the oldest"]:
|
|
assert designation in prompt, f"missing designation example: {designation}"
|
|
# Must state the over-detection bias.
|
|
low = prompt.lower()
|
|
assert "still list both" in low, "must instruct to list both when unsure"
|
|
|
|
|
|
def test_rule_9_name_hygiene(prompt: str) -> None:
|
|
assert "Rule 9: Name Hygiene" in prompt
|
|
low = prompt.lower()
|
|
assert "narration verbs" in low
|
|
# The concrete bug this rule fixes.
|
|
assert "Walking tallest" in prompt
|
|
# Internal labels must never end up in a name.
|
|
assert "(Scene 2)" in prompt
|
|
|
|
|
|
def test_unidentified_covers_designations(prompt: str) -> None:
|
|
low = prompt.lower()
|
|
assert "designation" in low
|
|
# Must forbid upgrading a designation into a guessed proper name.
|
|
assert "never becomes" in low or "not upgrade" in low
|
|
|
|
|
|
def test_prompt_file_is_markdown() -> None:
|
|
assert PROMPT_PATH.suffix == ".md"
|
|
assert PROMPT_PATH.exists()
|