Files
jellybit/internal/naming/effective_test.go
T
avandClaude Opus 4.8 5948f4d219 Веб-UI: режиссёр в блоке «Распознано как» на странице загрузки
На /download/{id} поле «Режиссёр» было захардкожено прочерком, хотя экран
ревью режиссёра уже выводит: слоистое разрешение полей ярлыка было заперто в
неэкспортируемом worker.effectiveDisplayName. Из-за этого билдеры вью видели
только слой распознавания+матч (rd.Plan.Director) без слоя контекста — то же
на экране ревью.

Вынес разрешение в экспортируемую naming.EffectiveFields(parsedContext, plan)
LabelFields с методом Label(): выбор слоя по сырым значениям (как прежде),
выбранные скаляры возвращаются очищенными (sanitize идемпотентен, display_name
побайтно тот же). effectiveDisplayName стал тонкой обёрткой; страница загрузки
и экран ревью берут режиссёра из той же функции — согласованно с заголовком.

OpenSpec: web-ui (ADDED «Режиссёр в блоке распознавания страницы загрузки»),
review (MODIFIED «Инфо и предпросмотр выбранного источника» — слоистое
разрешение с фолбэком на контекст). Change заархивирован. Беклог: закрыта
rezhisser-v-kartochke-zagruzki.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 21:21:27 +03:00

113 lines
4.7 KiB
Go
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.
package naming
import (
"encoding/json"
"testing"
"git.vakhrushev.me/av/jellybit/internal/recognize"
)
func mustJSON(t *testing.T, f Fields) string {
t.Helper()
b, err := json.Marshal(f)
if err != nil {
t.Fatalf("marshal fields: %v", err)
}
return string(b)
}
func episode(season int) recognize.PlanFile {
s := season
return recognize.PlanFile{Role: recognize.RoleEpisode, Season: &s}
}
// EffectiveFields: слоистое разрешение полей ярлыка (план → контекст) и сводка
// сезонов из плана/контекста. Проверяем и итоговый Label (совпадение с прежним
// display_name), и отдельно очищенное поле Director (его печатает шаблон).
func TestEffectiveFields(t *testing.T) {
cases := []struct {
name string
parsed Fields // пустой Title → parsed_context не задаётся
plan recognize.Plan
want string // ожидаемый Label()
wantDir string // ожидаемый Director
}{
{
name: "матч даёт режиссёра и год",
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Director: "Дени Вильнёв", Year: 2024},
want: "Дюна (Дени Вильнёв, 2024)",
wantDir: "Дени Вильнёв",
},
{
name: "режиссёр из контекста переживает распознавание без матча",
parsed: Fields{Type: "movie", Title: "Дюна", Director: "Дени Вильнёв", Year: 2021},
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Year: 2024}, // без director
want: "Дюна (Дени Вильнёв, 2024)", // год из плана, режиссёр из контекста
wantDir: "Дени Вильнёв",
},
{
name: "режиссёр матча бьёт контекстного",
parsed: Fields{Type: "movie", Title: "Дюна", Director: "Кто-то из контекста"},
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Director: "Дени Вильнёв", Year: 2024},
want: "Дюна (Дени Вильнёв, 2024)",
wantDir: "Дени Вильнёв",
},
{
name: "год из контекста, когда плана нет",
parsed: Fields{Type: "movie", Title: "Брат", Year: 1997},
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Брат"}, // year 0, без режиссёра
want: "Брат (1997)",
wantDir: "",
},
{
name: "сериал: сводка сезонов из плана",
plan: recognize.Plan{Type: recognize.MediaSeries, Title: "Фарго",
Files: []recognize.PlanFile{episode(1), episode(2), episode(3)}},
want: "Фарго. Сезоны 13",
wantDir: "",
},
{
name: "сериал: сезон из контекста, когда в плане нет эпизодов",
parsed: Fields{Type: "series", Title: "Сёгун", Season: ptr(2)},
plan: recognize.Plan{Type: recognize.MediaSeries, Title: "Сёгун", Year: 2024},
want: "Сёгун (2024). Сезон 2",
wantDir: "",
},
{
name: "режиссёр из управляющих символов и лишних пробелов очищается",
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Director: " Дени\tВильнёв\n ", Year: 2024},
want: "Дюна (Дени Вильнёв, 2024)",
wantDir: "Дени Вильнёв",
},
{
name: "режиссёр только из управляющих символов даёт пустое поле и выпадает из ярлыка",
plan: recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Director: "\x01\x02", Year: 2024},
want: "Дюна (2024)",
wantDir: "",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
parsed := ""
if c.parsed.Title != "" {
parsed = mustJSON(t, c.parsed)
}
ef := EffectiveFields(parsed, c.plan)
if got := ef.Label(); got != c.want {
t.Errorf("Label() = %q, want %q", got, c.want)
}
if ef.Director != c.wantDir {
t.Errorf("Director = %q, want %q", ef.Director, c.wantDir)
}
})
}
}
// Битый parsed_context не валит разрешение (best-effort): используется только план.
func TestEffectiveFieldsBrokenParsedContext(t *testing.T) {
plan := recognize.Plan{Type: recognize.MediaMovie, Title: "Дюна", Year: 2024}
if got := EffectiveFields("{не json", plan).Label(); got != "Дюна (2024)" {
t.Errorf("Label() = %q, want «Дюна (2024)»", got)
}
}