package inventory import ( "testing" "time" ) // A plan is a record the mesh keeps and resumes (novox/hq ADR 0162): written whole, read back open, // advanced, and gone from the open ones when done. func TestAPlanIsKeptAdvancedAndResumedFromTheStore(t *testing.T) { inv := ForTest(t) ctx := t.Context() p := Plan{ID: "plan-1", Repository: "novox/mesh-tools", Commit: "abc", Created: time.Now().UTC(), State: PlanBuilding, Tiers: [][]string{{"mesh-tools"}, {"builder"}, {"shop"}}, Modules: map[string]*PlanModule{"mesh-tools": {}, "builder": {}, "shop": {}}} if err := inv.SavePlan(ctx, &p); err != nil { t.Fatal(err) } open, err := inv.OpenPlans(ctx) if err != nil || len(open) != 1 || open[0].ID != "plan-1" || len(open[0].Tiers) != 3 { t.Fatalf("the plan was not kept whole: %v %+v", err, open) } // Another controller picks it up where it was left: a tier advanced and a module built. now := time.Now().UTC() resumed := open[0] resumed.Tier = 1 resumed.Modules["mesh-tools"].State = "built" resumed.Modules["mesh-tools"].BuiltAt = &now resumed.State = PlanRolling resumed.Note = "tier 0 built; waiting for builder on anchor to be applied" if err := inv.SavePlan(ctx, &resumed); err != nil { t.Fatal(err) } again, err := inv.PlanByID(ctx, "plan-1") if err != nil || again.Tier != 1 || again.Modules["mesh-tools"].State != "built" || again.State != PlanRolling { t.Fatalf("the advanced plan did not come back as left: %v %+v", err, again) } again.State = PlanDone if err := inv.SavePlan(ctx, &again); err != nil { t.Fatal(err) } if open, _ = inv.OpenPlans(ctx); len(open) != 0 { t.Fatalf("a done plan is not open: %+v", open) } if recent, _ := inv.RecentPlans(ctx, 5); len(recent) != 1 || recent[0].State != PlanDone { t.Fatalf("a done plan is still among the recent ones: %+v", recent) } } // novox/hq issue 254: a plan keeps the branch its merge went into, and a superseded plan is not open. func TestASupersededPlanIsNotOpen(t *testing.T) { inv := ForTest(t) ctx := t.Context() p := Plan{ID: "plan-1", Repository: "novox/mesh-catalog", Branch: "main", Commit: "abc", Created: time.Now().UTC(), State: PlanBuilding, Tiers: [][]string{{"gitea"}}, Modules: map[string]*PlanModule{"gitea": {}}} if err := inv.SavePlan(ctx, &p); err != nil { t.Fatal(err) } kept, err := inv.PlanByID(ctx, "plan-1") if err != nil || kept.Branch != "main" { t.Fatalf("the branch was not kept: %v %+v", err, kept) } kept.State = PlanSuperseded kept.Note = "superseded at tier 0 by plan-2" if err := inv.SavePlan(ctx, &kept); err != nil { t.Fatal(err) } if open, err := inv.OpenPlans(ctx); err != nil || len(open) != 0 { t.Fatalf("a superseded plan is still open: %v %+v", err, open) } if recent, _ := inv.RecentPlans(ctx, 5); len(recent) != 1 || recent[0].State != PlanSuperseded { t.Fatalf("a superseded plan is not among the recent ones as superseded: %+v", recent) } } // novox/hq issue 349 (review of the follow-up): the newest merge of a branch is the one merged last, whatever // order the plans were made in, in any state; a tie is broken by the plan made last; a release, another // branch and another repository are never it; and its time is kept to the nanosecond. func TestTheNewestMergeOfABranchIsTheOneMergedLast(t *testing.T) { inv := ForTest(t) ctx := t.Context() t0 := time.Date(2026, 10, 9, 10, 0, 0, 0, time.UTC) save := func(id, repo, branch string, merged, created time.Time, state string, release bool) { t.Helper() p := Plan{ID: id, Repository: repo, Branch: branch, Commit: id + "-commit", Merged: merged, Created: created, State: state, Tiers: [][]string{}, Modules: map[string]*PlanModule{}} if release { p.Release = &PlanRelease{} } if err := inv.SavePlan(ctx, &p); err != nil { t.Fatal(err) } } if _, found, err := inv.NewestMergeOf(ctx, "novox/mesh-catalog", "main"); err != nil || found { t.Fatalf("a branch with no plan: %v %v", found, err) } // Made in the other order than merged: the later merge's plan made first, and done. save("plan-later", "novox/mesh-catalog", "main", t0.Add(2*time.Minute+250*time.Millisecond), t0, PlanDone, false) save("plan-earlier", "novox/mesh-catalog", "main", t0.Add(time.Minute), t0.Add(5*time.Minute), PlanRolling, false) save("plan-other-branch", "novox/mesh-catalog", "release", t0.Add(time.Hour), t0, PlanRolling, false) save("plan-other-repo", "novox/mesh-controller", "main", t0.Add(time.Hour), t0, PlanRolling, false) save("release-1", "novox/mesh-catalog", "main", t0.Add(time.Hour), t0, PlanRolling, true) save("plan-unknown", "novox/mesh-catalog", "main", time.Time{}, t0.Add(time.Hour), PlanRolling, false) p, found, err := inv.NewestMergeOf(ctx, "Novox/Mesh-Catalog", "main") if err != nil || !found || p.ID != "plan-later" { t.Fatalf("the newest merge: %s %v %v", p.ID, found, err) } if !p.Merged.Equal(t0.Add(2*time.Minute + 250*time.Millisecond)) { t.Fatalf("its merge time was not kept to the nanosecond: %s", p.Merged) } // The same merge time: the plan made last. save("plan-again", "novox/mesh-catalog", "main", t0.Add(2*time.Minute+250*time.Millisecond), t0.Add(time.Minute), PlanBuilding, false) if p, _, _ := inv.NewestMergeOf(ctx, "novox/mesh-catalog", "main"); p.ID != "plan-again" { t.Fatalf("one merge time, two plans: %s", p.ID) } }