Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/wholeset_test.go
T
jochen 8f5e8bcb6e
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-knowledge → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh-knowledge: tell a failed whole-set rule to the operator as a warning naming the records, and leave the module healthy (hq issue 504)
The operator decided on 2026-10-12 that a rule of the record set that fails
is a fault of the records, which their authors repair, not the module
failing: as issue 508 decided for a proposal never reviewed, health says
only whether the module works, and a finding must not hold the module's
own rollouts. The findings go through the notice the knowledge base
already sends (operator-channel.notify, a warning), once per finding,
kept as told across restarts, and cleared when the rule holds again.
2026-10-12 13:25:09 +02:00

193 lines
7.6 KiB
Go

package main
import (
"context"
"errors"
"strings"
"testing"
)
// A decision nothing would cite is not accepted: its review asks changes itself, without a judgement, naming the
// rule cited, and the module stays healthy (novox/hq issue 504). Before, the review approved it, the acceptance
// broke the rule, and the module turned unhealthy for a record its author could only repair after the fact.
func TestADecisionNothingWouldCiteIsNotAccepted(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
n := w.reserved(t, "author@g14")
p := valid(n)
p.Homes = nil
if _, err := w.k.Propose("author@g14", p); err != nil {
t.Fatal(err)
}
judged := len(w.judge.reviews)
w.reviewAll(t)
e := mustGet(t, s, Key{KindDecision, n})
if e.Current.Status != StatusProposed {
t.Fatalf("a decision nothing cites was %s by its review", e.Current.Status)
}
rv := e.Current.Reviews
if len(rv) == 0 || rv[len(rv)-1].Verdict != VerdictChanges || !strings.Contains(rv[len(rv)-1].Findings, "the rule cited") ||
rv[len(rv)-1].By != ReviewBy {
t.Fatalf("its review asked no changes for the rule cited: %+v", rv)
}
if len(w.judge.reviews) != judged {
t.Fatal("a judgement was spent on a record the rule cited refuses")
}
if h := w.k.Health(nil); !h.Healthy {
t.Fatalf("unhealthy for a record not accepted: %s", h.Why)
}
// Named with its home, it is accepted, and the module stays healthy.
p.Homes = []string{testHome}
w.accepted(t, "author@g14", p)
if h := w.k.Health(nil); !h.Healthy {
t.Fatalf("unhealthy after a decision with its home was accepted: %s", h.Why)
}
})
}
// A new decision is cited by a record proposed after it: no record can link a number only reserved. Once the
// citing record is accepted, the new one proposed again is accepted. A correction of the citing record that drops
// the link still leaves the decision cited by nothing, and that is loud: told to the operator once, as a warning
// naming the rule and the record, while the module stays healthy and its rollouts are not held (the operator's
// decision of 2026-10-12 on novox/hq issue 504; to-be 55 §10). A record that cites it clears the notice.
func TestARecordAcceptedCitingANewDecisionLetsItBeAcceptedAndDroppingTheLinkIsLoud(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
n := w.reserved(t, "author@g14")
a := valid(n)
a.Homes = nil
if _, err := w.k.Propose("author@g14", a); err != nil {
t.Fatal(err)
}
w.reviewAll(t)
m := w.reserved(t, "author@g14")
b := valid(m)
b.Links = append(b.Links, LinkAsk{Kind: LinkCites, To: itoa4(n)})
w.accepted(t, "author@g14", b)
w.accepted(t, "author@g14", a) // its revision: an accepted record links it now
if h := w.k.Health(nil); !h.Healthy {
t.Fatalf("unhealthy with both accepted, one citing the other: %s", h.Why)
}
w.k.TellFindings()
if len(w.told) != 0 {
t.Fatalf("told with every rule holding: %v", w.told)
}
// A correction of the citing record drops its link: the rule, run after the acceptance, says so.
b.Links = b.Links[:len(b.Links)-1]
b.Insight = "it cited " + itoaKey(n)
w.accepted(t, "author@g14", b)
h := w.k.Health(nil)
if !h.Healthy || !strings.Contains(h.Why, "the rule cited") || !strings.Contains(h.Why, itoaKey(n)) {
t.Fatalf("a decision left cited by nothing: the health should stay healthy and name it, not %+v", h)
}
w.k.TellFindings()
w.k.TellFindings()
if len(w.told) != 1 || !strings.Contains(w.told[0], "rule cited") || !strings.Contains(w.told[0], itoaKey(n)) ||
!strings.HasPrefix(w.toldKeys[0], findingPrefix) {
t.Fatalf("the operator is not told once, naming the rule and the record: %v %v", w.told, w.toldKeys)
}
// A restart tells nothing again: what was told is kept in the store.
told := w.toldKeys[0]
restarted := newWorld(t, s, fixture())
restarted.clock = w.clock
restarted.k.TellFindings()
if len(restarted.told) != 0 {
t.Fatalf("told again after a restart: %v", restarted.told)
}
w.told = nil
if mustGet(t, s, Key{KindDecision, n}).Current.Status != StatusAccepted {
t.Fatal("the acceptance was undone")
}
// A record that cites it heals it.
k := w.reserved(t, "author@g14")
c := valid(k)
c.Links = append(c.Links, LinkAsk{Kind: LinkCites, To: itoa4(n)})
w.accepted(t, "author@g14", c)
if h := w.k.Health(nil); strings.Contains(h.Why, itoaKey(n)+" is accepted and nothing cites it") {
t.Fatalf("still said after a record cited it: %s", h.Why)
}
w.k.TellFindings()
if len(w.cleared) != 1 || w.cleared[0][0] != told {
t.Fatalf("the notice is not cleared once the rule holds again: %v", w.cleared)
}
w.k.TellFindings()
if len(w.cleared) != 1 || len(w.told) != 0 {
t.Fatalf("after the clear: cleared %v, told %v", w.cleared, w.told)
}
})
}
// Each of the 4 whole-set rules, failing on a record set made to break it.
func TestEachWholeSetRuleFails(t *testing.T) {
acc := func(n int, links ...Link) *Record {
return &Record{Kind: KindDecision, Number: n, Status: StatusAccepted, Links: links,
Clauses: []Clause{{N: 1, Text: "**One.** x", Status: ClauseInForce, Source: SourceWritten}}}
}
to := func(kind string, n, c int) Link {
return Link{Kind: kind, To: Ref{Kind: KindDecision, Number: n, Clause: c}}
}
cases := []struct {
rule string
set []*Record
}{
{"cited", func() []*Record { r := acc(1); r.AcceptedAt = r.AcceptedAt.AddDate(2026, 0, 0); return []*Record{r} }()},
{"supersession", []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }()}},
{"supersession", []*Record{acc(1), acc(2, to(LinkSupersedes, 1, 0))}},
{"partial", []*Record{acc(1), acc(2, to(LinkChanges, 1, 1))}},
{"partial", []*Record{func() *Record { r := acc(1); r.Clauses[0].Status = ClauseChanged; return r }()}},
{"rests-on-live", []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }(),
acc(3, to(LinkSupersedes, 1, 0)), acc(2, to(LinkExtends, 1, 0))}},
}
seen := map[string]bool{}
for _, c := range cases {
found := false
for _, f := range JudgeSet(c.set) {
found = found || f.Rule == c.rule
}
if !found {
t.Errorf("the rule %s did not fail on its case: %+v", c.rule, JudgeSet(c.set))
}
seen[c.rule] = true
}
for _, r := range WholeSetRules {
if !seen[r.ID] {
t.Errorf("the whole-set rule %s has no case", r.ID)
}
}
if len(WholeSetRules) != 4 {
t.Errorf("%d whole-set rules; ADR 0290 counts 4", len(WholeSetRules))
}
// A consistent set: the superseder names it, the extender is the superseder, the change is applied.
ok := []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }(),
acc(2, to(LinkSupersedes, 1, 0), to(LinkExtends, 1, 0))}
if f := JudgeSet(ok); len(f) != 0 {
t.Errorf("a consistent set fails: %+v", f)
}
}
// A finding's notice the operator's channel refused is not kept as told: it is sent again at the next step.
func TestAFindingsNoticeThatFailsIsSentAgain(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := newWorld(t, s, fixture())
f := Finding{Rule: "cited", Records: []string{"ADR 0300"}, Says: "ADR 0300 is accepted and nothing cites it"}
if err := s.SetFindings(context.Background(), []Finding{f}); err != nil {
t.Fatal(err)
}
tries := 0
w.k.Notify = func(key, summary, headline, explanation, resolved string) error {
tries++
if tries == 1 {
return errors.New("nothing answers operator-channel.notify")
}
w.told = append(w.told, summary)
return nil
}
w.k.TellFindings()
w.k.TellFindings()
w.k.TellFindings()
if tries != 2 || len(w.told) != 1 {
t.Fatalf("a refused notice: %d tries, told %v", tries, w.told)
}
})
}