web-ui: карточка и страница обновляются, пока задачу может двигать фон

- условие самообновления — доменный предикат store.State.IsObservable() вместо
  фазы catched; один поллер на поверхность, интервалы 5 с и 15 с
- отказ тика отвечает 200 и самозавершающимся фрагментом с корневым id цели
  вместо 404/500, который htmx не свопит
- заведён ADR-2026-08-10-observability-is-not-terminality, переписан раздел
  «Живой поллинг» в конвенции веб-UI
This commit is contained in:
av
2026-08-10 14:02:38 +03:00
parent 969926fae3
commit a5d873b62d
29 changed files with 1575 additions and 99 deletions
+21 -3
View File
@@ -15,9 +15,11 @@ import (
// stubReader — минимальный Reader для проверки рендера списка.
type stubReader struct {
list []store.Download
one *store.Download
sizes map[string]int64 // размеры разложенных файлов по download_id (фолбэк)
list []store.Download
one *store.Download
sizes map[string]int64 // размеры разложенных файлов по download_id (фолбэк)
getErr error // отказ чтения задачи (не ErrNotFound)
sizesErr error // отказ чтения размеров раскладки
}
func (s stubReader) ListDownloads(context.Context) ([]store.Download, error) { return s.list, nil }
@@ -25,12 +27,18 @@ func (s stubReader) ListDownloadsPage(context.Context, store.ListFilter) ([]stor
return s.list, len(s.list), nil
}
func (s stubReader) GetDownload(context.Context, string) (*store.Download, error) {
if s.getErr != nil {
return nil, s.getErr
}
if s.one == nil {
return nil, store.ErrNotFound
}
return s.one, nil
}
func (s stubReader) LayoutSizeByDownload(context.Context, []string) (map[string]int64, error) {
if s.sizesErr != nil {
return nil, s.sizesErr
}
return s.sizes, nil
}
@@ -94,6 +102,16 @@ func get(t *testing.T, h http.Handler, path string) *httptest.ResponseRecorder {
return rr
}
// getHTMX — тот же GET, но помеченный как htmx-запрос (тик самообновления).
func getHTMX(t *testing.T, h http.Handler, path string) *httptest.ResponseRecorder {
t.Helper()
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, path, nil)
req.Header.Set("HX-Request", "true")
h.ServeHTTP(rr, req)
return rr
}
// testULID — валидный lowercase-ULID для маршрутов (pathID валидирует формат).
const testULID = "01arz3ndektsv4rrffq69g5fav"