package main import ( "encoding/json" "os" "path/filepath" "reflect" "slices" "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"` Template string `json:"template"` } `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, EventMilestoneSet, EventMilestoneMoved}) { 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), and the milestones (issue 1781) a short history each, their trail beside their events. if !reflect.DeepEqual(m.Logs, []string{ChangesLog}) { t.Fatalf("logs %v", m.Logs) } d := m.stateDeclarations(t) if len(d) != 5 || d[4].Name != MilestonesState || d[4].History < 2 || d[4].TTLSeconds != 0 || 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 delivery's transitions are heard from the gate alone (novox/hq ADR 0450 §8, issues 1350 and 1406): today's module is // forgotten, so nothing says mesh-delivery.transition. The subscriptions are the same list. if !reflect.DeepEqual(transitionEvents, []string{"mesh-delivery-gate.transition"}) { t.Fatalf("the triage subscribes to %v", transitionEvents) } // 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-gate.transition", DeliveredEvent}) || !reflect.DeepEqual(m.Invokes, []string{"claude-code.claude_code_judge", "seat:mesh-delivery.checks", "seat:operator-channel.notify", "gitea.gitea_pull_reviews", "gitea.gitea_contains", "gitea.gitea_get_pull_request", "records.records_status", "records.records_list", "records.records_read", "mesh-knowledge.knowledge_highest"}) { t.Fatalf("consumes %v, invokes %v", m.Consumes, m.Invokes) } // The tools bundle, and the archive the build packs files/ into for the templates (novox/hq ADR 0349 §2). if len(m.Build.Artifacts) != 2 || m.Build.Artifacts[0].From != "cmd/mesh-issues" || m.Build.Artifacts[1].From != "files" || 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 = templateText(t, map[string]any{"id": r.ID, "template": r.Template}) } } 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") } // 1133/AC1: the repository mesh-delivery is registered, so an issue can be located in it. for _, repo := range []string{"hq", "mesh-catalog", "mesh-controller", "mesh-delivery", "mesh-host", "mesh-sdk", "mesh-tools"} { if Owners(s.Owners).owns(repo) != repo { t.Errorf("%s is not registered", repo) } } modules := filepath.Join("..", "..", "..") // retired are modules the catalogue no longer holds that stay registered, because issues are located in them // (novox/hq ADR 0454, issue 1351: today's delivery module, removed after the gate took the seat). retired := []string{"mesh-catalog modules/mesh-delivery"} for _, o := range s.Owners { if slices.Contains(retired, o) { continue } 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) } } } // The owners the tracker runs with are the manifest's default (no layer sets them), and the template's copy is // what the test above reads: the two are one list, or that test proves a list nobody runs (novox/hq issue 1133). // 1133/AC2 func TestTheOwnersDefaultIsTheTemplatesOwners(t *testing.T) { raw, err := os.ReadFile(filepath.Join("..", "..", "module.json")) if err != nil { t.Fatal(err) } var m struct { Config struct { Owners struct { Default []string `json:"default"` } `json:"owners"` } `json:"config"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } tmpl, err := os.ReadFile(filepath.Join("..", "..", "files", "settings.json")) if err != nil { t.Fatal(err) } var s Settings if err := json.Unmarshal(tmpl, &s); err != nil { t.Fatal(err) } if len(m.Config.Owners.Default) == 0 || !reflect.DeepEqual(m.Config.Owners.Default, s.Owners) { t.Fatalf("the owners default (%d) and the template's owners (%d) differ", len(m.Config.Owners.Default), len(s.Owners)) } } // Every module folder of the catalogue that holds code the mesh builds (a go.mod or a package.json) is a // registered owner, so an issue is located where it lives and the next new module cannot be missed // (novox/hq issue 1815: mesh-build-go and mesh-build-node were not on the list). // 1815/AC1 func TestEveryCodeModuleOfTheCatalogueIsAnOwner(t *testing.T) { _, _ = readManifest(t) tmpl, err := os.ReadFile(filepath.Join("..", "..", "files", "settings.json")) if err != nil { t.Fatal(err) } var s Settings if err := json.Unmarshal(tmpl, &s); err != nil { t.Fatal(err) } modules := filepath.Join("..", "..", "..") dirs, err := os.ReadDir(modules) if err != nil { t.Fatal(err) } found := 0 for _, d := range dirs { if !d.IsDir() { continue } for _, marker := range []string{"go.mod", "package.json"} { if _, err := os.Stat(filepath.Join(modules, d.Name(), marker)); err != nil { continue } found++ if !slices.Contains(s.Owners, "mesh-catalog modules/"+d.Name()) { t.Errorf("modules/%s holds code (%s) but %q is not among the owners: add it to module.json and files/settings.json", d.Name(), marker, "mesh-catalog modules/"+d.Name()) } break } } if found < 10 { t.Fatalf("only %d code modules found under %s: the test looks in the wrong place", found, modules) } // 1815/AC2: the delivery repository's modules cannot ride on its repository entry (owns refuses a folder // of a repository that is registered only as a whole), so each is named. for _, m := range []string{"builder", "checker", "indexer"} { if Owners(s.Owners).owns("mesh-delivery modules/mesh-delivery-"+m) == "" { t.Errorf("mesh-delivery-%s is not an owner", m) } } }