Files
mesh-controller/internal/inventory/builds_test.go
T
jschoubben aa771616bb A merge rebuilds what it changed, and what packages it
Three faults in one path. A merge rebuilt every module built from the repository, so one change in
a repository holding twenty-six of them meant twenty-six builds. A merge into a repository a module
only *packages* source from rebuilt nothing — two modules are built from the control plane's own
repository and neither had ever been rebuilt when it moved — because the manifest the mesh keeps
carries no build section, so a build now says which repositories it read and the mesh keeps that
beside what it stood on. And a module handed over by hand could record a repository with no
directory inside it, which is a module nothing can ever rebuild (novox/hq 04-ISSUES/131, /132).

A change inside no module's own directory is a change to what they share, and everything built from
that repository is rebuilt: rebuilding too much is the safe direction, because the fault this whole
path exists for is a mesh that believes it is current and is not.
2026-09-28 09:20:01 +02:00

172 lines
5.2 KiB
Go

package inventory
import (
"context"
"strings"
"testing"
)
// A build result was answered to whoever asked and kept nowhere, so "when did this last build",
// "why did it fail" and "which machine built what is running" had no answer at all.
func aBuild(id, module, failed string) Build {
b := Build{
ID: id, Repository: "https://forge.invalid/" + strings.TrimSuffix(module, "?") + ".git",
Module: module, On: "a-build-machine", Failed: failed,
}
if failed == "" {
b.Commit = "c0ffee" + id
b.Made = []Artifact{{Name: "config", Kind: "archive", Reference: "…/blobs/sha256:…"}}
}
return b
}
func TestAFailedBuildIsARowLikeAnyOther(t *testing.T) {
// One that leaves no trace is indistinguishable from a build nobody asked for, and the
// difference is the whole of whether somebody should be looking at something.
inv := fresh(t)
ctx := context.Background()
if err := inv.RecordBuild(ctx, aBuild("1", "", "cannot clone: no such repository")); err != nil {
t.Fatal(err)
}
got, err := inv.Builds(ctx, "", 10)
if err != nil {
t.Fatal(err)
}
if len(got) != 1 {
t.Fatalf("got %d builds", len(got))
}
if got[0].Worked() {
t.Fatal("a failure was recorded as a success")
}
if !strings.Contains(got[0].Failed, "no such repository") {
t.Fatalf("the builder's own words were not kept: %q", got[0].Failed)
}
// And it kept what was asked for, which is the only thing a person can go and look at when
// the build never learned what it was building.
if got[0].Module != "" || got[0].Repository == "" {
t.Fatalf("got %+v", got[0])
}
}
func TestOneResultRecordedTwiceIsOneBuild(t *testing.T) {
// A result can arrive twice: as the answer to whoever asked, and on the exchange when nobody
// was. Two rows would show one build as two, and which is real is not answerable afterwards.
inv := fresh(t)
ctx := context.Background()
for i := 0; i < 2; i++ {
if err := inv.RecordBuild(ctx, aBuild("same", "shell", "")); err != nil {
t.Fatal(err)
}
}
got, err := inv.Builds(ctx, "", 10)
if err != nil {
t.Fatal(err)
}
if len(got) != 1 {
t.Fatalf("one build was recorded %d times", len(got))
}
}
func TestBuildsComeBackNewestFirstAndCanBeAskedPerModule(t *testing.T) {
inv := fresh(t)
ctx := context.Background()
for _, b := range []Build{
aBuild("1", "shell", ""),
aBuild("2", "meshboard", ""),
aBuild("3", "shell", "the tests failed"),
} {
if err := inv.RecordBuild(ctx, b); err != nil {
t.Fatal(err)
}
}
all, err := inv.Builds(ctx, "", 10)
if err != nil {
t.Fatal(err)
}
if len(all) != 3 || all[0].ID != "3" {
t.Fatalf("newest is not first: %v", ids(all))
}
// Per module, because "what has happened to this" is asked when deciding whether to trust it.
shell, err := inv.Builds(ctx, "shell", 10)
if err != nil {
t.Fatal(err)
}
if len(shell) != 2 {
t.Fatalf("shell has %d builds: %v", len(shell), ids(shell))
}
for _, b := range shell {
if b.Module != "shell" {
t.Fatalf("asked for shell and got %q", b.Module)
}
}
}
func TestWhatWasPublishedIsKeptWithTheBuild(t *testing.T) {
// So a digest can be traced back to the build that made it, without keeping the manifest a
// second time in a place that can disagree with the first.
inv := fresh(t)
ctx := context.Background()
if err := inv.RecordBuild(ctx, aBuild("1", "shell", "")); err != nil {
t.Fatal(err)
}
got, _ := inv.Builds(ctx, "", 10)
if len(got[0].Made) != 1 || got[0].Made[0].Kind != "archive" {
t.Fatalf("what was published was not kept: %+v", got[0].Made)
}
}
func TestAskingForMoreThanThereIsIsNotAnError(t *testing.T) {
inv := fresh(t)
got, err := inv.Builds(context.Background(), "", 100)
if err != nil {
t.Fatal(err)
}
if len(got) != 0 {
t.Fatalf("got %d", len(got))
}
}
func ids(builds []Build) []string {
var out []string
for _, b := range builds {
out = append(out, b.ID)
}
return out
}
// What a build read besides its module's own repository comes back for the newest build of each
// module, and only for builds that worked. Nothing recorded is absent rather than empty, which is how
// a build made before the mesh kept this is told from one that read nothing (novox/hq 04-ISSUES/131).
func TestWhatABuildReadComesBackForTheNewestBuildOfEachModule(t *testing.T) {
inv := fresh(t)
ctx := context.Background()
older := aBuild("older", "builder", "")
older.Read = []ReadRepository{{Repository: "novox/mesh-controller", Ref: "release"}}
newer := aBuild("newer", "builder", "")
newer.Read = []ReadRepository{{Repository: "novox/mesh-controller", Ref: "main"}}
plain := aBuild("plain", "gitea", "")
failed := aBuild("failed", "route-proxy", "cannot clone")
failed.Read = []ReadRepository{{Repository: "novox/mesh-controller", Ref: "main"}}
for _, b := range []Build{older, newer, plain, failed} {
if err := inv.RecordBuild(ctx, b); err != nil {
t.Fatal(err)
}
}
read, err := inv.ReadRepositories(ctx)
if err != nil {
t.Fatal(err)
}
if len(read["builder"]) != 1 || read["builder"][0].Ref != "main" {
t.Fatalf("the newest build's reading is %+v", read["builder"])
}
if _, has := read["gitea"]; has {
t.Fatalf("a build that read nothing but its own repository reads as %+v", read["gitea"])
}
if _, has := read["route-proxy"]; has {
t.Fatal("a failed build's reading was kept as what that module reads")
}
}