v0.260.0 — a box ahead of the catalog reads „Naprakész", and the pin never moves backwards (R-524)
gates / gates (push) Successful in 24s

MEASURED 2026-09-15 (BIGNIGHT Phase 6): privatebin updated 2.0.5 -> 2.0.6, catalog
reverted to 2.0.5, and the box read „Frissítés elérhető — ma" over an Update that
would have moved the pin BACKWARDS onto a possibly-migrated datadir.

- stacks.CatalogOrder: the comparison gains a fourth answer (Ahead) and moves out of
  web, so the badge and UpdatePreflight cannot drift apart.
- The badge: ahead reads „Naprakész"/"Up to date", tag-ok, with a title saying why.
- The refusal: UpdatePreflight returns `downgrade` (409), born as a bundle key; the
  API now renders update refusals through errText so it reaches English households.
- Ahead is narrow: every differing service must be orderable AND newer, else Behind.
- Ordering is util.Version.Compare behind a tag normaliser — no second comparator.
- Three red-proofs, each seen to fail.

R-589 was already fixed in v0.258.0; only its register row was stale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-21 12:48:55 +02:00
parent 19ef0329ab
commit 8f8a64cad7
14 changed files with 597 additions and 42 deletions
+21 -1
View File
@@ -617,6 +617,10 @@ func TestUpdateBadgeFollowsTheLanguage(t *testing.T) {
}
current := behind
current.CatalogImages = map[string]string{"a": "old"}
// R-524: the box runs something the catalog has moved BACK from.
ahead := behind
ahead.AppConfig = &stacks.AppConfig{InstalledImages: map[string]stacks.InstalledImage{"a": {Ref: "privatebin/pdo:2.0.6"}}}
ahead.CatalogImages = map[string]string{"a": "privatebin/pdo:2.0.5"}
unknown := stacks.Stack{Name: "app", Deployed: false}
huFn := s.templateFuncMap()["updateBadge"].(func(stacks.Stack) *MetaBadge)
@@ -654,7 +658,23 @@ func TestUpdateBadgeFollowsTheLanguage(t *testing.T) {
if !strings.HasPrefix(enBehind.Label, "Update available") || !strings.Contains(enBehind.Label, "3 days ago") {
t.Errorf("en behind label = %q, want the English age suffix", enBehind.Label)
}
for _, b := range []*MetaBadge{enCur, enBehind} {
// 4. R-524's ahead arm: the SAME word and the SAME class as current in both languages, with a
// title that differs from current's — so the household is told why, and never warned.
huAhead, enAhead := huFn(ahead), enFn(ahead)
if huAhead == nil || enAhead == nil {
t.Fatal("an app ahead of the catalog must carry a badge")
}
if huAhead.Label != huFn(current).Label || enAhead.Label != enCur.Label {
t.Errorf("ahead must wear the same word as current; hu %q / en %q", huAhead.Label, enAhead.Label)
}
if huAhead.Class != "tag-ok" || enAhead.Class != "tag-ok" {
t.Errorf("ahead must not be a warning; hu %q / en %q", huAhead.Class, enAhead.Class)
}
if huAhead.Title == huFn(current).Title || enAhead.Title == enCur.Title {
t.Error("ahead must say WHY in its title, not reuse current's sentence")
}
for _, b := range []*MetaBadge{enCur, enBehind, enAhead} {
if strings.ContainsAny(b.Label+b.Title, "áéíóöőúüűÁÉÍÓÖŐÚÜŰ") {
t.Errorf("an English badge still carries Hungarian: %q / %q", b.Label, b.Title)
}