first commit

This commit is contained in:
2026-08-14 23:41:57 +08:00
commit 086803a8dd
471 changed files with 91938 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
package memory
import (
"path/filepath"
"testing"
)
// openTemp opens a Store backed by a temp-file DB and registers cleanup.
func openTemp(t *testing.T) *Store {
t.Helper()
dir := t.TempDir()
dbPath := filepath.Join(dir, "sub", "memory.db") // sub/ must be created by Open
st, err := Open(dbPath, dir, "")
if err != nil {
t.Fatalf("Open: %v", err)
}
t.Cleanup(func() { st.Close() })
return st
}
// tableExists reports whether a table or virtual table with the given name
// exists in sqlite_master.
func tableExists(t *testing.T, st *Store, name string) bool {
t.Helper()
var got string
err := st.DB().QueryRow(
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, name,
).Scan(&got)
if err != nil {
return false
}
return got == name
}
// TestFTS5SmokeTest de-risks the pure-Go SQLite FTS5 dependency: it asserts the
// FTS5 virtual table is created without error and is queryable.
func TestFTS5SmokeTest(t *testing.T) {
st := openTemp(t)
if !tableExists(t, st, "memory_fts") {
t.Fatalf("memory_fts virtual table not created")
}
// A MATCH query must run without error (proves FTS5 is compiled in).
rows, err := st.DB().Query(`SELECT rowid FROM memory_fts WHERE memory_fts MATCH ?`, "anything")
if err != nil {
t.Fatalf("FTS5 MATCH query failed (FTS5 not available?): %v", err)
}
rows.Close()
}
// TestSchemaObjectsCreated verifies the content table, indexes and triggers.
func TestSchemaObjectsCreated(t *testing.T) {
st := openTemp(t)
if !tableExists(t, st, "memory_index") {
t.Fatalf("memory_index table not created")
}
for _, idx := range []string{"memory_index_scope_idx", "memory_index_type_idx"} {
var n int
if err := st.DB().QueryRow(
`SELECT count(*) FROM sqlite_master WHERE type='index' AND name=?`, idx,
).Scan(&n); err != nil || n != 1 {
t.Fatalf("index %q missing (n=%d, err=%v)", idx, n, err)
}
}
for _, trg := range []string{"memory_ai", "memory_ad", "memory_au"} {
var n int
if err := st.DB().QueryRow(
`SELECT count(*) FROM sqlite_master WHERE type='trigger' AND name=?`, trg,
).Scan(&n); err != nil || n != 1 {
t.Fatalf("trigger %q missing (n=%d, err=%v)", trg, n, err)
}
}
}
// insertRow inserts a memory_index row and returns its id.
func insertRow(t *testing.T, st *Store, path, body string) int64 {
t.Helper()
res, err := st.DB().Exec(
`INSERT INTO memory_index (path, scope, scope_id, type, body, fingerprint, last_indexed_at)
VALUES (?, 'global', '', 'free', ?, 'fp', 0)`, path, body)
if err != nil {
t.Fatalf("insert: %v", err)
}
id, err := res.LastInsertId()
if err != nil {
t.Fatalf("last insert id: %v", err)
}
return id
}
// ftsMatchCount returns how many FTS rows match the given single-term query.
func ftsMatchCount(t *testing.T, st *Store, term string) int {
t.Helper()
var n int
if err := st.DB().QueryRow(
`SELECT count(*) FROM memory_fts WHERE memory_fts MATCH ?`, term,
).Scan(&n); err != nil {
t.Fatalf("fts match count %q: %v", term, err)
}
return n
}
// TestTriggerSyncInsertDeleteUpdate verifies the AFTER INSERT/DELETE/UPDATE
// triggers keep memory_fts in sync with memory_index.
func TestTriggerSyncInsertDeleteUpdate(t *testing.T) {
st := openTemp(t)
// INSERT -> searchable.
id := insertRow(t, st, "/mem/a.md", "alpha bravo charlie")
if got := ftsMatchCount(t, st, "bravo"); got != 1 {
t.Fatalf("after insert: MATCH bravo = %d, want 1", got)
}
// UPDATE -> re-synced (old term gone, new term present).
if _, err := st.DB().Exec(`UPDATE memory_index SET body=? WHERE id=?`, "delta echo", id); err != nil {
t.Fatalf("update: %v", err)
}
if got := ftsMatchCount(t, st, "bravo"); got != 0 {
t.Fatalf("after update: MATCH bravo = %d, want 0", got)
}
if got := ftsMatchCount(t, st, "echo"); got != 1 {
t.Fatalf("after update: MATCH echo = %d, want 1", got)
}
// DELETE -> removed from index.
if _, err := st.DB().Exec(`DELETE FROM memory_index WHERE id=?`, id); err != nil {
t.Fatalf("delete: %v", err)
}
if got := ftsMatchCount(t, st, "echo"); got != 0 {
t.Fatalf("after delete: MATCH echo = %d, want 0", got)
}
}
// TestOpenIdempotent verifies that running the migration twice on the same file
// is safe and preserves data.
func TestOpenIdempotent(t *testing.T) {
dir := t.TempDir()
dbPath := filepath.Join(dir, "memory.db")
st1, err := Open(dbPath, dir, "")
if err != nil {
t.Fatalf("first Open: %v", err)
}
insertRow(t, st1, "/mem/keep.md", "persistent needle")
if err := st1.Close(); err != nil {
t.Fatalf("close first: %v", err)
}
// Re-open: migration must not error and existing data must survive.
st2, err := Open(dbPath, dir, "")
if err != nil {
t.Fatalf("second Open (migration not idempotent?): %v", err)
}
t.Cleanup(func() { st2.Close() })
if got := ftsMatchCount(t, st2, "needle"); got != 1 {
t.Fatalf("after reopen: MATCH needle = %d, want 1", got)
}
}
// TestInMemoryOpen verifies the ":memory:" DSN works (no parent dir creation).
func TestInMemoryOpen(t *testing.T) {
st, err := Open(":memory:", "", "")
if err != nil {
t.Fatalf("Open in-memory: %v", err)
}
defer st.Close()
if !tableExists(t, st, "memory_fts") {
t.Fatalf("memory_fts not created in in-memory db")
}
}