diff --git a/internal/catalogue/no_subjects_test.go b/internal/catalogue/no_subjects_test.go new file mode 100644 index 0000000..f86a8d2 --- /dev/null +++ b/internal/catalogue/no_subjects_test.go @@ -0,0 +1,73 @@ +package catalogue + +import ( + "encoding/json" + "os" + "path/filepath" + "regexp" + "strings" + "testing" +) + +// **A manifest holds no subject** (novox/hq design 29 §1). +// +// A module names its events, tools and seats locally, and the mesh derives where they land. The +// property that buys: reorganise the subject space and every manifest in the catalogue is still +// correct. It holds today by construction — nothing reads a subject from a manifest — and a rule +// held by construction is one a later field breaks quietly, with the symptom appearing as a +// permission that does not match a subject rather than as a manifest that was wrong. +func TestNoManifestContainsASubject(t *testing.T) { + root := filepath.Join("..", "..", "..", "mesh-catalog", "modules") + entries, err := os.ReadDir(root) + if err != nil { + t.Skipf("catalogue sibling not present: %v", err) + } + + // Anything in the mesh's own subject space, and anything shaped like a wire address. + subject := regexp.MustCompile(`^(mesh|\$JS)\.[a-zA-Z0-9_*>.-]+$`) + + var found []string + var walk func(module string, path string, v any) + walk = func(module, path string, v any) { + switch t := v.(type) { + case string: + if subject.MatchString(t) { + found = append(found, module+" "+path+" = "+t) + } + case map[string]any: + for k, inner := range t { + walk(module, path+"."+k, inner) + } + case []any: + for _, inner := range t { + walk(module, path+"[]", inner) + } + } + } + + checked := 0 + for _, e := range entries { + if !e.IsDir() { + continue + } + raw, err := os.ReadFile(filepath.Join(root, e.Name(), "module.json")) + if err != nil { + continue + } + var m any + if err := json.Unmarshal(raw, &m); err != nil { + t.Errorf("%s: %v", e.Name(), err) + continue + } + checked++ + walk(e.Name(), "", m) + } + if checked == 0 { + t.Skip("no manifests read") + } + if len(found) > 0 { + t.Errorf("a manifest names a subject, so reorganising the subject space would mean "+ + "editing the catalogue:\n %s", strings.Join(found, "\n ")) + } + t.Logf("%d manifests hold no subject", checked) +}