Add the replay of issue 349: a merge acted on late superseded the merge after it (hq issue 349) #290

Merged
mesh-admin merged 2 commits from fix/349-replay into main 2026-10-13 03:54:10 +00:00
2 changed files with 68 additions and 0 deletions
Showing only changes of commit bbf70fbda4 - Show all commits
+67
View File
@@ -0,0 +1,67 @@
package main
import (
"testing"
"time"
"github.com/novox/mesh-controller/internal/catalogue"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/link"
)
// Replay of novox/hq issue 349 (2026-10-09): two merges into the catalogue's main were acted on in reverse order.
// The later one (a082615b, a security fix to the forge's module) was planned first; the earlier one (8ff8197a,
// another module), handed over late, was planned after it, and its plan superseded the later merge's at tier 0
// with "gitea planned there again": the forge's module was to be built for the commit without the fix. Written
// only with what the controller had before its fix (mesh-controller #179): a mesh, two registered modules of the
// catalogue, the merges as the forge announces them, the plans the inventory keeps — so it is laid over the
// commit before.
func TestReplay349(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
asksWithPaths(t)
for _, module := range []string{"app", "notes"} {
if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: module, Version: "1"},
inventory.Source{Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/" + module, Ref: "main",
BuiltFrom: "c0", Head: "c0"}); err != nil {
t.Fatal(err)
}
}
// Both made after the modules were last looked at, so neither is history.
made := time.Now().UTC().Add(time.Minute)
merge := func(commit, module string, at time.Time) link.SourceMoved {
return link.SourceMoved{Owner: "novox", Repo: "mesh-catalog", Base: "main", Commit: commit,
MergedAt: at.Format(time.RFC3339Nano), Paths: []string{"modules/" + module + "/module.json"},
ModuleDirs: []string{"modules/" + module}, ModuleDirsSaid: true}
}
const fix, earlier = "a082615bfix", "8ff8197aearlier"
// The later merge, the fix to notes, is acted on first; the earlier one, moving app, after it.
if err := (following{open: open}).SourceMoved(ctx, merge(fix, "notes", made.Add(time.Minute))); err != nil {
t.Fatal(err)
}
if err := (following{open: open}).SourceMoved(ctx, merge(earlier, "app", made)); err != nil {
t.Fatal(err)
}
plans, err := inv.RecentPlans(ctx, 20)
if err != nil {
t.Fatal(err)
}
// A plan superseded hands its modules to the plan that took it over; the plans left standing are what the mesh
// builds. One of them builds the fixed module, and every one that does builds it at the commit holding the fix.
built := 0
for _, p := range plans {
t.Logf("%s %s at %.8s: %v %s", p.ID, p.State, p.Commit, p.Tiers, p.Note)
if _, holds := p.Modules["notes"]; !holds || p.State == inventory.PlanSuperseded {
continue
}
built++
if p.Commit != fix {
t.Errorf("%s plans the fixed module notes at %s, a commit without the fix %s: %s", p.ID, p.Commit, fix, p.Note)
}
}
if built == 0 {
t.Error("no plan left standing builds notes, the module the later merge fixed")
}
}
+1
View File
@@ -41,3 +41,4 @@
./cmd/mesh-controller TestPlanAndNodeListAnOptionalRequirementLeftUnbound
./cmd/mesh-controller TestReplay340
./cmd/mesh-controller TestOnlyATrustedFilesNamedKeyIsRefusedThroughAVerb
./cmd/mesh-controller TestReplay349