Files
mesh-catalog/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/pullkey_test.go
T
jschoubben 454fb8a918
mesh/delivery covered after its failure: a later walk that contains it was delivered: plan-1792777718506798022 (novox/mesh-catalog at e8300455 into main,…
mesh/delivery-group group feat/r4g merged: every member is merged
mesh/check checking 454fb8a918ad on 3bec357968da | novox/mesh-catalog#464 in group feat/r4g
Write a pull request's check verdict into its key after the key is created: the status event can be taken first (R4g, issue 1887)
2026-10-23 19:42:54 +02:00

166 lines
5.9 KiB
Go

package main
import (
"errors"
"sync"
"testing"
)
// **The pull request's key** (novox/hq ADR 0406 §4, issue 975): one key per pull request on the bus, holding its head,
// the trunk commit its current check is asked against and its verdict, written by compare-and-set. A head the forge
// announces writes head and trunk; a verdict for that head and trunk writes the verdict; nothing else writes.
type fakePulls struct {
mu sync.Mutex
keys map[string]PullKey
writes int
// lose makes the first n writes lose to another writer, which sets rival.
lose int
rival PullKey
}
func newFakePulls() *fakePulls { return &fakePulls{keys: map[string]PullKey{}} }
func (f *fakePulls) Change(key string, apply func(PullKey) (PullKey, bool)) error {
f.mu.Lock()
defer f.mu.Unlock()
for tries := 0; tries < 5; tries++ {
next, write := apply(f.keys[key])
if !write {
return nil
}
if f.lose > 0 { // another writer was first: read again and apply again
f.lose--
f.keys[key] = f.rival
continue
}
f.keys[key] = next
f.writes++
return nil
}
return errors.New("lost the race too often")
}
func (f *fakePulls) get(key string) PullKey {
f.mu.Lock()
defer f.mu.Unlock()
return f.keys[key]
}
const appKey = "novox.app.7"
func announcedAt(head, trunk string) PullEvent {
p := pr("novox/app", 7, head, "feat/x", "src/a.go")
p.BaseSHA = trunk
return p
}
func TestAnAnnouncedHeadWritesTheKeyAndTheSameAnnouncementAgainWritesNothing(t *testing.T) {
w := newWorld(t)
pulls := newFakePulls()
w.h.Pulls = pulls
p := announcedAt(readyHead, "t1t1t1t1t1t1")
w.h.PullUpdated(p)
w.h.KeepPull(p)
if got := pulls.get(appKey); got.Head != readyHead || got.Trunk != "t1t1t1t1t1t1" || got.Verdict != "" {
t.Fatalf("the key holds %+v", got)
}
w.h.KeepPull(p)
if pulls.writes != 1 {
t.Fatalf("the same announcement wrote %d times", pulls.writes)
}
}
func TestANewTrunkCommitClearsTheVerdictAndANewHeadReplacesTheKey(t *testing.T) {
w := newWorld(t)
pulls := newFakePulls()
w.h.Pulls = pulls
w.h.KeepPull(announcedAt(readyHead, "t1t1t1t1t1t1"))
w.h.KeepPull(announcedAt(readyHead, "t2t2t2t2t2t2"))
if got := pulls.get(appKey); got.Trunk != "t2t2t2t2t2t2" || got.Verdict != "" {
t.Fatalf("the check asked again against a new trunk left %+v", got)
}
w.h.KeepPull(announcedAt(pushed, "t2t2t2t2t2t2"))
if got := pulls.get(appKey); got.Head != pushed {
t.Fatalf("a new head left %+v", got)
}
}
func TestAMeshCheckForTheKeysHeadAndTrunkIsWrittenAndOneForAnotherIsNot(t *testing.T) {
w := newWorld(t)
pulls := newFakePulls()
w.h.Pulls = pulls
w.h.PullUpdated(announcedAt(readyHead, "a1a1a1a1a1a1"))
w.h.KeepPull(announcedAt(readyHead, "a1a1a1a1a1a1"))
status := func(head, trunk, state string) ForgeStatusEvent {
return ForgeStatusEvent{Owner: "novox", Repo: "app", SHA: head, Context: ctxCheck, State: state,
Description: "pass " + head[:12] + " on " + trunk + " | unit 3 run"}
}
w.h.KeepCheckVerdict(status(pushed, "a1a1a1a1a1a1", "success"))
if pulls.get(appKey).Verdict != "" {
t.Fatal("a verdict for another head was written")
}
w.h.KeepCheckVerdict(status(readyHead, "b2b2b2b2b2b2", "success"))
if pulls.get(appKey).Verdict != "" {
t.Fatal("a verdict for another trunk commit was written")
}
w.h.KeepCheckVerdict(status(readyHead, "a1a1a1a1a1a1", "pending"))
if pulls.get(appKey).Verdict != "" {
t.Fatal("a pending check was written as a verdict")
}
w.h.KeepCheckVerdict(status(readyHead, "a1a1a1a1a1a1", "success"))
if got := pulls.get(appKey); got.Verdict != "pass" {
t.Fatalf("the key holds %+v", got)
}
}
func TestALostRaceReadsTheKeyAgainAndAppliesTheChangeToWhatItFound(t *testing.T) {
w := newWorld(t)
pulls := newFakePulls()
pulls.lose, pulls.rival = 1, PullKey{Head: readyHead, Trunk: "t1t1t1t1t1t1", Verdict: "pass"}
w.h.Pulls = pulls
// The rival writer already holds this very head and trunk with a verdict: the change finds nothing to write.
w.h.KeepPull(announcedAt(readyHead, "t1t1t1t1t1t1"))
if got := pulls.get(appKey); got.Verdict != "pass" || pulls.writes != 0 {
t.Fatalf("after the lost race the key holds %+v after %d writes", got, pulls.writes)
}
}
// The forge's catch-up after a restart can announce a merge again past the bus's de-duplication window (issue 975):
// heard again, it changes nothing, and moves no trunk back.
func TestTheSameMergeHeardAgainAfterALaterMergeMovesNoTrunkBack(t *testing.T) {
w := newWorld(t)
a, b := "aaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb"
w.h.PullUpdated(pr("novox/app", 7, readyHead, "feat/x", "src/a.go"))
w.h.PullUpdated(pr("novox/app", 8, a, "feat/y", "src/a.go"))
w.h.PullUpdated(pr("novox/app", 9, b, "feat/z", "src/a.go"))
w.h.Checked(judgedAt("novox/app", 7, readyHead, "pass", "m8m8m8m8m8m8"))
w.h.PullMerged(merged("novox/app", 8, a, "m8m8m8m8m8m8"))
w.h.PullMerged(merged("novox/app", 9, b, "m9m9m9m9m9m9"))
id := IDOf("novox/app", readyHead)
before := w.delivery(id)
w.h.PullMerged(merged("novox/app", 8, a, "m8m8m8m8m8m8")) // announced again, past the window
after := w.delivery(id)
if after.TrunkNow != before.TrunkNow || after.TrunkNow != "m9m9m9m9m9m9" {
t.Fatalf("the trunk moved from %q to %q on a merge heard again; wanted it to stay m9m9m9m9m9m9", before.TrunkNow, after.TrunkNow)
}
}
// The forge's mesh/check event handled wholly between the announcement's PullUpdated and its KeepPull (the two events are
// taken on goroutines of their own): the verdict must still reach the pull request's key.
func TestAVerdictHeardBeforeTheKeyIsWrittenReachesTheKey(t *testing.T) {
w := newWorld(t)
pulls := newFakePulls()
w.h.Pulls = pulls
p := announcedAt(readyHead, "a1a1a1a1a1a1")
w.h.PullUpdated(p)
s := ForgeStatusEvent{Owner: "novox", Repo: "app", SHA: readyHead, Context: ctxCheck, State: "success",
Description: "pass " + readyHead[:12] + " on a1a1a1a1a1a1 | unit 3 run"}
w.h.CommitStatus(s)
w.h.KeepCheckVerdict(s)
w.h.KeepPull(p)
if got := pulls.get(appKey); got.Head != readyHead || got.Verdict != "pass" {
t.Fatalf("the key holds %+v, want the head with the verdict pass", got)
}
}