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) } }