package conditions import ( "encoding/json" "strings" "testing" ) // **The plain rule** (novox/hq ADR 0253): what the operator reads names no identifier, no code and no time // of its own; the words conditions are made of pass. func TestThePlainRuleRefusesIdentifiersCodeAndTimes(t *testing.T) { refused := map[string]string{ "the walk of novox/mesh-catalog a6385479 waits": "a hash", "plan plan-1791454185265004861 is waiting": "a hash", "see `mesh-delivery.show` for why": "markup", "call mesh-delivery.show for why": "a dotted name", "key plan.x.waiting": "a dotted name", "waited 56m0s": "a duration in code", "since 10:40": "a clock time", "since 2026-10-08": "a clock time", "raised at noon UTC": "a clock time", "start it with plans go --why": "a command's flag", "one line\nand another": "a line break", "the resolver at 10.77.0.1 is silent": "", "": "nothing", "**bold**": "markup", "storage-media.mount failed": "a dotted name", "the store at /var/lib/mesh is full": "", "a commit 0123abc landed": "a hash", } for text, want := range refused { why, ok := Plain(text, "g14") if ok { t.Errorf("%q passed", text) continue } if want != "" && !strings.HasPrefix(why, want) { t.Errorf("%q: refused for %q, want %q", text, why, want) } } for _, text := range []string{ "openrazer delivery waiting to start", "The change to openrazer is merged and built, and has waited 56 minutes for mesh-delivery to let it start.", "3 failed services on shanks", "On shanks, mnt-recalbox (a mount), storage-media (a mount) and greenclip failed.", "Name lookups fail on g14", "Retired data of postgres on anchor waits for app_db", "Push keeps being fixed by hand", } { if why, ok := Plain(text, "g14", "shanks", "anchor"); !ok { t.Errorf("%q refused: %s", text, why) } } } // **A condition carries its plain words**: a producer's own; else its kind's; else its scope's — and the // event says them beside the summary, for every channel. func TestAConditionCarriesItsPlainWords(t *testing.T) { k, _, told, _ := keeper(t) before := Unworded var unworded []string Unworded = func(o Observation, why string) { unworded = append(unworded, why) } t.Cleanup(func() { Unworded = before }) Wording("test-silent", func(o Observation) Words { return Words{Headline: o.Machine + " is not answering", Explanation: "The mesh has not heard from it.", Resolved: o.Machine + " answers again"} }) c, err := k.Observe(t.Context(), Observation{Scope: ScopeMachine, ID: "ace", Kind: "test-silent", Machine: "ace", Severity: Warning, Source: "S1", Summary: "ace has not been heard from since 12:00 UTC (bound 3m0s)"}) if err != nil { t.Fatal(err) } if c.Headline != "ace is not answering" || c.Resolved != "ace answers again" || c.Explanation == "" { t.Fatalf("the kind's words were not kept: %+v", c) } if c.Summary != "ace has not been heard from since 12:00 UTC (bound 3m0s)" { t.Errorf("the summary was changed: %q", c.Summary) } said := settled(t, told, 1) body, _ := json.Marshal(said[0]) for _, field := range []string{`"headline":"ace is not answering"`, `"explanation":"The mesh has not heard from it."`, `"resolved":"ace answers again"`} { if !strings.Contains(string(body), field) { t.Errorf("the event does not carry %s: %s", field, body) } } // A producer's own words win; a resolved line it leaves out is made from its headline. c, _ = k.Observe(t.Context(), Observation{Scope: ScopeModule, ID: "openrazer.g14", Kind: "test-silent", Machine: "g14", Severity: Warning, Source: "health", Summary: "openrazer on g14 is not healthy", Headline: "openrazer not working on g14", Explanation: "Its service stopped with an error."}) if c.Headline != "openrazer not working on g14" || c.Resolved != "Resolved: openrazer not working on g14" { t.Errorf("the producer's words: %+v", c) } if len(unworded) != 0 { t.Errorf("words that were plain were reported: %v", unworded) } // Words that are not plain, and a kind with none, are said from the scope — and reported. c, _ = k.Observe(t.Context(), Observation{Scope: ScopeModule, ID: "openrazer.g14", Token: "x", Kind: "test-unworded", Machine: "g14", Severity: Warning, Source: "health", Summary: "openrazer on g14 is not healthy"}) if c.Headline != "Openrazer on g14 needs a look" || !strings.Contains(c.Explanation, "Nothing to do yet") { t.Errorf("the scope's words: %+v", c) } c, _ = k.Observe(t.Context(), Observation{Scope: ScopePlan, ID: "plan-1791454185265004861", Kind: "test-silent", Severity: Warning, Source: "S16", Summary: "the walk waits", Headline: "plan-1791454185265004861 waits"}) if strings.Contains(c.Headline, "plan-") || c.Headline != "A walk needs a look" { t.Errorf("an id reached the headline: %+v", c) } if len(unworded) != 2 || !strings.Contains(unworded[0], "no plain words") || !strings.Contains(unworded[1], "not plain") { t.Errorf("what was said in borrowed words was not reported: %v", unworded) } } // **A healer that gave up says so in the explanation**: the words of the kind said "nothing to do"; once the // budget is spent, the operator is needed. func TestAnEscalatedConditionSaysItNeedsTheOperator(t *testing.T) { k, _, _, _ := keeper(t) ctx := t.Context() o := Observation{Scope: ScopeMachine, ID: "ace", Kind: "silent", Machine: "ace", Severity: Warning, Source: "S1", Summary: "ace is silent", Headline: "ace is not answering", Explanation: "Nothing to do yet."} if _, err := k.Observe(ctx, o); err != nil { t.Fatal(err) } c, _, err := k.Escalate(ctx, o.Key(), Attempt{What: "budget spent", Outcome: "escalated", By: "healer H1"}) if err != nil { t.Fatal(err) } if !strings.HasSuffix(c.Explanation, Escalated) { t.Fatalf("escalated: %q", c.Explanation) } c, _ = k.Observe(ctx, o) if strings.Count(c.Explanation, Escalated) != 1 { t.Errorf("seen again after escalation: %q", c.Explanation) } }