A merge planned without looking at open plans, so two plans worked the same modules and a stuck plan stayed open for ever. The newer plan folds in what older plans of the same repository and branch had not built or sent, and closes them as superseded. A person can close a stuck plan by id with `plans close <id>`.
96 lines
4.3 KiB
Go
96 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// novox/hq issue 254, ADR 0218: a newer plan takes over what the older open plans of its repository
|
|
// and branch had not built, and closes them as superseded; another repository's plan, another
|
|
// branch's, and a plan made after it are left alone.
|
|
func TestANewerPlanSupersedesTheOlderOpenPlansOfItsRepository(t *testing.T) {
|
|
at := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC)
|
|
sent := at.Add(time.Minute)
|
|
plan := func(id, repository, branch string, created time.Time, modules map[string]*inventory.PlanModule) inventory.Plan {
|
|
return inventory.Plan{ID: id, Repository: repository, Branch: branch, Commit: id + "-commit",
|
|
Created: created, State: inventory.PlanRolling, Modules: modules}
|
|
}
|
|
older := plan("plan-1", "novox/mesh-catalog", "main", at, map[string]*inventory.PlanModule{
|
|
"gitea": {State: "built", SentAt: &sent}, // done with: stays done
|
|
"keycloak": {State: "asked"}, // asked, not answered: folded
|
|
"plex": {}, // not yet asked: folded
|
|
"agent": {State: "built"}, // built, rolls out, not sent: folded
|
|
"notes": {State: "built"}, // built, records: nothing to send
|
|
})
|
|
stuck := plan("plan-0", "Novox/Mesh-Catalog", "", at.Add(-time.Hour), map[string]*inventory.PlanModule{
|
|
"runtime": {State: "asked"},
|
|
})
|
|
other := plan("plan-2", "novox/mesh-controller", "main", at, map[string]*inventory.PlanModule{"mesh-controller": {}})
|
|
release := plan("plan-3", "novox/mesh-catalog", "release", at, map[string]*inventory.PlanModule{"lemurs": {}})
|
|
later := plan("plan-5", "novox/mesh-catalog", "main", at.Add(2*time.Hour), map[string]*inventory.PlanModule{"later": {}})
|
|
done := plan("plan-6", "novox/mesh-catalog", "main", at, map[string]*inventory.PlanModule{"finished": {}})
|
|
done.State = inventory.PlanDone
|
|
|
|
newer := plan("plan-4", "novox/mesh-catalog", "main", at.Add(time.Hour), nil)
|
|
newer.Commit = "97b1b2b0c0ffee"
|
|
rollsOut := func(m string) bool { return m != "notes" }
|
|
folded, closed := supersededBy(newer, []inventory.Plan{stuck, older, other, release, later, done, newer}, rollsOut)
|
|
|
|
if want := []string{"agent", "keycloak", "plex", "runtime"}; !reflect.DeepEqual(folded, want) {
|
|
t.Fatalf("folded %v, wanted %v", folded, want)
|
|
}
|
|
var ids []string
|
|
for _, p := range closed {
|
|
ids = append(ids, p.ID)
|
|
if p.State != inventory.PlanSuperseded || p.Open() {
|
|
t.Errorf("%s was left %s", p.ID, p.State)
|
|
}
|
|
if !strings.Contains(p.Note, "plan-4") || !strings.Contains(p.Note, "97b1b2b0") {
|
|
t.Errorf("%s does not name the plan that superseded it: %q", p.ID, p.Note)
|
|
}
|
|
}
|
|
if want := []string{"plan-0", "plan-1"}; !reflect.DeepEqual(ids, want) {
|
|
t.Fatalf("superseded %v, wanted %v — another repository, another branch, a later plan and a "+
|
|
"finished one are left alone", ids, want)
|
|
}
|
|
if other.State != inventory.PlanRolling {
|
|
t.Fatal("the plan handed in was changed in place")
|
|
}
|
|
if line := planLine(closed[1], time.Now()); !strings.Contains(line, "superseded") {
|
|
t.Fatalf("a superseded plan reads %q", line)
|
|
}
|
|
}
|
|
|
|
// novox/hq issue 254: a person closes a plan that will not move again, by its id.
|
|
func TestAPersonClosesAStuckPlan(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
stuck := inventory.Plan{ID: "plan-97b1b2b", Repository: "novox/mesh-catalog", Commit: "97b1b2b",
|
|
Created: time.Now().UTC(), State: inventory.PlanRolling, Tier: 1, Tiers: [][]string{{"a"}, {"b"}},
|
|
Modules: map[string]*inventory.PlanModule{"a": {State: "built"}, "b": {}}}
|
|
if err := open.inventory.SavePlan(ctx, stuck); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := plansCommand(ctx, []string{"close", stuck.ID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
closed, err := open.inventory.PlanByID(ctx, stuck.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if closed.State != inventory.PlanFailed || !strings.Contains(closed.Note, "closed by hand") {
|
|
t.Fatalf("the plan was left %s: %q", closed.State, closed.Note)
|
|
}
|
|
if err := plansCommand(ctx, []string{"close", stuck.ID}); err == nil {
|
|
t.Fatal("a plan already closed was closed again")
|
|
}
|
|
if argv, err := argvFor("plans", map[string]any{"close": stuck.ID}); err != nil ||
|
|
!reflect.DeepEqual(argv, []string{"plans", "close", stuck.ID}) {
|
|
t.Fatalf("the seat's verb does not close a plan: %v %v", argv, err)
|
|
}
|
|
}
|