issue 1025: mesh-delivery announces each merge on the Approve as the event merged, with its warrant

This commit is contained in:
2026-10-17 15:40:00 +02:00
parent ce56b491cf
commit 4c0ed75090
4 changed files with 100 additions and 3 deletions
@@ -251,3 +251,73 @@ func TestAPushToAnUnmarkedGroupMemberEndsTheGroupsAskAndApprove(t *testing.T) {
t.Fatalf("merged on an Approve of heads the group no longer has: %v", w.forge.merges)
}
}
// mergedSaid is the module events `merged` the world has said.
func mergedSaid(w *world) []map[string]any {
w.mu.Lock()
defer w.mu.Unlock()
var out []map[string]any
for _, m := range w.said {
if m["event-kind"] == "merged" {
out = append(out, m)
}
}
return out
}
// Issue 1025: a merge on the operator's Approve is announced once, with the warrant's ask, option and heads.
func TestIssue1025AMergeOnTheApproveIsAnnouncedWithItsWarrant(t *testing.T) {
w := markedGreen(t)
id := IDOf("novox/app", readyHead)
open := w.op.open()
if len(open) != 1 {
t.Fatalf("asks %v", open)
}
if got := mergedSaid(w); len(got) != 0 {
t.Fatalf("announced before the merge: %v", got)
}
d := w.delivery(id)
wantHeads := map[string]string{}
for k, v := range d.Ready.Heads {
wantHeads[k] = v
}
w.op.answer(open[0], OptionApprove)
w.settleAll()
got := mergedSaid(w)
if len(got) != 1 {
t.Fatalf("announced %d times; wanted once: %v", len(got), got)
}
m := got[0]
if m["owner"] != "novox" || m["repo"] != "app" || m["number"] != float64(7) || m["head_sha"] != readyHead ||
m["ask_id"] != open[0] || m["option"] != OptionApprove {
t.Fatalf("the event does not name the pull request and its warrant: %v", m)
}
hs, _ := m["heads"].(map[string]any)
if len(hs) == 0 || len(hs) != len(wantHeads) {
t.Fatalf("heads %v; wanted %v", hs, wantHeads)
}
for k, v := range wantHeads {
if hs[k] != v {
t.Fatalf("heads %v; wanted %v", hs, wantHeads)
}
}
if g, ok := m["group"]; ok && g != "" {
t.Fatalf("a lone delivery names group %v", g)
}
w.later(2 * time.Minute)
w.settleAll()
if n := len(mergedSaid(w)); n != 1 {
t.Fatalf("announced %d times after more ticks", n)
}
}
// Issue 1025: a declined merge announces nothing.
func TestIssue1025ADeclinedMergeAnnouncesNothing(t *testing.T) {
w := markedGreen(t)
open := w.op.open()
w.op.answer(open[0], OptionDecline)
w.settleAll()
if got := mergedSaid(w); len(got) != 0 || len(w.forge.merges) != 0 {
t.Fatalf("declined, yet merges %v, announced %v", w.forge.merges, got)
}
}
@@ -54,7 +54,7 @@ func TestItClaimsTheDeliverySeatAndFollowsWhatItHandles(t *testing.T) {
if !reflect.DeepEqual(m.Consumes, followed) {
t.Fatalf("consumes %v, follows %v", m.Consumes, followed)
}
if !reflect.DeepEqual(m.Emits, []string{"transition", "group"}) {
if !reflect.DeepEqual(m.Emits, []string{"transition", "group", "merged"}) {
t.Fatalf("emits %v", m.Emits)
}
if !reflect.DeepEqual(m.State, []string{"deliveries", "groups", "asked", "pulls"}) {
@@ -343,7 +343,15 @@ func (h *Holder) askMerge(id string, p OpenPull, head string) func() {
return func() {
err := h.Forge.Merge(p.Owner, p.Repo, p.Number, head)
h.mu.Lock()
defer h.mu.Unlock()
var announce map[string]any // issue 1025: said after unlocking
defer func() {
h.mu.Unlock()
if announce != nil {
if e := h.Emit("merged", announce); e != nil {
h.Logf("[mesh-delivery] could not announce the merge of %s: %v", id, e)
}
}
}()
d := h.deliveries[id]
if d == nil || d.Ready == nil {
return
@@ -351,6 +359,7 @@ func (h *Holder) askMerge(id string, p OpenPull, head string) func() {
r, now := d.Ready, h.Now()
r.merging = false
if err == nil {
announce = mergedEvent(d, p, head)
r.Merged, r.Answer, r.RefusedSince = &now, "merged", nil
h.Logf("[mesh-delivery] %s merged on its ready mark at %s, its check passed against its trunk's head", id,
shortOf(head, 12))
@@ -467,3 +476,20 @@ func (h *Holder) readyLines(now time.Time) []StalledLine {
}
return out
}
// mergedEvent is the module event `merged` (issue 1025): the pull request merged, its head and group, and the ask,
// option and heads the operator's Approve was bound to (the warrant the controller reads back from the router). A merge
// without an Approve names no ask and no option: no warrant is invented. Under the lock, before anything clears them.
func mergedEvent(d *Delivery, p OpenPull, head string) map[string]any {
r := d.Ready
heads := map[string]string{}
for k, v := range r.Heads {
heads[k] = v
}
ev := map[string]any{"owner": p.Owner, "repo": p.Repo, "number": p.Number, "head_sha": head, "group": d.Group,
"ask_id": "", "option": "", "heads": heads}
if r.Approved != nil {
ev["ask_id"], ev["option"] = r.AskID, OptionApprove
}
return ev
}
+2 -1
View File
@@ -37,7 +37,8 @@
],
"emits": [
"transition",
"group"
"group",
"merged"
],
"invokes": [
"seat:mesh-controller.delivery-plan",