package main import ( "encoding/json" "os" "path/filepath" "reflect" "sort" "strings" "testing" ) // The manifest says what the code does (novox/hq to-be 53 §1): the seat it declares and holds once for the // mesh with the verbs it serves, the events it says, the two states it keeps (made by the controller, never // by this module), and its settings: the first number and the registered owners. type manifest struct { Module string `json:"module"` Seats []seat `json:"seats"` Claims []seat `json:"claims"` Emits []string `json:"emits"` Consumes []string `json:"consumes"` Invokes []string `json:"invokes"` State []json.RawMessage `json:"state"` Logs []string `json:"logs"` Tools []string `json:"tools"` Resources []struct { ID string `json:"id"` Path string `json:"path"` Content string `json:"content"` } `json:"resources"` Build struct { Artifacts []struct { From string `json:"from"` Loads []string `json:"loads"` Env map[string]string `json:"env"` } `json:"artifacts"` } `json:"build"` } type seat struct { Name string `json:"name"` Scope string `json:"scope"` Serves []string `json:"serves"` } type stateDeclaration struct { Name string `json:"name"` History int `json:"history"` TTLSeconds int `json:"ttl-seconds"` } func readManifest(t *testing.T) (manifest, string) { t.Helper() raw, err := os.ReadFile(filepath.Join("..", "..", "module.json")) if err != nil { t.Fatal(err) } var m manifest if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } return m, string(raw) } func (m manifest) stateDeclarations(t *testing.T) []stateDeclaration { t.Helper() var out []stateDeclaration for _, s := range m.State { var d stateDeclaration if json.Unmarshal(s, &d.Name) != nil { if err := json.Unmarshal(s, &d); err != nil { t.Fatal(err) } } out = append(out, d) } return out } func TestItDeclaresAndHoldsTheIssueTrackerSeatOnceForTheMesh(t *testing.T) { m, _ := readManifest(t) want := []seat{{Name: Seat, Scope: "mesh", Serves: Verbs}} if m.Module != "mesh-issues" || !reflect.DeepEqual(m.Seats, want) || !reflect.DeepEqual(m.Claims, want) { t.Fatalf("module %q, seats %+v, claims %+v", m.Module, m.Seats, m.Claims) } if !reflect.DeepEqual(m.Emits, []string{EventOpened, EventMoved, EventNoted, EventLinked, EventImported, EventSet, EventTriaged, EventReady, EventReadyToVerify, EventReserved}) { t.Fatalf("emits %v", m.Emits) } // The record is the log `changes` (ADR 0300 §1). Four states, none expiring: issues the fold of the log (§3), // still keeping its older values until the carry has read them (they go to one value in a follow-up), the // counters one value each, the backlog's pass of the duplicate check one value (ADR 0278 §6), each reserved // number one value (issue 424). if !reflect.DeepEqual(m.Logs, []string{ChangesLog}) { t.Fatalf("logs %v", m.Logs) } d := m.stateDeclarations(t) if len(d) != 4 || d[0].Name != IssuesState || d[0].History < 2 || d[0].TTLSeconds != 0 || d[1].Name != CountersState || d[1].History > 1 || d[1].TTLSeconds != 0 || d[2].Name != GroomingState || d[2].History > 1 || d[2].TTLSeconds != 0 || d[3].Name != ReservationsState || d[3].History > 1 || d[3].TTLSeconds != 0 { t.Fatalf("state %+v", d) } // The duplicate check hears the tracker's own opens and asks the agent module on its node (ADR 0278 §1, §5); // the import and a reservation read the record through the records module (issue 424). if !reflect.DeepEqual(m.Consumes, []string{"mesh-issues." + EventOpened, "mesh-issues." + EventMoved, "mesh-delivery.transition"}) || !reflect.DeepEqual(m.Invokes, []string{"claude-code.claude_code_judge", "seat:mesh-delivery.checks", "seat:operator-channel.notify", "gitea.gitea_pull_reviews", "records.records_status", "records.records_list", "records.records_read", "mesh-knowledge.knowledge_highest"}) { t.Fatalf("consumes %v, invokes %v", m.Consumes, m.Invokes) } if len(m.Build.Artifacts) != 1 || m.Build.Artifacts[0].From != "cmd/mesh-issues" || m.Build.Artifacts[0].Env["MESH_ISSUES_SETTINGS"] == "" || !reflect.DeepEqual(m.Build.Artifacts[0].Loads, []string{"mesh-issues"}) { t.Fatalf("build %+v", m.Build) } } func TestTheToolsAreTheSeatsVerbsAndSayWhatTheyRefuse(t *testing.T) { m, _ := readManifest(t) var verbs []string for _, tool := range Tools(&Tracker{}) { s, verb, ok := strings.Cut(tool.Name, ".") if !ok || s != Seat { t.Errorf("%s is not a verb of %s", tool.Name, Seat) } verbs = append(verbs, verb) if len(tool.Description) < 80 { t.Errorf("%s says too little of what it does", tool.Name) } for _, internal := range []string{"ADR", "to-be", "bucket", "compare-and-set", "JetStream", "KV"} { if strings.Contains(tool.Description, internal) { t.Errorf("%s's description uses the internal name %q", tool.Name, internal) } } } if !reflect.DeepEqual(verbs, m.Claims[0].Serves) || len(m.Tools) != 0 { t.Fatalf("verbs %v, claim %v, own tools %v", verbs, m.Claims[0].Serves, m.Tools) } } // The settings the manifest starts with: the first number, and a registered list of owners in which every // module named is one the catalogue holds. func TestTheSettingsStartTheCounterAndRegisterTheOwners(t *testing.T) { m, raw := readManifest(t) var content string for _, r := range m.Resources { if r.ID == "settings" { content = r.Content } } var s Settings if err := json.Unmarshal([]byte(content), &s); err != nil { t.Fatalf("the settings the manifest writes are not the module's: %v", err) } if s.FirstNumber < 365 { t.Fatalf("first-number %d: the first issue continues hq's numbering, after 364", s.FirstNumber) } if !sort.StringsAreSorted(s.Owners) { t.Error("the owners are not sorted") } for _, repo := range []string{"hq", "mesh-catalog", "mesh-controller", "mesh-host", "mesh-sdk", "mesh-tools"} { if Owners(s.Owners).owns(repo) != repo { t.Errorf("%s is not registered", repo) } } modules := filepath.Join("..", "..", "..") for _, o := range s.Owners { if name, ok := strings.CutPrefix(o, "mesh-catalog modules/"); ok { if _, err := os.Stat(filepath.Join(modules, name, "module.json")); err != nil { t.Errorf("the owner %q names a module the catalogue does not hold", o) } } } for _, never := range []string{"/home/", "jochen", "shanks", "novox.be"} { if strings.Contains(strings.ToLower(raw), never) { t.Errorf("module.json names %q", never) } } }