Sort_YM/tests/test_analyze.py
2026-08-16 19:33:35 +03:00

87 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
from pathlib import Path
from sort_ym import analyze
def test_theme_pattern_accepts_english_snake_case():
assert re.match(analyze.THEME_PATTERN, "digital_aging")
assert re.match(analyze.THEME_PATTERN, "modern_loneliness")
def test_theme_pattern_rejects_pilot_failure_modes():
# Пилот без паттерна давал кириллицу и составные значения через слэш вперемешку
# с английским в одном прогоне - агрегировать такое невозможно.
assert not re.match(analyze.THEME_PATTERN, "одиночество")
assert not re.match(analyze.THEME_PATTERN, "эмоциональное выгорание")
assert not re.match(analyze.THEME_PATTERN, "Digital_Aging")
assert not re.match(analyze.THEME_PATTERN, "ethereal/hopeful")
assert not re.match(analyze.THEME_PATTERN, "ab")
def test_mood_enum_excludes_stance_axis():
# Смешение осей в пилоте давало "aggressive/triumphant"; ирония/сарказм живут в stance.
assert "ironic" not in analyze.MOODS
assert "sarcastic" not in analyze.MOODS
assert "sincere" not in analyze.MOODS
assert len(analyze.MOODS) == len(set(analyze.MOODS))
def test_response_schema_constrains_mood_and_themes():
props = analyze.RESPONSE_SCHEMA["properties"]
assert props["mood"]["items"]["enum"] == analyze.MOODS
assert props["mood"]["maxItems"] == 3
assert props["themes"]["items"]["pattern"] == analyze.THEME_PATTERN
assert set(analyze.RESPONSE_SCHEMA["required"]) == set(props)
def test_lyrics_hash_changes_with_text():
assert analyze.lyrics_hash("текст") == analyze.lyrics_hash("текст")
assert analyze.lyrics_hash("текст") != analyze.lyrics_hash("другой текст")
def test_verify_key_line_accepts_verbatim_quote_ignoring_whitespace_and_case():
text = "Первая строка\nА я иду, шагаю по Москве\nТретья строка"
assert analyze.verify_key_line("А я иду, шагаю по Москве", text)
assert analyze.verify_key_line("а я иду, шагаю по москве", text)
def test_verify_key_line_rejects_paraphrase_and_empty():
text = "А я иду, шагаю по Москве"
assert not analyze.verify_key_line("Герой идёт по городу", text)
assert not analyze.verify_key_line("", text)
def test_is_fresh_requires_model_prompt_version_and_hash_match():
entry = {"model": "m1", "prompt_version": 1, "lyrics_hash": "h1"}
assert analyze.is_fresh(entry, "m1", 1, "h1")
assert not analyze.is_fresh(entry, "m2", 1, "h1")
assert not analyze.is_fresh(entry, "m1", 2, "h1")
assert not analyze.is_fresh(entry, "m1", 1, "h2")
assert not analyze.is_fresh(None, "m1", 1, "h1")
def test_is_fresh_false_for_error_marker():
# Ошибочные записи перезапрашиваются при следующем прогоне - это и есть механизм
# "повторить упавшие треки" вместо отдельного флага.
entry = {"model": "m1", "prompt_version": 1, "lyrics_hash": "h1", "error": "TimeoutError: ..."}
assert not analyze.is_fresh(entry, "m1", 1, "h1")
def test_top_themes_orders_by_frequency_and_limits():
cache = {
"1": {"themes": ["loneliness", "city"]},
"2": {"themes": ["loneliness", "memory"]},
"3": {"themes": ["loneliness", "city"]},
"4": {"error": "TimeoutError: ..."},
}
assert analyze.top_themes(cache, limit=2) == ["loneliness", "city"]
def test_load_analysis_returns_empty_dict_when_missing(tmp_path: Path):
assert analyze.load_analysis(tmp_path) == {}