Say what relates two changes, so unrelated ones can walk beside each other
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed

The helper decides, by repository, module (widened to dependents), delivery
group head or module graph edge, whether two changes are related, as novox/hq
ADR 0304 decision 1 reads in ADR 0306 decision 3. Nothing calls it yet; it is
the first step of issue 525's parallel walks.
This commit is contained in:
2026-10-12 02:04:38 +02:00
parent b12da96906
commit 96005a8bfd
2 changed files with 190 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
package main
import (
"encoding/json"
"testing"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/link"
)
// heardMerge is a kept merge of a repository from a head branch that moved modules.
func heardMerge(repository, head string, moves ...string) inventory.BatchedMerge {
m := inventory.BatchedMerge{Repository: repository, Branch: "main", Commit: "c-" + repository}
m.Event, _ = json.Marshal(keptMerge{SourceMoved: link.SourceMoved{Head: head}, Moves: moves})
return m
}
// walkOf is a walk carrying one repository's commit and planning modules.
func walkOf(repository string, modules ...string) inventory.Plan {
p := inventory.Plan{Repository: repository, Commit: "w-" + repository, Modules: map[string]*inventory.PlanModule{}}
for _, name := range modules {
p.Modules[name] = &inventory.PlanModule{}
}
return p
}
// TestRelatedChanges: two changes are related by a repository, a module, a group head or a graph edge between
// their modules, and by a module reached only through dependents (novox/hq ADR 0304 decision 1, ADR 0306
// decision 3); otherwise they are not.
func TestRelatedChanges(t *testing.T) {
edges := []inventory.Edge{
{From: "nzbget", To: "base-image", Kind: inventory.EdgeStandsOn},
{From: "app", To: "build-machine", Kind: inventory.EdgeBuiltBy},
}
cases := []struct {
name string
a, b footprint
why string
}{
{"repository, case aside",
footprintOfWalk(walkOf("novox/Mesh-Catalog", "notes"), nil),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("NOVOX/mesh-catalog", "fix/a", "app")}, nil),
"both change novox/mesh-catalog"},
{"module",
footprintOfWalk(walkOf("novox/one", "nzbget"), nil),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "fix/b", "nzbget")}, nil),
"both move nzbget"},
{"edge, walk depends on batch",
footprintOfWalk(walkOf("novox/one", "nzbget"), nil),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "fix/b", "base-image")}, nil),
"nzbget depends on base-image (stands-on)"},
{"edge, batch depends on walk",
footprintOfWalk(walkOf("novox/one", "build-machine"), nil),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "fix/b", "app")}, nil),
"app depends on build-machine (built-by)"},
{"group head",
footprintOfWalk(walkOf("novox/one", "notes"), []inventory.BatchedMerge{heardMerge("novox/one", "feat/x")}),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "feat/x", "app")}, nil),
"both are members of group feat/x"},
{"module reached through dependents",
footprintOfWalk(walkOf("novox/one", "nzbget"), nil),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "fix/b", "base-image")}, edges),
"both move nzbget"},
{"unrelated",
footprintOfWalk(walkOf("novox/one", "notes"), []inventory.BatchedMerge{heardMerge("novox/one", "feat/x")}),
footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "feat/y", "app")}, edges),
""},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
for _, pair := range [][2]footprint{{c.a, c.b}, {c.b, c.a}} {
is, why := related(pair[0], pair[1], edges)
if is != (c.why != "") || why != c.why {
t.Errorf("related = %v, %q; want %q", is, why, c.why)
}
}
})
}
}
// TestFootprintOfBatchWidensToDependents: a batch's modules are what its merges moved and every module that
// depends on them, along the edges that widen a walk.
func TestFootprintOfBatchWidensToDependents(t *testing.T) {
edges := []inventory.Edge{
{From: "nzbget", To: "base-image", Kind: inventory.EdgeStandsOn},
{From: "base-image", To: "build-machine", Kind: inventory.EdgeBuiltBy},
}
f := footprintOfBatch([]inventory.BatchedMerge{heardMerge("novox/two", "", "base-image")}, edges)
if !f.modules["base-image"] || !f.modules["nzbget"] || f.modules["build-machine"] || len(f.heads) != 0 {
t.Errorf("footprint = %+v; want base-image and nzbget, no head", f)
}
}