From f796dc0b73075be13fefe80fe10d556ec9fa899e Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 13 Oct 2026 05:21:56 +0200 Subject: [PATCH] Add the replay of issue 349: a merge acted on late superseded the merge after it (hq issue 349) Two catalogue merges acted on in reverse order left the fixed module planned at the commit without the fix. The replay uses only what the controller had before #179, so mesh-lab's prover can show it fails there and passes on #179 and main. Listed among the store tests, so a check without a database cannot pass its skip. --- cmd/mesh-controller/replay349_test.go | 67 +++++++++++++++++++++++++++ testdata/store-tests | 1 + 2 files changed, 68 insertions(+) create mode 100644 cmd/mesh-controller/replay349_test.go diff --git a/cmd/mesh-controller/replay349_test.go b/cmd/mesh-controller/replay349_test.go new file mode 100644 index 00000000..69e9e4db --- /dev/null +++ b/cmd/mesh-controller/replay349_test.go @@ -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") + } +} diff --git a/testdata/store-tests b/testdata/store-tests index 0cd4b1ba..2f766f99 100644 --- a/testdata/store-tests +++ b/testdata/store-tests @@ -40,3 +40,4 @@ ./internal/inventory TestAStoredBuildWithASilentMergeableFileIsStillReadAndComposed ./cmd/mesh-controller TestPlanAndNodeListAnOptionalRequirementLeftUnbound ./cmd/mesh-controller TestReplay340 +./cmd/mesh-controller TestReplay349