package main import ( "encoding/json" "os" "path/filepath" "strings" "testing" ) // The module's directory, where its manifest and its files/ are. const moduleDir = "../.." // templateText is the text a file resource places on the machine: its template's, in the module's files/ // (novox/hq ADR 0349 §2). A resource that names no template, or a template that is not there, fails the test. func templateText(t *testing.T, r map[string]any) string { t.Helper() name, _ := r["template"].(string) if name == "" { t.Fatalf("resource %v names no template", r["id"]) } raw, err := os.ReadFile(filepath.Join(moduleDir, filepath.FromSlash(name))) if err != nil { t.Fatalf("resource %v names %s: %v", r["id"], name, err) } return string(raw) } // No file resource carries its text inline: each names its template in files/, and the build packs files/ // into the one archive the controller reads the templates back from (novox/hq ADR 0349 §2). func TestEveryFileTakesItsTextFromATemplate(t *testing.T) { raw, err := os.ReadFile(filepath.Join(moduleDir, "module.json")) if err != nil { t.Fatal(err) } var m struct { Resources []map[string]any `json:"resources"` Build struct { Artifacts []map[string]any `json:"artifacts"` } `json:"build"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } for _, r := range m.Resources { if _, inline := r["content"]; inline { t.Errorf("resource %v keeps its text inline: keep it in files/ and name it as the template", r["id"]) } if r["type"] == "file" { templateText(t, r) } } packs := 0 for _, a := range m.Build.Artifacts { if a["kind"] == "archive" && a["from"] == "files" { packs++ if a["name"] != "files" { t.Errorf("the templates archive is named %v, not files", a["name"]) } } } if packs != 1 { t.Errorf("the build packs files/ into %d archives, not one", packs) } } // Design 47 G §561, A-155: the put-off wait is a bound key of a state of the module's own with a key-ttl, the finding backup-waits // goes through the findings template and its events, the setting put-off-bound is a file the night reads, and the module hears // the findings asked. func TestThePutOffBoundAndItsStatesAreInTheManifest(t *testing.T) { raw, err := os.ReadFile(filepath.Join(moduleDir, "module.json")) if err != nil { t.Fatal(err) } var m struct { Consumes []string `json:"consumes"` Requires []struct { Name, Template string Options map[string]any } `json:"requires"` Config map[string]map[string]any `json:"config"` Resources []map[string]any `json:"resources"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } got := map[string]bool{} for _, r := range m.Requires { got[r.Name+"/"+r.Template] = true if r.Name == "put-off" && r.Options["key-ttl"] != true { t.Errorf("the state put-off has no key-ttl: %v", r.Options) } } for _, want := range []string{"put-off/state", "findings/state", "finding-events/events"} { if !got[want] { t.Errorf("the manifest requires no %s: %v", want, got) } } if m.Config["put-off-bound"]["default"] != float64(60) { t.Errorf("the setting put-off-bound is %v", m.Config["put-off-bound"]) } heard := false for _, c := range m.Consumes { heard = heard || c == "mesh-conditions.findings-asked" } if !heard { t.Error("the module does not hear mesh-conditions.findings-asked") } for _, r := range m.Resources { if r["id"] == "night" { env, _ := r["env"].(map[string]any) if env[PutOffBoundEnv] != "${dir:state}/put-off-bound" { t.Errorf("the night is not given the file of put-off-bound: %v", env) } } if r["id"] == "put-off-bound" && strings.TrimSpace(templateText(t, r)) != "${config:put-off-bound}" { t.Errorf("the file of put-off-bound does not hold its setting") } } }