Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/index_test.go
T
jschoubben a4e3a9cbe0 CAT-K8: the search index is in the module's memory (bleve), built from the ledger and kept as it writes
ask and holds on the ledger answer from a full-text index of the heads and the current versions' text and clauses; it is
built at start (index not built is the answer meanwhile), a new version replaces its record's documents, a reserved,
withdrawn or retracted record has none. A new pure Go dependency, bleve.

Issue 1966.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:12:44 +01:00

67 lines
2.5 KiB
Go

package main
import (
"errors"
"testing"
)
// The search index (to-be 55 §1, AC52): built from the ledger at start, refusing "index not built" meanwhile, kept current
// as the module writes, and answering the questions the knowledge base is asked as the in-memory ranking does: the records
// the questions are about come first.
func TestTheSearchIndexAnswersTheQuestionsAsTheStoreDoes(t *testing.T) {
f := newFakeLedger(t)
ls := newLedgerStore(f, newFakeStates())
w := newWorld(t, ls, fixture())
w.k.States = newFakeStates()
ls.indexAsync = true
if _, err := ls.Search(t.Context(), "merge window", 5); !errors.Is(err, ErrIndexNotBuilt) {
t.Fatalf("a question before the index was built: %v", err)
}
if _, err := w.k.Import("import@test", ImportAsk{}, fixture()); err != nil {
t.Fatal(err)
}
if err := ls.BuildIndex(t.Context()); err != nil {
t.Fatal(err)
}
if st := ls.IndexState(); !st.Built || st.Docs < 20 {
t.Fatalf("%+v", st)
}
mem := imported(t, newMemStore())
for _, q := range []string{"merge window", "how is the tool runtime chosen", "what holds about resolved names", "a knowledge base the mesh writes"} {
want, _ := mem.k.Store.Search(t.Context(), q, 3)
got, err := ls.Search(t.Context(), q, 8)
if err != nil {
t.Fatal(err)
}
// The ranking differs (the index weighs by how rare a word is), so the record the store puts first is among the
// first few of the index's.
found := false
for _, h := range got {
found = found || (len(want) > 0 && h.Key == want[0].Key)
}
if len(want) > 0 && !found {
t.Errorf("%q: the store puts %v first, the index %v", q, want[0], got)
}
}
// A new version is in the index at once; a withdrawn record is out of it.
r, _ := w.k.ReserveNumber("author@g14", KindDecision, "a test record")
if hits, _ := ls.Search(t.Context(), "zebra quantum", 5); len(hits) != 0 {
t.Fatalf("%v", hits)
}
p := valid(r.Number)
p.Title = "Zebra quantum processors"
p.Sections[2].Text = "A zebra quantum processor."
if _, err := w.k.Propose("author@g14", p); err != nil {
t.Fatal(err)
}
if hits, _ := ls.Search(t.Context(), "zebra quantum", 5); len(hits) == 0 || hits[0].Key.Number != r.Number {
t.Fatalf("a proposed record is not in the index: %v", hits)
}
if _, err := w.k.WithdrawRecord("author@g14", Key{KindDecision, r.Number}, "not needed"); err != nil {
t.Fatal(err)
}
if hits, _ := ls.Search(t.Context(), "zebra quantum", 5); len(hits) != 0 {
t.Fatalf("a withdrawn record is in the index: %v", hits)
}
}