Files
mesh-catalog/modules/mesh-delivery/cmd/mesh-delivery/recheck_test.go
T
jochen 74f912e1fb
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Put the merge check back to pending before a recheck asks it (hq issue 305)
A recheck asks of the head the forge already judged, and the forge keeps
the newest status of each context on it. Nobody reset mesh/merge-gate or
mesh/repo-check, so the old green stood and branch protection would merge
on it while the fresh check ran; rechecked heads did turn red.

mesh-delivery's recheck now asks the forge's holder to set the gate, and
the repository check when the head holds one, to pending ("checking
again: <why>") before it asks the controller; a forge that cannot be told
refuses the recheck whole. The gitea holder takes pending, and only
pending, on the merge check's statuses by hand: no verdict, so issue 293
still holds.
2026-10-08 10:11:26 +02:00

98 lines
3.6 KiB
Go

package main
import (
"strings"
"testing"
)
// A recheck puts the merge check's statuses back to pending before it asks the check: the forge keeps the
// newest status of each context on the head, and a recheck asks of that same head, so the old verdict would
// stand — and branch protection would let the pull request merge on it — until the fresh one lands.
// heldGreen is a head whose merge check passed, as the forge's holder set it from the verdict.
func heldGreen(t *testing.T, w *world, repo string, number int) string {
t.Helper()
w.h.PullUpdated(pr(repo, number, head, "feat/x", "src/a.go"))
owner, name, _ := strings.Cut(repo, "/")
_ = w.forge.Status(owner, name, head, ctxGate, "success", "pass: the gate said pass", "")
_ = w.forge.Status(owner, name, head, ctxRepo, "success", "pass: merge-check.sh passed", "")
w.forge.required = []string{ctxGate, ctxRepo}
w.h.Checked(verdict(repo, number, head, "pass"))
w.settleAll()
id := IDOf(repo, head)
if s := w.state(id); s != Ready {
t.Fatalf("a passing verdict left it %s", s)
}
return id
}
func TestARecheckPutsTheMergeCheckBackToPendingBeforeItAsks(t *testing.T) {
w := newWorld(t)
id := heldGreen(t, w, "novox/app", 7)
at := "novox/app@" + head + " "
var whenAsked map[string]string
w.ctl.onCheck = func() {
w.forge.mu.Lock()
defer w.forge.mu.Unlock()
whenAsked = map[string]string{ctxGate: w.forge.statuses[at+ctxGate], ctxRepo: w.forge.statuses[at+ctxRepo]}
}
if _, err := w.h.Recheck(id, "main moved on", "a person"); err != nil {
t.Fatal(err)
}
if len(w.ctl.checks) != 1 {
t.Fatalf("its check was asked %v", w.ctl.checks)
}
for _, c := range []string{ctxGate, ctxRepo} {
if !strings.HasPrefix(whenAsked[c], "pending checking again: main moved on") {
t.Fatalf("as its check was asked, the forge held %s %q: the old verdict still lets it merge", c, whenAsked[c])
}
}
checks, err := w.forge.Statuses("novox", "app", 0, head, "main")
if err != nil {
t.Fatal(err)
}
if checks.Merge.Mergeable == nil || *checks.Merge.Mergeable {
t.Fatalf("rechecked, the forge would still merge it: %+v", checks.Merge)
}
if s := w.state(id); s != Proposed {
t.Fatalf("rechecked, it is %s", s)
}
}
// Only the statuses the head holds are put back: a verdict that says no repository check would leave a
// pending one standing for ever.
func TestARecheckLeavesAStatusTheHeadNeverHadAlone(t *testing.T) {
w := newWorld(t)
w.h.PullUpdated(pr("novox/app", 8, head, "feat/y", "src/a.go"))
_ = w.forge.Status("novox", "app", head, ctxGate, "failure", "fail: the gate said fail", "")
w.h.Checked(verdict("novox/app", 8, head, "fail"))
w.settleAll()
id := IDOf("novox/app", head)
if _, err := w.h.Recheck(id, "flaky", "a person"); err != nil {
t.Fatal(err)
}
if s := w.forge.statuses["novox/app@"+head+" "+ctxGate]; !strings.HasPrefix(s, "pending checking again: flaky") {
t.Fatalf("the gate is %q", s)
}
if s, set := w.forge.statuses["novox/app@"+head+" "+ctxRepo]; set {
t.Fatalf("a repository check the head never had was set: %q", s)
}
}
// A forge that cannot be told refuses the recheck whole: nothing is asked on a stale verdict, and the
// delivery stays where it stood, for the person to ask again.
func TestARecheckTheForgeCannotTakeIsRefused(t *testing.T) {
w := newWorld(t)
id := heldGreen(t, w, "novox/app", 9)
w.forge.down = true
if _, err := w.h.Recheck(id, "main moved on", "a person"); err == nil {
t.Fatal("a recheck whose statuses could not be put back was taken")
}
if len(w.ctl.checks) != 0 {
t.Fatalf("its check was asked on a stale verdict: %v", w.ctl.checks)
}
if s := w.state(id); s != Ready {
t.Fatalf("refused, it moved to %s", s)
}
}