Files
mesh-controller/internal/builder/predefined_test.go
T
mesh-adminandClaude Opus 5.5 2941ef3695 Read the list of predefined keys strictly, and prove the build, check, gate and registration on it
Part 3: the list's own reading (catalogue/predefined.go) and the tests of the build, module check, merge gate and registration (hq ADR 0348 clause 7, issue 661).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 05:47:44 +00:00

107 lines
4.6 KiB
Go

package builder
import (
"context"
"slices"
"strings"
"testing"
"github.com/novox/mesh-controller/internal/catalogue"
)
// A build reads the list of predefined keys at the root of the repository at the commit it builds (novox/hq ADR
// 0348 §7, design 57 package K2, issue 661): it refuses a list that is wrong, judges the manifest against it, and
// carries it with the commit's generation for the controller to publish.
const theFirstKeys = `{
"font-size": {"type": "number", "description": "the size text is shown in, in points", "minimum": 6, "maximum": 72},
"agent-managed-settings": {"type": "object", "description": "the managed settings of every agent session, a JSON object", "trusted": true}
}`
// fallingBack is a module of the catalogue whose font size falls back to the predefined key named.
func fallingBack(key, kind string) string {
def := "10"
if kind == "string" {
def = `"10"`
}
return `{"module":"dunst","version":"1",
"build":{"artifacts":[{"name":"look","kind":"archive","from":"files"}]},
"config":{"font-size":{"type":"` + kind + `","description":"the notification's font size, in points",
"default":` + def + `,"predefined":"` + key + `"}},
"resources":[
{"id":"theme","type":"archive","path":"/opt/dunst","artifact":"look"},
{"id":"rc","type":"file","path":"/etc/xdg/dunst/dunstrc","content":"font = Inter ${config:font-size}\n"}]}`
}
func buildInTheCatalogue(t *testing.T, manifest string, list *string) (Result, error) {
t.Helper()
r := &recorded{contents: map[string]string{"modules/dunst/" + ManifestName: manifest,
"modules/dunst/files/theme.conf": "dark", "README.md": "the catalogue"}}
if list != nil {
r.contents[catalogue.PredefinedFile] = *list
}
return Build(context.Background(), onTrunk{recorded: r}.run, r, "https://forge.invalid/mesh-catalog.git",
"modules/dunst", "", t.TempDir(), nil, Npmrc{}, GitCredential{}, nil)
}
func TestABuildNamingAPredefinedKeyIsJudgedAgainstItsTreesListAndCarriesIt(t *testing.T) {
list := theFirstKeys
got, err := buildInTheCatalogue(t, fallingBack("font-size", "number"), &list)
if err != nil {
t.Fatalf("a module falling back to font-size did not build: %v", err)
}
if got.Predefined == nil || got.Predefined.Generation != 42 {
t.Fatalf("the list was not carried with its generation: %+v", got.Predefined)
}
carried, err := catalogue.ParsePredefined(got.Predefined.Keys)
if err != nil || len(carried) != 2 || carried["font-size"].Type != catalogue.ConfigNumber {
t.Fatalf("the list carried is %s (%v)", got.Predefined.Keys, err)
}
// A change to the list is a change to the module that was judged against it.
if len(got.Sources) == 0 || !slices.Contains(got.Sources[0].Paths, catalogue.PredefinedFile) {
t.Fatalf("its build source leaves the list out: %+v", got.Sources)
}
for _, c := range []struct{ manifest, says string }{
{fallingBack("font-scale", "number"), `"font-scale", which is not on the list`},
{fallingBack("font-size", "string"), "which is a number, and this value is a string"},
} {
if _, err := buildInTheCatalogue(t, c.manifest, &list); err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("wanted the build refused saying %q, got %v", c.says, err)
}
}
}
func TestABuildRefusesAWrongList(t *testing.T) {
wrong := `{"font-size": {"type": "number", "description": "points", "unit": "pt"}}`
if _, err := buildInTheCatalogue(t, fallingBack("font-size", "number"), &wrong); err == nil ||
!strings.Contains(err.Error(), `unknown field "unit"`) {
t.Fatalf("a wrong list was built with: %v", err)
}
}
// A repository without the list builds as before, carrying none; a value naming a key there is refused.
func TestABuildWithoutAListCarriesNone(t *testing.T) {
plain := strings.Replace(fallingBack("font-size", "number"), `,"predefined":"font-size"`, "", 1)
got, err := buildInTheCatalogue(t, plain, nil)
if err != nil {
t.Fatal(err)
}
if got.Predefined != nil {
t.Fatalf("a list was carried from a tree holding none: %+v", got.Predefined)
}
if slices.Contains(got.Sources[0].Paths, catalogue.PredefinedFile) {
t.Fatalf("the build source names a list: %+v", got.Sources)
}
if _, err := buildInTheCatalogue(t, fallingBack("font-size", "number"), nil); err == nil ||
!strings.Contains(err.Error(), "is not present") {
t.Fatalf("a key named where no list is: %v", err)
}
// A list present and a module naming no key: carried, and not the module's build source.
list := theFirstKeys
got, err = buildInTheCatalogue(t, plain, &list)
if err != nil || got.Predefined == nil || slices.Contains(got.Sources[0].Paths, catalogue.PredefinedFile) {
t.Fatalf("a module naming no key: %+v %+v %v", got.Predefined, got.Sources, err)
}
}