issue 1025: the merged announcement is an owed effect, and a group's merge is tested

This commit is contained in:
2026-10-17 15:41:43 +02:00
parent 4c0ed75090
commit 3ec2881a27
4 changed files with 65 additions and 10 deletions
@@ -321,3 +321,60 @@ func TestIssue1025ADeclinedMergeAnnouncesNothing(t *testing.T) {
t.Fatalf("declined, yet merges %v, announced %v", w.forge.merges, got)
}
}
// Issue 1025: a merge of a delivery in a group carries the group id and the heads the group's Approve was bound to.
func TestIssue1025AMergeInAGroupCarriesTheGroupAndItsHeads(t *testing.T) {
w := newWorld(t)
w.op.auto = false
const ctlHead, catHead = "c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1", "d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2"
ctl, cat := IDOf("novox/mesh-controller", ctlHead), IDOf("novox/mesh-catalog", catHead)
w.ctl.order = func(ms []Member) Order { return Order{Order: []string{ctl, cat}} }
w.h.PullUpdated(pr("novox/mesh-controller", 1, ctlHead, "feat/x", "internal/a.go"))
w.h.PullUpdated(pr("novox/mesh-catalog", 2, catHead, "feat/x", "modules/app/module.json"))
w.h.Checked(judgedAt("novox/mesh-controller", 1, ctlHead, "pass", "t1t1t1t1t1t1"))
w.h.Checked(judgedAt("novox/mesh-catalog", 2, catHead, "pass", "t3t3t3t3t3t3"))
w.forge.open = []OpenPull{markedPull("novox/mesh-controller", 1, ctlHead, ctlHead),
markedPull("novox/mesh-catalog", 2, catHead, catHead)}
w.h.SeeOpenPulls()
w.settleAll()
w.h.Checked(CheckedEvent{Group: "feat/x", ID: "check-1", Verdict: "pass", Summary: "both compose"})
w.settleAll()
open := w.op.open()
if len(open) != 1 {
t.Fatalf("asks %v", open)
}
w.op.answer(open[0], OptionApprove)
w.settleAll()
got := mergedSaid(w)
if len(got) != 1 {
t.Fatalf("announced %d; wanted the first member's merge once: %v", len(got), got)
}
m := got[0]
hs, _ := m["heads"].(map[string]any)
if m["group"] != "feat/x" || m["ask_id"] != open[0] || m["option"] != OptionApprove || m["repo"] != "mesh-controller" ||
hs[ctl] != ctlHead || hs[cat] != catHead || len(hs) != 2 {
t.Fatalf("the group's merge event: %v", m)
}
}
// Issue 1025: the announcement is owed and kept, so it is said after a restart, and once.
func TestIssue1025AnOwedMergedIsSaidAfterARestartAndOnce(t *testing.T) {
w := newWorld(t)
p := pr("novox/app", 7, readyHead, "feat/x", "src/a.go")
w.h.PullUpdated(p)
id := IDOf("novox/app", readyHead)
w.h.mu.Lock()
d := w.h.deliveries[id]
d.Owed = append(d.Owed, Effect{Kind: EffectEmit, Event: "merged", Line: `{"owner":"novox","repo":"app","number":7,"head_sha":"` + readyHead + `"}`, Since: w.now})
if err := w.store.PutDelivery(d); err != nil {
t.Fatal(err)
}
w.h.mu.Unlock()
again := newWorldOver(t, w.store, w.ctl, w.forge, w.now.Add(time.Minute))
again.settleAll()
again.later(time.Hour)
again.settleAll()
if n := len(mergedSaid(again)); n != 1 {
t.Fatalf("said %d times after the restart; wanted once", n)
}
}
@@ -428,6 +428,9 @@ func (h *Holder) do(e Effect, repo [2]string, number int, body, url string, grou
if group {
event = "group"
}
if e.Event != "" {
event = e.Event
}
return h.Emit(event, payload)
case EffectNote:
if h.Forge == nil {
@@ -282,6 +282,7 @@ type Step struct {
// on the pull request refreshed, a status set.
type Effect struct {
Kind string `json:"kind"`
Event string `json:"event,omitempty"` // an emit's module event, when not transition or group (issue 1025)
Commit string `json:"commit,omitempty"`
Line string `json:"line,omitempty"`
Context string `json:"context,omitempty"`
@@ -343,15 +343,7 @@ 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()
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)
}
}
}()
defer h.mu.Unlock()
d := h.deliveries[id]
if d == nil || d.Ready == nil {
return
@@ -359,7 +351,9 @@ 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)
// Issue 1025: the announcement is owed, kept with the delivery, so a death right after the merge
// still says it once.
d.Owed = append(d.Owed, Effect{Kind: EffectEmit, Event: "merged", Line: string(mustJSON(mergedEvent(d, p, head))), Since: now})
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))