176 lines
8.0 KiB
Go
176 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// R6-C (ADR 0539 §17): the gate keeps no state after the merge, so an issue's fix is delivered when the delivery view says its
|
|
// source revision reached the machines (`mesh-delivery-view.delivered`: {revision, group, heads}), matched to the fix by the
|
|
// group it merged in or by the commit its merge landed as. The same revision twice is once.
|
|
|
|
func mergedFix(t *testing.T, h *harness, number int, group, mergedAs string) {
|
|
t.Helper()
|
|
body := map[string]any{"repository": "novox/mesh-catalog", "number": number, "to": "merged", "merged_as": mergedAs,
|
|
"at": time.Date(2026, 10, 24, 12, 0, 0, 0, time.UTC)}
|
|
if group != "" {
|
|
body["group"] = group
|
|
}
|
|
raw, _ := json.Marshal(body)
|
|
if err := h.OnTransition(stdio.Envelope{Key: "mesh-delivery-gate.transition", Body: raw}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func viewSays(t *testing.T, h *harness, revision int, group string, heads map[string]string) {
|
|
t.Helper()
|
|
raw, _ := json.Marshal(map[string]any{"revision": revision, "group": group, "heads": heads})
|
|
if err := h.OnDelivered(stdio.Envelope{Key: DeliveredEvent, Body: raw}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func fixIssue(t *testing.T, h *harness, pr string) uint64 {
|
|
t.Helper()
|
|
n := h.open(t, "a fix to deliver "+pr)
|
|
fixedAtFixing(t, h, n, pr)
|
|
return n
|
|
}
|
|
|
|
func TestAFixIsDeliveredWhenTheViewSaysTheGroupThatMovedTheSourceReachedTheMachines(t *testing.T) {
|
|
h := newHarness(t)
|
|
in := fixIssue(t, h, "mesh-catalog#20")
|
|
other := fixIssue(t, h, "mesh-catalog#21")
|
|
mergedFix(t, h, 20, "feat/r6", "aaaaaaaaaaaa1111")
|
|
mergedFix(t, h, 21, "feat/other", "bbbbbbbbbbbb2222")
|
|
if v, _ := h.View(in); v.Delivery != "fix merged, not delivered: mesh-catalog#20" {
|
|
t.Fatalf("before the view speaks: %q", v.Delivery)
|
|
}
|
|
viewSays(t, h, 7, "feat/r6", map[string]string{"novox/mesh-sdk": "cccccccccccc3333"})
|
|
if v, _ := h.View(in); v.Delivery != "delivered to every machine it should reach" {
|
|
t.Fatalf("the group's fix after the view spoke: %q", v.Delivery)
|
|
}
|
|
if v, _ := h.View(other); v.Delivery != "fix merged, not delivered: mesh-catalog#21" {
|
|
t.Fatalf("a fix of another group was delivered by a revision that is not its own: %q", v.Delivery)
|
|
}
|
|
if n := countEvents(h, EventReadyToVerify); n != 1 {
|
|
t.Fatalf("%d ready-to-verify, wanted one", n)
|
|
}
|
|
}
|
|
|
|
func TestAFixIsDeliveredWhenTheViewSaysTheHeadItsMergeLandedAs(t *testing.T) {
|
|
h := newHarness(t)
|
|
n := fixIssue(t, h, "mesh-catalog#22")
|
|
mergedFix(t, h, 22, "", "dddddddddddd4444eeee")
|
|
viewSays(t, h, 8, "some-other-group", map[string]string{"novox/mesh-sdk": "dddddddddddd4444eeee"})
|
|
if v, _ := h.View(n); v.Delivery != "fix merged, not delivered: mesh-catalog#22" {
|
|
t.Fatalf("a head of another repository delivered it: %q", v.Delivery)
|
|
}
|
|
viewSays(t, h, 9, "some-other-group", map[string]string{"novox/mesh-catalog": "dddddddddddd"})
|
|
if v, _ := h.View(n); v.Delivery != "delivered to every machine it should reach" {
|
|
t.Fatalf("the head its merge landed as: %q", v.Delivery)
|
|
}
|
|
}
|
|
|
|
func TestTheSameRevisionTwiceIsOnceAndTheGatesOwnDeliveredIsNoWord(t *testing.T) {
|
|
h := newHarness(t)
|
|
n := fixIssue(t, h, "mesh-catalog#23")
|
|
// The gate keeps no state after the merge: a transition to delivered, from an older gate, says nothing.
|
|
raw, _ := json.Marshal(map[string]any{"repository": "novox/mesh-catalog", "number": 23, "to": "delivered", "merged_as": "ffffffffffff5555"})
|
|
if err := h.OnTransition(stdio.Envelope{Key: "mesh-delivery-gate.transition", Body: raw}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v, _ := h.View(n); v.Delivery != "nothing heard from the delivery of mesh-catalog#23" {
|
|
t.Fatalf("the gate's delivered counted: %q", v.Delivery)
|
|
}
|
|
mergedFix(t, h, 23, "feat/x", "ffffffffffff5555")
|
|
for range 2 {
|
|
viewSays(t, h, 10, "feat/x", nil)
|
|
}
|
|
if is := get(t, h, n); len(is.Delivered) != 1 || countEvents(h, EventReadyToVerify) != 1 {
|
|
t.Fatalf("delivered %v, %d ready-to-verify", is.Delivered, countEvents(h, EventReadyToVerify))
|
|
}
|
|
}
|
|
|
|
// "A newer revision covers older ones": the view says each revision once and may skip one delivered together with a later one,
|
|
// so a merged fix whose merge is an ancestor of the head the event holds for its repository is delivered too, as the forge says.
|
|
func TestANewerRevisionCoversTheOlderMergesOfItsRepositoryAsTheForgeSaysAncestry(t *testing.T) {
|
|
h := newHarness(t)
|
|
older := fixIssue(t, h, "mesh-catalog#30")
|
|
newer := fixIssue(t, h, "mesh-catalog#31")
|
|
unrelated := fixIssue(t, h, "mesh-catalog#32")
|
|
mergedFix(t, h, 30, "feat/a", "1111111111110000")
|
|
mergedFix(t, h, 31, "feat/b", "2222222222220000")
|
|
mergedFix(t, h, 32, "feat/c", "3333333333330000")
|
|
var asked []string
|
|
h.Contains = func(owner, repo, commit, in string) (bool, error) {
|
|
asked = append(asked, owner+"/"+repo+" "+commit+" in "+in)
|
|
return commit == "1111111111110000", nil // only the first is an ancestor of the revision's head
|
|
}
|
|
viewSays(t, h, 12, "feat/b", map[string]string{"novox/mesh-catalog": "2222222222220000"})
|
|
for n, want := range map[uint64]string{older: "delivered to every machine it should reach",
|
|
newer: "delivered to every machine it should reach", unrelated: "fix merged, not delivered: mesh-catalog#32"} {
|
|
if v, _ := h.View(n); v.Delivery != want {
|
|
t.Errorf("issue %d reads %q, wanted %q (asked %v)", n, v.Delivery, want, asked)
|
|
}
|
|
}
|
|
// A forge that cannot say leaves the fix for the next event, and a repository the revision holds no head for is not asked.
|
|
h2 := newHarness(t)
|
|
n := fixIssue(t, h2, "mesh-catalog#33")
|
|
mergedFix(t, h2, 33, "feat/d", "4444444444440000")
|
|
h2.Contains = func(string, string, string, string) (bool, error) { return false, errStuck }
|
|
viewSays(t, h2, 13, "feat/z", map[string]string{"novox/mesh-catalog": "5555555555550000"})
|
|
if v, _ := h2.View(n); v.Delivery != "fix merged, not delivered: mesh-catalog#33" {
|
|
t.Fatalf("a forge that could not say delivered it: %q", v.Delivery)
|
|
}
|
|
viewSays(t, h2, 14, "feat/z", map[string]string{"novox/mesh-sdk": "5555555555550000"})
|
|
if v, _ := h2.View(n); v.Delivery != "fix merged, not delivered: mesh-catalog#33" {
|
|
t.Fatalf("another repository's head covered it: %q", v.Delivery)
|
|
}
|
|
}
|
|
|
|
// A fix kept with only its merged flag (heard through `checks`, or kept before release 6) is matched to a delivered revision
|
|
// by asking the delivery for its merge: the group and the commit it landed as.
|
|
func TestAFixKeptWithoutItsMergeIsMatchedByAskingTheDeliveryForIt(t *testing.T) {
|
|
h := newHarness(t)
|
|
n := fixIssue(t, h, "mesh-catalog#40")
|
|
// Kept as a fix the tracker heard merged and nothing more: no group, no commit, no repository.
|
|
if _, err := h.change(n, Act{Op: "delivery", By: Seat}, func(i *Issue) error {
|
|
at := time.Date(2026, 10, 24, 12, 0, 0, 0, time.UTC)
|
|
i.Fixes = append(i.Fixes, Fix{PullRequest: "mesh-catalog#40", State: "published", Merged: true, MergedAt: &at, Since: at})
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
asked := 0
|
|
h.Deliveries = func(repository, number string) (Delivery, error) {
|
|
asked++
|
|
return Delivery{State: "merged", Merged: true, Repository: "novox/mesh-catalog", Group: "feat/r6", MergedAs: "9999999999990000"}, nil
|
|
}
|
|
viewSays(t, h, 20, "feat/r6", nil)
|
|
if asked == 0 {
|
|
t.Fatal("the delivery was not asked for the merge of a fix kept without it")
|
|
}
|
|
if v, _ := h.View(n); v.Delivery != "delivered to every machine it should reach" {
|
|
t.Fatalf("%q", v.Delivery)
|
|
}
|
|
// What `checks` says is carried: group, commit and repository reach the fix when it is heard through the delivery.
|
|
h2 := newHarness(t)
|
|
m := h2.open(t, "x")
|
|
h2.Deliveries = func(string, string) (Delivery, error) {
|
|
return Delivery{State: "merged", Merged: true, Since: time.Date(2026, 10, 24, 12, 0, 0, 0, time.UTC), Repository: "novox/mesh-catalog",
|
|
Group: "feat/q", MergedAs: "8888888888880000"}, nil
|
|
}
|
|
fixedAtFixing(t, h2, m, "mesh-catalog#41")
|
|
if err := h2.OnMoved(stdio.Envelope{Body: []byte(`{"number":` + itoa(int(m)) + `}`)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f := fixOf(get(t, h2, m), "mesh-catalog#41")
|
|
if f == nil || f.Group != "feat/q" || f.MergedAs != "8888888888880000" || f.Repository != "novox/mesh-catalog" {
|
|
t.Fatalf("heard through checks the fix keeps %+v", f)
|
|
}
|
|
}
|