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.
98 lines
3.6 KiB
Go
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)
|
|
}
|
|
}
|