mesh/merge-gate pass: builds mesh-delivery, mesh-issues → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery-group group fix/403-fix-time ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request
hq ADR 0313: the operator's five minutes start when an agent moves the issue to fixing, not at the merge. mesh-delivery says on each transition, and in checks, when the pull request opened, when it merged and the class of what it moves. mesh-issues keeps the first move to fixing, the fix time with its four phases once the last fix is delivered, serves fix-times, and says issue-tracker.fix.<class>.over-budget on the operator's channel, cleared by the next fix of that class within budget.
96 lines
3.6 KiB
Go
96 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// novox/hq issue 403, ADR 0313: the issue tracker measures a fix's time from the move to fixing to delivered and
|
|
// splits it at the pull request's opening and its merge. A transition says what it reads them by: when the pull
|
|
// request was first heard (its first head, not a later one), when it merged, and the class of what it moves —
|
|
// core, leaf, or none for a change that moves no module. `checks` says the same for a fix the tracker hears of late.
|
|
|
|
// transitionsTo are the transition events said for a delivery that moved to a state.
|
|
func transitionsTo(w *world, id string, to State) []map[string]any {
|
|
var out []map[string]any
|
|
for _, e := range w.said {
|
|
if e["event-kind"] == "transition" && e["id"] == id && e["to"] == string(to) {
|
|
out = append(out, e)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestATransitionSaysWhenItsPullRequestOpenedMergedAndItsClass(t *testing.T) {
|
|
w := newWorld(t)
|
|
repo := "novox/mesh-catalog"
|
|
opened := w.now
|
|
first := pr(repo, 9, "head9first", "fix/403-x", "modules/app/a.go")
|
|
w.h.PullUpdated(first)
|
|
w.later(3 * time.Minute)
|
|
second := pr(repo, 9, "head9second", "fix/403-x", "modules/app/a.go")
|
|
w.h.PullUpdated(second)
|
|
c := verdictMoving(repo, 9, "head9second", "app")
|
|
c.Plan.Class = "core"
|
|
w.h.Checked(c)
|
|
w.later(2 * time.Minute)
|
|
mergedWhen := w.now
|
|
w.h.PullMerged(mergedAt(second, "merge9aaaaaa", mergedWhen))
|
|
walk := aWalk("plan-merge9", repo, "merge9aaaaaa", false)
|
|
walk.Delivery = &WalkDelivery{Merges: []WalkMerge{{Repository: repo, Commit: "merge9aaaaaa"}}}
|
|
w.h.WalkMoved(w.ctl.put(walk))
|
|
w.settleAll()
|
|
walk = w.ctl.walk(walk.ID)
|
|
walk.State, walk.Revision = walkDone, walk.Revision+1
|
|
walk.Modules = map[string]*WalkModule{"app": {State: "built"}}
|
|
w.h.WalkMoved(w.ctl.put(walk))
|
|
w.settleAll()
|
|
|
|
id := IDOf(repo, "head9second")
|
|
if s := w.state(id); s != Delivered {
|
|
t.Fatalf("the merge is %s, not delivered", s)
|
|
}
|
|
said := transitionsTo(w, id, Delivered)
|
|
if len(said) == 0 {
|
|
t.Fatalf("no transition to delivered was said: %v", w.said)
|
|
}
|
|
e := said[len(said)-1]
|
|
if e["opened"] != opened.UTC().Format(time.RFC3339) {
|
|
t.Errorf("opened %v, want the first head's %s (not the second head's)", e["opened"], opened.Format(time.RFC3339))
|
|
}
|
|
if e["merged_at"] != mergedWhen.UTC().Format(time.RFC3339) {
|
|
t.Errorf("merged_at %v, want %s", e["merged_at"], mergedWhen.Format(time.RFC3339))
|
|
}
|
|
if e["class"] != "core" {
|
|
t.Errorf("class %v, want the plan's core", e["class"])
|
|
}
|
|
|
|
a, err := w.h.Checks(repo, 9, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if a.Delivery == nil || a.Delivery.Opened != opened.UTC().Format(time.RFC3339) || a.Delivery.Class != "core" ||
|
|
a.Delivery.MergedAt != mergedWhen.UTC().Format(time.RFC3339) {
|
|
t.Errorf("checks says %+v", a.Delivery)
|
|
}
|
|
}
|
|
|
|
// A plan that moves no module is class none, so the tracker gives its fix no budget; a plan from a controller that
|
|
// says no class falls back to its walk's class once known, and says nothing before.
|
|
func TestAFixClassIsNoneWhenItMovesNothing(t *testing.T) {
|
|
for _, c := range []struct {
|
|
why string
|
|
d Delivery
|
|
want string
|
|
}{
|
|
{"the planner's class", Delivery{Plan: &DeliveryPlan{Moved: []string{"app"}, Class: "leaf"}}, "leaf"},
|
|
{"a plan that moves nothing, no class said", Delivery{Plan: planMoving("novox/x")}, "none"},
|
|
{"no class said, its walk's known", Delivery{Plan: planMoving("novox/x", "app"), Time: &DeliveryTime{Class: "core"}}, "core"},
|
|
{"no class said, nothing known", Delivery{Plan: planMoving("novox/x", "app")}, ""},
|
|
} {
|
|
if got := fixClass(&c.d); got != c.want {
|
|
t.Errorf("%s: %q, want %q", c.why, got, c.want)
|
|
}
|
|
}
|
|
}
|