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
67 lines
2.5 KiB
Go
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)
|
|
}
|
|
}
|