Files
mesh-catalog/modules/mesh-issues/cmd/mesh-issues/delivered_test.go
T

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)
}
}