Files
mesh-controller/internal/catalogue/predefined_test.go
T
mesh-tools 97a2d841c7 Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions,
hands bus accounts to the vault and judges the new manifest words, on the
feature's SDK and node-engine validator (feature 57, config and provisioning).
2026-10-15 21:27:33 +00:00

176 lines
7.0 KiB
Go

package catalogue
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
// The list of predefined keys (novox/hq ADR 0348 §7, design 57 package K2, issue 661): read strictly, and every
// manifest naming a key judged against the list of the tree it is read in.
// theFirstKeys is the catalogue's first list: font-size and agent-managed-settings.
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}
}`
func TestTheFirstListIsRead(t *testing.T) {
list, err := ParsePredefined([]byte(theFirstKeys))
if err != nil {
t.Fatal(err)
}
if got := strings.Join(list.Keys(), ","); got != "agent-managed-settings,font-size" {
t.Fatalf("keys %s", got)
}
if k := list["font-size"]; k.Type != ConfigNumber || *k.Minimum != 6 || *k.Maximum != 72 || k.Trusted {
t.Fatalf("font-size read as %+v", k)
}
if k := list["agent-managed-settings"]; k.Type != ConfigObject || !k.Trusted {
t.Fatalf("agent-managed-settings read as %+v", k)
}
// One list is one text, however it was written.
a, _ := list.Canonical()
again, _ := ParsePredefined(a)
b, _ := again.Canonical()
if string(a) != string(b) {
t.Fatalf("canonical text moved:\n%s\n%s", a, b)
}
}
func TestAListIsRefusedWholeForAnyWrongKey(t *testing.T) {
for _, c := range []struct{ file, says string }{
{`[]`, "not a JSON object of keys"},
{`null`, "not a JSON object of keys"},
{`{"font-size": {"type": "number", "description": "points", "default": 10}}`, `unknown field "default"`},
{`{"font-size": {"description": "points"}}`, "no type"},
{`{"font-size": {"type": "decimal", "description": "points"}}`, `type "decimal"`},
{`{"font-size": {"type": "number"}}`, "no description"},
{`{"accent": {"type": "object", "description": "colours"}}`, "not trusted"},
{`{"font-size": {"type": "number", "description": "points", "minimum": 9, "maximum": 6}}`, "above its maximum"},
{`{"accent": {"type": "string", "description": "a name", "pattern": "("}}`, "not a regular expression"},
{`{"accent": {"type": "boolean", "description": "on or off", "minimum": 1}}`, "minimum or maximum is for a number"},
{`{"Font Size": {"type": "number", "description": "points"}}`, "not a usable key"},
{`{"places": {"type": "string", "description": "where"}}`, "placement"},
{`{"puid": {"type": "number", "description": "an id"}}`, "placement"},
} {
if _, err := ParsePredefined([]byte(c.file)); err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("%s: wanted a refusal saying %q, got %v", c.file, c.says, err)
}
}
}
// fallingBackTo is inConfigForm with its font size falling back to a predefined key.
func fallingBackTo(t *testing.T, key string) []byte {
t.Helper()
m := inConfigForm()
declaration(m, "font-size")["predefined"] = key
raw, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
return raw
}
// A manifest naming font-size registers against the list; one naming a key not on it, or of another type, is
// refused — judged against the list given, whatever the mesh published.
func TestAManifestIsJudgedAgainstTheListOfItsTree(t *testing.T) {
withPredefined(t, nil)
list, err := ParsePredefined([]byte(theFirstKeys))
if err != nil {
t.Fatal(err)
}
if _, err := ParseManifestWith(fallingBackTo(t, "font-size"), list); err != nil {
t.Fatalf("a value falling back to font-size is refused: %v", err)
}
if _, err := ParseManifestWith(fallingBackTo(t, "font-scale"), list); err == nil ||
!strings.Contains(err.Error(), `"font-scale", which is not on the list`) {
t.Fatalf("a key not on the list: %v", err)
}
m := inConfigForm()
declaration(m, "theme")["predefined"] = "font-size"
raw, _ := json.Marshal(m)
if _, err := ParseManifestWith(raw, list); err == nil || !strings.Contains(err.Error(), "which is a number") {
t.Fatalf("a key of another type: %v", err)
}
// Where no tree is read, the published list judges: none published refuses.
if _, err := ParseManifest(fallingBackTo(t, "font-size")); err == nil || !strings.Contains(err.Error(), "is not present") {
t.Fatalf("judged against no list: %v", err)
}
withPredefined(t, list)
if _, err := ParseManifest(fallingBackTo(t, "font-size")); err != nil {
t.Fatalf("judged against the published list: %v", err)
}
}
// A template's text filled from the tree keeps the list the manifest was judged against: read again, it is not
// judged against the published one.
func TestTemplatesFilledKeepTheListTheManifestWasJudgedAgainst(t *testing.T) {
withPredefined(t, nil)
list, _ := ParsePredefined([]byte(theFirstKeys))
dir := t.TempDir()
if err := os.MkdirAll(filepath.Join(dir, DefaultTemplatesDir), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, DefaultTemplatesDir, "rc"), []byte("font = ${config:font-size}\n"), 0o644); err != nil {
t.Fatal(err)
}
m := inConfigForm()
declaration(m, "font-size")["predefined"] = "font-size"
r := resourceOf(m, "configuration")
delete(r, "content")
r["template"] = DefaultTemplatesDir + "/rc"
m["build"] = map[string]any{"artifacts": []any{map[string]any{"name": "files", "kind": "archive", "from": "files"}}}
declaration(m, "width")["runtime"] = true
declaration(m, "sender")["runtime"] = true
raw, _ := json.Marshal(m)
parsed, err := ParseManifestWith(raw, list)
if err != nil {
t.Fatal(err)
}
if _, err := WithTemplatesFrom(parsed, dir); err != nil {
t.Fatalf("filled from the tree, it was judged against another list: %v", err)
}
}
// module check reads the list at the root of the repository above the manifest; no repository, no list.
func TestTheListIsFoundAtTheRepositoryRootAboveAManifest(t *testing.T) {
root := t.TempDir()
module := filepath.Join(root, "modules", "dunst")
if err := os.MkdirAll(module, 0o755); err != nil {
t.Fatal(err)
}
if list, err := PredefinedAbove(module); err != nil || list != nil {
t.Fatalf("outside a repository: %v %v", list, err)
}
if err := os.Mkdir(filepath.Join(root, ".git"), 0o755); err != nil {
t.Fatal(err)
}
if list, err := PredefinedAbove(module); err != nil || list != nil {
t.Fatalf("a repository without the file: %v %v", list, err)
}
if err := os.WriteFile(filepath.Join(root, PredefinedFile), []byte(theFirstKeys), 0o644); err != nil {
t.Fatal(err)
}
list, err := PredefinedAbove(module)
if err != nil || len(list) != 2 {
t.Fatalf("the list above: %v %v", list, err)
}
if err := os.WriteFile(filepath.Join(root, PredefinedFile), []byte(`{"font-size": {}}`), 0o644); err != nil {
t.Fatal(err)
}
if _, err := PredefinedAbove(module); err == nil {
t.Fatal("a wrong list above was read")
}
if err := os.Remove(filepath.Join(root, PredefinedFile)); err != nil {
t.Fatal(err)
}
if err := os.Symlink("/etc/hostname", filepath.Join(root, PredefinedFile)); err == nil {
if _, err := PredefinedAbove(module); err == nil || !strings.Contains(err.Error(), "not a plain file") {
t.Fatalf("a link read as the list: %v", err)
}
}
}