Files
mesh-controller/internal/builder/build_source_test.go
T
jochen 4d1b81b6cb
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Say what a build was made from, and hand an image compiling Go only that (hq ADR 0267, issue 363)
A merge to the controller's repository moved the route proxy and the build
seat's holder whatever it changed, because nothing said which files their
builds read. A build of a trunk commit now says its build source per
repository: a Go program's import closure, an archive's directory, an
image's recipe and the package it names in the new 'compiles' field. That
image is built from its build source alone, so a recipe reading past it
fails by name, and its fingerprint is over what it was handed.
2026-10-10 02:47:35 +02:00

177 lines
7.6 KiB
Go

package builder
import (
"context"
"os"
"path/filepath"
"slices"
"strings"
"testing"
)
// What a build says it was made from, as files (novox/hq ADR 0267), and what a build compiling a Go
// program is handed.
// onTrunk answers the trunk's questions as a clone of a commit on main would, or off it.
type onTrunk struct {
*recorded
off bool
}
func (o onTrunk) run(ctx context.Context, dir, name string, args ...string) (string, error) {
if name == "git" && len(args) > 0 && args[0] == "symbolic-ref" {
return "origin/main\n", nil
}
if name == "git" && len(args) > 0 && args[0] == "merge-base" && o.off {
return "", os.ErrNotExist
}
return compiling{o.recorded}.run(ctx, dir, name, args...)
}
// aSharedRepository is a repository holding two programs that share a package, as the controller's does.
func aSharedRepository(readme, shared string) map[string]string {
return map[string]string{
"go.mod": "module example.com/ctl\n\ngo 1.22\n",
"go.sum": "",
"README.md": readme,
"cmd/ctl/main.go": "package main\n\nimport _ \"example.com/ctl/internal/shared\"\n\nfunc main() {}\n",
"proxy/main.go": "package main\n\nimport _ \"example.com/ctl/internal/shared\"\n\nfunc main() {}\n",
"internal/shared/s.go": "package shared\n\nconst S = " + shared + "\n",
"internal/only/o.go": "package only\n",
}
}
const aProxy = `{"module":"route-proxy","version":"1",
"build":{"artifacts":[
{"name":"server","kind":"image","from":"Dockerfile","compiles":"proxy",
"context":{"repository":"https://forge.invalid/ctl.git","ref":"main"}},
{"name":"trust","kind":"upstream","from":"alpine@sha256:` + "3333333333333333333333333333333333333333333333333333333333333333" + `"}]}}`
func buildTheProxy(t *testing.T, context_ map[string]string, off bool) (Result, *recorded, string) {
t.Helper()
r := &recorded{
contents: map[string]string{"modules/route-proxy/" + ManifestName: aProxy, "modules/route-proxy/Dockerfile": "FROM scratch\nCOPY . .\n", "modules/route-proxy/README.md": "x"},
secondary: map[string]map[string]string{"https://forge.invalid/ctl.git": context_},
}
workspace := t.TempDir()
got, err := Build(context.Background(), onTrunk{r, off}.run, r,
"https://forge.invalid/catalogue.git", "modules/route-proxy", "", workspace, nil, Npmrc{}, GitCredential{}, nil)
if err != nil {
t.Fatal(err)
}
return got, r, workspace
}
func TestAnImageCompilingGoIsHandedItsBuildSourceAndSaysIt(t *testing.T) {
got, r, workspace := buildTheProxy(t, aSharedRepository("one", "1"), false)
// Built in the narrowed tree, which holds the program's closure and nothing else.
at := ""
for i, line := range r.ran {
if strings.HasPrefix(line, "docker build ") {
at = r.dirs[i]
}
}
if filepath.Base(at) != "narrow-server" {
t.Fatalf("docker build ran in %q, not the narrowed build source", at)
}
for file, want := range map[string]bool{"proxy/main.go": true, "internal/shared/s.go": true, "go.mod": true,
"cmd/ctl/main.go": false, "internal/only/o.go": false, "README.md": false} {
_, err := os.Stat(filepath.Join(workspace, "narrow-server", filepath.FromSlash(file)))
if (err == nil) != want {
t.Errorf("%s handed to the recipe: %v, wanted %v", file, err == nil, want)
}
}
// Said per repository: its own, the manifest and the recipe; the context's, the closure.
if len(got.Sources) != 2 {
t.Fatalf("sources %+v", got.Sources)
}
own, ctx := got.Sources[0], got.Sources[1]
if own.Repository != "" || !slices.Equal(own.Paths, []string{"modules/route-proxy/Dockerfile", "modules/route-proxy/module.json"}) {
t.Errorf("its own build source: %+v", own)
}
if ctx.Repository != "https://forge.invalid/ctl.git" || ctx.Ref != "main" {
t.Errorf("the context's build source names %q at %q", ctx.Repository, ctx.Ref)
}
for file, want := range map[string]bool{"proxy/main.go": true, "internal/shared/s.go": true, "go.mod": true,
"cmd/ctl/main.go": false, "README.md": false} {
if SourceHolds(ctx.Paths, file) != want {
t.Errorf("the context's build source holds %s: %v, wanted %v (%v)", file, !want, want, ctx.Paths)
}
}
// **The fingerprint is over the build source** (rule 5): a change outside it is one build, inside it another.
readme, _, _ := buildTheProxy(t, aSharedRepository("two", "1"), false)
if readme.Source != got.Source {
t.Errorf("a README of the context changed the fingerprint: %s %s", got.Source, readme.Source)
}
shared, _, _ := buildTheProxy(t, aSharedRepository("one", "2"), false)
if shared.Source == got.Source {
t.Error("a change to the program's closure kept its fingerprint")
}
}
// A build off the trunk says no build source: the planner maps a merge onto the trunk's.
func TestABuildOffTheTrunkSaysNoBuildSource(t *testing.T) {
got, _, _ := buildTheProxy(t, aSharedRepository("one", "1"), true)
if len(got.Sources) != 0 {
t.Fatalf("a build off the trunk said %+v", got.Sources)
}
}
// A recipe reading past its build source fails, naming what it could not find — in the real docker build;
// here, the file is simply not in the tree it is handed, which is what makes that so.
func TestAnImageCompilingANonexistentPackageFails(t *testing.T) {
r := &recorded{
contents: map[string]string{ManifestName: strings.Replace(aProxy, `"compiles":"proxy"`, `"compiles":"nowhere"`, 1),
"Dockerfile": "FROM scratch\n"},
secondary: map[string]map[string]string{"https://forge.invalid/ctl.git": aSharedRepository("one", "1")},
}
_, err := Build(context.Background(), onTrunk{r, false}.run, r,
"https://forge.invalid/catalogue.git", "", "", t.TempDir(), nil, Npmrc{}, GitCredential{}, nil)
if err == nil || !strings.Contains(err.Error(), "nowhere") {
t.Fatalf("a package that is not there built: %v", err)
}
}
// A Go bundle of a module built from its repository's root says its import closure, and the manifest;
// an image that compiles nothing it was told of leaves the module's source whole, and says none.
func TestAGoBundleSaysItsClosureAndAnUntoldImageNothing(t *testing.T) {
files := aSharedRepository("one", "1")
manifest := `{"module":"ctl","version":"1","build":{"artifacts":[
{"name":"controller","kind":"bundle","language":"go","system":"arch","from":"cmd/ctl","binary":"ctl"}]},
"resources":[{"id":"controller","type":"process","name":"ctl","artifact":"controller","run":["./ctl"]}]}`
r := &recorded{contents: map[string]string{ManifestName: manifest}}
for k, v := range files {
r.contents[k] = v
}
held := map[string]string{"mesh-tools-go/build": "registry.invalid/mesh-tools-go/build@sha256:" + strings.Repeat("b", 64)}
got, err := Build(context.Background(), onTrunk{r, false}.run, r,
"https://forge.invalid/ctl.git", "", "", t.TempDir(), held, Npmrc{}, GitCredential{}, nil)
if err != nil {
t.Fatal(err)
}
if len(got.Sources) != 1 || got.Sources[0].Repository != "" {
t.Fatalf("sources %+v", got.Sources)
}
for file, want := range map[string]bool{"cmd/ctl/main.go": true, "internal/shared/s.go": true, ManifestName: true,
"go.sum": true, "proxy/main.go": false, "README.md": false, "internal/only/o.go": false} {
if SourceHolds(got.Sources[0].Paths, file) != want {
t.Errorf("%s: held %v, wanted %v (%v)", file, !want, want, got.Sources[0].Paths)
}
}
untold := `{"module":"ctl","version":"1","build":{"artifacts":[
{"name":"server","kind":"image","from":"Dockerfile"}]}}`
r = &recorded{contents: map[string]string{ManifestName: untold, "Dockerfile": "FROM scratch\n"}}
got, err = Build(context.Background(), onTrunk{r, false}.run, r,
"https://forge.invalid/ctl.git", "", "", t.TempDir(), nil, Npmrc{}, GitCredential{}, nil)
if err != nil {
t.Fatal(err)
}
if len(got.Sources) != 0 {
t.Fatalf("an image compiling nothing it was told of said a build source: %+v", got.Sources)
}
}