package memory import ( "database/sql" "os" "path/filepath" "testing" "time" _ "modernc.org/sqlite" ) func testDBPath(t *testing.T) string { dir := t.TempDir() return filepath.Join(dir, "test_memory.db") } func newTestStore(t *testing.T) *Store { t.Helper() dbPath := testDBPath(t) db, err := openDB(dbPath) if err != nil { t.Fatalf("open db: %v", err) } t.Cleanup(func() { db.Close() }) s := &Store{db: db, path: dbPath} if err := s.migrate(); err != nil { t.Fatalf("migrate: %v", err) } return s } func openDB(path string) (*sql.DB, error) { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return nil, err } return sql.Open("sqlite", path) } func TestStoreAndRetrieve(t *testing.T) { s := newTestStore(t) m := &Memory{ Type: TypeFact, Key: "golang_version", Content: "User uses Go 1.24", Source: "conversation", } if err := s.Store(m); err != nil { t.Fatalf("store: %v", err) } if m.ID == "" { t.Fatal("expected ID to be set") } got, err := s.Get(m.ID) if err != nil { t.Fatalf("get: %v", err) } if got.Key != m.Key { t.Errorf("expected key %s, got %s", m.Key, got.Key) } if got.Content != m.Content { t.Errorf("expected content %s, got %s", m.Content, got.Content) } } func TestDelete(t *testing.T) { s := newTestStore(t) m := &Memory{ Type: TypePreference, Key: "editor", Content: "vim", } s.Store(m) if err := s.Delete(m.ID); err != nil { t.Fatalf("delete: %v", err) } _, err := s.Get(m.ID) if err == nil { t.Error("expected error after delete") } } func TestList(t *testing.T) { s := newTestStore(t) for i := 0; i < 5; i++ { s.Store(&Memory{ Type: TypeFact, Key: "fact_" + string(rune('a'+i)), Content: "content", }) } memories, err := s.List(TypeFact, 10, 0) if err != nil { t.Fatalf("list: %v", err) } if len(memories) != 5 { t.Errorf("expected 5 memories, got %d", len(memories)) } } func TestSearch(t *testing.T) { s := newTestStore(t) s.Store(&Memory{Type: TypeFact, Key: "language", Content: "Go is the primary language"}) s.Store(&Memory{Type: TypeFact, Key: "editor", Content: "VSCode is the editor"}) s.Store(&Memory{Type: TypeContext, Key: "project", Content: "Muyue is a Go project"}) results, err := s.Search("Go language", 10) if err != nil { t.Fatalf("search: %v", err) } if len(results) == 0 { t.Error("expected search results") } } func TestRecallPreferences(t *testing.T) { s := newTestStore(t) s.Store(&Memory{Type: TypePreference, Key: "theme", Content: "dark"}) s.Store(&Memory{Type: TypePreference, Key: "lang", Content: "fr"}) s.Store(&Memory{Type: TypeFact, Key: "tool", Content: "go"}) prefs, err := s.RecallPreferences() if err != nil { t.Fatalf("recall preferences: %v", err) } if len(prefs) != 2 { t.Errorf("expected 2 preferences, got %d", len(prefs)) } } func TestRecallRecent(t *testing.T) { s := newTestStore(t) s.Store(&Memory{Type: TypeFact, Key: "old", Content: "old fact"}) recent, err := s.RecallRecent(time.Now().Add(-1*time.Hour), 10) if err != nil { t.Fatalf("recall recent: %v", err) } if len(recent) == 0 { t.Error("expected recent memories") } } func TestStorePreference(t *testing.T) { s := newTestStore(t) if err := s.StorePreference("editor", "vim"); err != nil { t.Fatalf("store preference: %v", err) } prefs, _ := s.RecallPreferences() if len(prefs) != 1 { t.Errorf("expected 1 preference, got %d", len(prefs)) } } func TestCount(t *testing.T) { s := newTestStore(t) s.Store(&Memory{Type: TypeFact, Key: "a", Content: "a"}) s.Store(&Memory{Type: TypeFact, Key: "b", Content: "b"}) count, err := s.Count() if err != nil { t.Fatalf("count: %v", err) } if count != 2 { t.Errorf("expected 2, got %d", count) } }