mesh/merge-gate head 05576db018a4 merged into trunk a688dcff394b — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 05576db018a4 merged into trunk a688dcff394b — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/611-an-unchanged-build-is-delivered delivered: every member is delivered
A delivery plan made while another walk built a module reads it whole and names it; the walk, cut later, finds none of the changed files in the build source that build recorded and moves nothing. Its record named no module, so mesh-delivery read 'matched what runs' as 'never built', stopped the delivery and asked the operator about a change already on every machine. The walk now says each such module in delivery.unchanged, with why; its modules keep meaning what it builds.
146 lines
6.9 KiB
Go
146 lines
6.9 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// novox/hq issue 611: mesh-controller #293 changed one test file. Its delivery plan, made while another walk built
|
|
// build-agent and route-proxy (issue 606), read them whole and named them; at the cut their recorded build sources
|
|
// held none of its files, so its walk moved nothing and its record named no module. mesh-delivery could not tell
|
|
// "matched what runs" from "never built" and stopped the delivery, asking the operator about a change already on
|
|
// every machine. The walk now says, per module, that its build matched what runs (delivery.unchanged, ADR 0267 §5).
|
|
|
|
// The shape of #293: a test file of the controller's repository. The controller, built from the root, and the two
|
|
// modules packaging the repository each recorded a build source holding none of it: each is said unchanged, with
|
|
// why; a module of another repository is not named.
|
|
func TestAMergeSaysTheModulesWhoseBuildSourceItLeftUnchanged(t *testing.T) {
|
|
const catalogueRepo = "http://forge.internal:20000/novox/mesh-catalog.git"
|
|
const controllerRepo = "http://forge.internal:20000/novox/mesh-controller.git"
|
|
entries := []inventory.Entry{
|
|
fromRepo("mesh-controller", controllerRepo, ""),
|
|
fromRepo("build-agent", catalogueRepo, "modules/build-agent"),
|
|
fromRepo("route-proxy", catalogueRepo, "modules/route-proxy"),
|
|
fromRepo("gitea", catalogueRepo, "modules/gitea"),
|
|
}
|
|
read := map[string][]inventory.ReadRepository{
|
|
"mesh-controller": {{Own: true, Paths: []string{"go.mod", "module.json", "cmd/mesh-controller/", "internal/builder/"}}},
|
|
"build-agent": {
|
|
{Repository: "novox/mesh-controller", Ref: "main", Paths: []string{"go.mod", "cmd/mesh-builder/", "internal/builder/"}},
|
|
{Own: true, Paths: []string{"modules/build-agent/Dockerfile", "modules/build-agent/module.json"}},
|
|
},
|
|
"route-proxy": {
|
|
{Repository: "novox/mesh-controller", Ref: "main", Paths: []string{"go.mod", "examples/route-proxy/"}},
|
|
{Own: true, Paths: []string{"modules/route-proxy/Dockerfile", "modules/route-proxy/module.json"}},
|
|
},
|
|
}
|
|
m := link.SourceMoved{Owner: "novox", Repo: "mesh-controller", Base: "main", Commit: "8f1aec0",
|
|
Paths: []string{"internal/builder/replay363_test.go"}}
|
|
if moved := reachOfMerge(m, entries, read, sharedRepositoryEdges).Moved(); len(moved) != 0 {
|
|
t.Fatalf("the fixture moves %v: a test file is in no build source", moved)
|
|
}
|
|
got := unchangedOfMerge(m, entries, read)
|
|
var names []string
|
|
for n, why := range got {
|
|
names = append(names, n)
|
|
if !strings.Contains(why, "holds none of") || !strings.Contains(why, "internal/builder/replay363_test.go") ||
|
|
!strings.Contains(why, "the build the mesh holds is its build") {
|
|
t.Errorf("%s is said unchanged as %q", n, why)
|
|
}
|
|
}
|
|
if want := []string{"build-agent", "mesh-controller", "route-proxy"}; !reflect.DeepEqual(sortedOf(names), want) {
|
|
t.Fatalf("said unchanged: %v; want %v", sortedOf(names), want)
|
|
}
|
|
|
|
// A module whose build source holds a changed file is moved, never said unchanged.
|
|
touched := m
|
|
touched.Paths = []string{"internal/builder/builder.go"}
|
|
if got := unchangedOfMerge(touched, entries, read); got["mesh-controller"] != "" || got["build-agent"] != "" {
|
|
t.Errorf("a module the merge moves is said unchanged: %v", got)
|
|
}
|
|
if got := unchangedOfMerge(touched, entries, read); got["route-proxy"] == "" {
|
|
t.Errorf("the route proxy, whose build source holds none of it, is not said unchanged: %v", got)
|
|
}
|
|
// With no build source recorded, the module is read whole and moved: nothing says it unchanged.
|
|
unsaid := map[string][]inventory.ReadRepository{"build-agent": {{Repository: "novox/mesh-controller", Ref: "main"}}}
|
|
if got := unchangedOfMerge(m, entries, unsaid); len(got) != 0 {
|
|
t.Errorf("modules with no recorded build source are said unchanged: %v", got)
|
|
}
|
|
// Files not all said: every module is moved, none said unchanged.
|
|
truncated := m
|
|
truncated.PathsTruncated = true
|
|
if got := unchangedOfMerge(truncated, entries, read); len(got) != 0 {
|
|
t.Errorf("a merge whose files were not all said says %v unchanged", got)
|
|
}
|
|
}
|
|
|
|
// A module the walk builds anyway — another merge's, or a dependent — is not said unchanged: the modules map keeps
|
|
// meaning what the walk builds, and the two never name one module.
|
|
func TestAWalkSaysUnchangedOnlyWhatItDoesNotBuild(t *testing.T) {
|
|
unchanged := map[string]string{"build-agent": "why a", "route-proxy": "why b"}
|
|
got := unchangedLeft(unchanged, map[string]*inventory.PlanModule{"build-agent": {}})
|
|
if !reflect.DeepEqual(got, map[string]string{"route-proxy": "why b"}) {
|
|
t.Fatalf("left %v", got)
|
|
}
|
|
if got := unchangedLeft(map[string]string{"x": "y"}, map[string]*inventory.PlanModule{"x": {}}); got != nil {
|
|
t.Fatalf("left %v; want none", got)
|
|
}
|
|
}
|
|
|
|
// Against the store, the path a real merge takes: a merge whose module recorded a build source that holds none
|
|
// of its files is cut into a walk done with nothing to walk, whose record names the module unchanged and why.
|
|
func TestAWalkThatMovedNothingSaysWhichModulesMatchedWhatRuns(t *testing.T) {
|
|
open := windowed(t)
|
|
endsItsLine(t)
|
|
asksRecorded(t)
|
|
ctx := t.Context()
|
|
inv := open.inventory
|
|
if err := inv.RecordBuild(ctx, inventory.Build{ID: "build-one", Repository: "novox/one", Ref: "main",
|
|
Module: "one", Commit: "c0", On: "builder", Asked: t0.Add(-time.Hour),
|
|
Sources: []inventory.BuildSource{{Paths: []string{"main.go", "go.mod"}}}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
readme := repoMerge("one", "one1aaaaaaaa", t0)
|
|
readme.Paths = []string{"README.md"}
|
|
hear(t, open, readme, t0.Add(time.Second))
|
|
cutAt(t, open, t0.Add(2*time.Minute))
|
|
ws, _ := walks(t, open)
|
|
if len(ws) != 1 {
|
|
t.Fatalf("walks: %+v", ws)
|
|
}
|
|
w := ws[0]
|
|
if w.State != inventory.PlanDone || len(w.Tiers) != 0 || len(w.Modules) != 0 {
|
|
t.Fatalf("the walk reads %s, tiers %v, modules %v; want done with nothing to walk", w.State, w.Tiers, w.Modules)
|
|
}
|
|
if w.Delivery == nil || !strings.Contains(w.Delivery.Unchanged["one"], "holds none of the 1 changed file(s)") {
|
|
t.Fatalf("the walk does not say one matched what runs: %+v", w.Delivery)
|
|
}
|
|
if !strings.Contains(w.Note, "unchanged") || !strings.Contains(w.Note, "one") {
|
|
t.Errorf("the walk's note does not name it: %q", w.Note)
|
|
}
|
|
|
|
// A merge that changes the build source is walked, and says nothing unchanged.
|
|
code := repoMerge("one", "one2bbbbbbbb", t0.Add(3*time.Minute))
|
|
hear(t, open, code, t0.Add(3*time.Minute+time.Second))
|
|
cutAt(t, open, t0.Add(6*time.Minute))
|
|
ws, _ = walks(t, open)
|
|
if len(ws) != 2 {
|
|
t.Fatalf("walks: %+v", ws)
|
|
}
|
|
if _, built := ws[0].Modules["one"]; !built || ws[0].Delivery == nil || len(ws[0].Delivery.Unchanged) != 0 {
|
|
t.Fatalf("the walk of a change to main.go reads modules %v, delivery %+v", ws[0].Modules, ws[0].Delivery)
|
|
}
|
|
}
|
|
|
|
func sortedOf(names []string) []string {
|
|
out := append([]string{}, names...)
|
|
sort.Strings(out)
|
|
return out
|
|
}
|