layout: непомещающееся целевое имя уводит задачу в review вместо failed

- предел длины компонента (255 байт) проверяется в BuildLinks до первой
  операции с ФС: ни каталога, ни ссылки при отказе не создаётся
- причина пустого предпросмотра считается на показе (ReviewData.PreviewError)
  и печатается в панели действий и в карточке Telegram: у задачи без
  записанной причины взять её больше неоткуда
This commit is contained in:
av
2026-08-10 12:16:44 +03:00
parent 1710e5a9d5
commit b9f0929d0c
31 changed files with 1511 additions and 17 deletions
+55
View File
@@ -352,3 +352,58 @@ func TestRetryListShowsProgress(t *testing.T) {
t.Errorf("карточка downloading без прогресс-поллера: %s", rr.Body.String())
}
}
// TestActionBarNamesReasonWithoutPreview: при пустом предпросмотре панель
// действий печатает записанную причину, а не общее «Подтверди источник» —
// иначе экран советует подтвердить уже подтверждённое и молчит о настоящей
// причине (см. spec review, «Панель действий при пустом предпросмотре»).
func TestActionBarNamesReasonWithoutPreview(t *testing.T) {
t.Run("причина показа старше записанной", func(t *testing.T) {
rd := reviewDataWithSource(false)
rd.PreviewError = `layout: имя не помещается: "Очень длинное" — 400 байт при пределе 255`
rd.Download.ErrorMsg = store.NullString("устаревшая причина прошлого перехода")
rv := actionReviewer{stubReviewer: stubReviewer{data: rd}}
h := testRouterAction(t, stubReader{one: &rd.Download}, rv, stubCommander{}, stubLive{})
rr := post(t, h, "/ui/downloads/"+testULID+"/nobase", nil, true)
if rr.Code != http.StatusOK {
t.Fatalf("nobase (htmx) = %d, want 200", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "не помещается") {
t.Errorf("панель не назвала причину: %s", body)
}
if strings.Contains(body, "Подтверди источник") {
t.Errorf("панель печатает общий текст вместо причины: %s", body)
}
if strings.Contains(body, "устаревшая") {
t.Errorf("записанная причина перебила посчитанную на показе: %s", body)
}
})
t.Run("записанная причина, когда посчитанной нет", func(t *testing.T) {
rd := reviewDataWithSource(false)
rd.Download.ErrorMsg = store.NullString("целевой файл уже существует")
rv := actionReviewer{stubReviewer: stubReviewer{data: rd}}
h := testRouterAction(t, stubReader{one: &rd.Download}, rv, stubCommander{}, stubLive{})
rr := post(t, h, "/ui/downloads/"+testULID+"/nobase", nil, true)
if !strings.Contains(rr.Body.String(), "уже существует") {
t.Errorf("панель не назвала записанную причину: %s", rr.Body.String())
}
})
t.Run("причины нет → прежний общий текст", func(t *testing.T) {
rd := reviewDataWithSource(false)
rv := actionReviewer{stubReviewer: stubReviewer{data: rd}}
h := testRouterAction(t, stubReader{one: &rd.Download}, rv, stubCommander{}, stubLive{})
rr := post(t, h, "/ui/downloads/"+testULID+"/nobase", nil, true)
if rr.Code != http.StatusOK {
t.Fatalf("nobase (htmx) = %d, want 200", rr.Code)
}
if !strings.Contains(rr.Body.String(), "Подтверди источник") {
t.Errorf("без причины ожидался общий текст: %s", rr.Body.String())
}
})
}