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 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 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 := decodeWithForTest(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) } }