diff --git a/internal/tgbot/bot_test.go b/internal/tgbot/bot_test.go index d57a737..8d5ab63 100644 --- a/internal/tgbot/bot_test.go +++ b/internal/tgbot/bot_test.go @@ -413,6 +413,39 @@ func TestBot_NotifyFailedEscapesExternalText(t *testing.T) { } } +// Карточка ревью: спецсимволы во внешнем пути раскладки и в причинах +// распознавания экранируются (esc после tailPath/join), сырая разметка не +// просачивается. Закрывает сценарий «Внешний путь и причины экранируются». +func TestReviewCard_EscapesPathAndReasons(t *testing.T) { + b, _, _, _ := newTestBot(t, []int64{7}) + rd := reviewData(store.StateReview) + rd.Recognition.Reasons = `["Tom & Jerry x"]` + rd.Preview[0].Dst = "/srv/media/series/A & B /file .mkv" + text, _ := b.renderCard(rd) + + if !strings.Contains(text, "Tom & Jerry <b>x</b>") { + t.Errorf("причины не экранированы:\n%s", text) + } + if !strings.Contains(text, "A & B <x>") || !strings.Contains(text, "file <y>.mkv") { + t.Errorf("путь раскладки не экранирован:\n%s", text) + } + if strings.Contains(text, "") || strings.Contains(text, "") { + t.Errorf("сырая разметка просочилась:\n%s", text) + } +} + +// opErr: при наличии download id он уходит моноширинным (tap-to-copy) под +// ключом download_id=; без id — дружелюбный текст без ключа. Закрывает сценарий +// «Id в сообщении об ошибке копируется». +func TestOpErr_IDMonospace(t *testing.T) { + if got := opErr("Не удалось выполнить действие", tid); !strings.Contains(got, "download_id="+idCode(tid)) { + t.Errorf("id не моноширинным под ключом download_id: %q", got) + } + if got := opErr("Готово", ""); strings.Contains(got, "download_id") { + t.Errorf("без id ключа download_id быть не должно: %q", got) + } +} + func TestBot_CallbackRetry(t *testing.T) { b, _, _, rev := newTestBot(t, []int64{7}) rev.data = reviewData(store.StateFailed)