package store import ( "context" "path/filepath" "strings" "testing" "github.com/kami/maven/internal/memory" ) func newMemTestStore(t *testing.T) *Store { t.Helper() path := filepath.Join(t.TempDir(), "mem_test.db") st, err := Open(context.Background(), path) if err != nil { t.Fatalf("Open: %v", err) } t.Cleanup(func() { _ = st.Close() }) return st } func TestMemoryStoreInsertSearch(t *testing.T) { ctx := context.Background() m := newMemTestStore(t).VectorMemory() // three orthonormal-ish vectors; a query aligned with the second must rank it top. if err := m.Insert(ctx, "a", []float32{1, 0, 0}, map[string]string{"text": "вода"}); err != nil { t.Fatal(err) } if err := m.Insert(ctx, "b", []float32{0, 1, 0}, map[string]string{"text": "сон", "type": "fact"}); err != nil { t.Fatal(err) } if err := m.Insert(ctx, "c", []float32{0, 0, 1}, map[string]string{"text": "еда"}); err != nil { t.Fatal(err) } got, err := m.Search(ctx, []float32{0, 1, 0}, 2) if err != nil { t.Fatalf("Search: %v", err) } if len(got) != 2 { t.Fatalf("topK=2 returned %d results", len(got)) } if got[0].ID != "b" { t.Errorf("top hit = %q, want b", got[0].ID) } if got[0].Meta["text"] != "сон" || got[0].Meta["type"] != "fact" { t.Errorf("meta not round-tripped: %v", got[0].Meta) } if got[0].Score < 0.99 { t.Errorf("aligned vector score = %v, want ~1.0", got[0].Score) } } func TestMemoryStoreUpsertReplaces(t *testing.T) { ctx := context.Background() m := newMemTestStore(t).VectorMemory() if err := m.Insert(ctx, "x", []float32{1, 0}, map[string]string{"text": "старое"}); err != nil { t.Fatal(err) } if err := m.Insert(ctx, "x", []float32{0, 1}, map[string]string{"text": "новое"}); err != nil { t.Fatal(err) } got, err := m.Search(ctx, []float32{0, 1}, 10) if err != nil { t.Fatal(err) } if len(got) != 1 { t.Fatalf("re-inserting the same id produced %d rows, want 1 (upsert)", len(got)) } if got[0].Meta["text"] != "новое" { t.Errorf("upsert kept the old value: %q", got[0].Meta["text"]) } } func TestMemoryStorePersistsAcrossReopen(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "persist.db") st, err := Open(ctx, path) if err != nil { t.Fatal(err) } if err := st.VectorMemory().Insert(ctx, "k", []float32{1, 0, 0}, map[string]string{"text": "запомни"}); err != nil { t.Fatal(err) } if err := st.Close(); err != nil { t.Fatal(err) } // reopen the same file — the in-memory floor would have lost this. st2, err := Open(ctx, path) if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = st2.Close() }) got, err := st2.VectorMemory().Search(ctx, []float32{1, 0, 0}, 1) if err != nil { t.Fatal(err) } if len(got) != 1 || got[0].Meta["text"] != "запомни" { t.Fatalf("memory did not survive reopen: %v", got) } } // A voiceprint sharing the vector table must never rank as a note hit. The // dimensions match here on purpose: what used to hide these rows was cosine // returning 0 on a width mismatch, which is a property of two model choices and // not of the store. func TestMemoryStoreSearchSkipsVoiceprints(t *testing.T) { ctx := context.Background() m := newMemTestStore(t).VectorMemory() if err := m.Insert(ctx, "note:1", []float32{0, 1, 0}, map[string]string{"text": "заметка"}); err != nil { t.Fatal(err) } if err := m.Insert(ctx, memory.NonRecallPrefix+"kami", []float32{1, 0, 0}, map[string]string{"name": "Ками"}); err != nil { t.Fatal(err) } got, err := m.Search(ctx, []float32{1, 0, 0}, 10) if err != nil { t.Fatalf("Search: %v", err) } for _, r := range got { if strings.HasPrefix(r.ID, memory.NonRecallPrefix) { t.Fatalf("recall returned a voiceprint: %+v", r) } } if len(got) != 1 || got[0].ID != "note:1" { t.Fatalf("Search = %+v, want just the note", got) } // The row is still there for the speaker code that owns it. recs, err := m.ByPrefix(ctx, memory.NonRecallPrefix) if err != nil || len(recs) != 1 { t.Fatalf("ByPrefix = %+v, %v; want the voiceprint", recs, err) } }