package memory import ( "context" "fmt" "math" "testing" "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/db" ) func setupTestDB(t *testing.T) (*db.Config, string) { t.Helper() cfg := db.DefaultConfig() cfg.Host = "localhost" cfg.Port = 5432 cfg.User = "cyrene" cfg.Password = "cyrene_pass" cfg.DBName = "cyrene_ai" cfg.SSLMode = "disable" return &cfg, fmt.Sprintf("test-%s-%d", t.Name(), time.Now().UnixNano()%100000) } func TestRepoCreateAndGet(t *testing.T) { cfg, uid := setupTestDB(t) database, err := db.Connect(*cfg) if err != nil { t.Skipf("db not available: %v", err) } defer database.Close() database.Exec(CreateTableSQL()) repo := NewRepository(database) defer database.Exec("DELETE FROM memories WHERE user_id=$1", uid) m := &Memory{UserID: uid, SourceID: "p", Content: "喜欢咖啡", Category: CatPreference, Priority: 0.8, Decay: 1.0} id, err := repo.Create(context.Background(), m) if err != nil { t.Fatalf("Create: %v", err) } if id == "" { t.Fatal("empty ID") } got, err := repo.GetByID(context.Background(), id) if err != nil { t.Fatalf("GetByID: %v", err) } if got.Content != "喜欢咖啡" { t.Errorf("got %q", got.Content) } } func TestRepoListByUser(t *testing.T) { cfg, uid := setupTestDB(t) database, err := db.Connect(*cfg) if err != nil { t.Skipf("db not available: %v", err) } defer database.Close() database.Exec(CreateTableSQL()) repo := NewRepository(database) defer database.Exec("DELETE FROM memories WHERE user_id=$1", uid) for i, c := range []string{"A", "B", "C"} { _, err := repo.Create(context.Background(), &Memory{UserID: uid, Content: c, Decay: 1.0}) if err != nil { t.Fatalf("Create %d: %v", i, err) } } memories, err := repo.ListByUser(context.Background(), uid, 10) if err != nil { t.Fatalf("ListByUser: %v", err) } if len(memories) < 3 { t.Errorf("expected >=3, got %d", len(memories)) } } func TestRepoDelete(t *testing.T) { cfg, uid := setupTestDB(t) database, err := db.Connect(*cfg) if err != nil { t.Skipf("db not available: %v", err) } defer database.Close() database.Exec(CreateTableSQL()) repo := NewRepository(database) id, err := repo.Create(context.Background(), &Memory{UserID: uid, Content: "del", Decay: 1.0}) if err != nil { t.Fatalf("Create: %v", err) } if err := repo.Delete(context.Background(), id); err != nil { t.Fatalf("Delete: %v", err) } _, err = repo.GetByID(context.Background(), id) if err == nil { t.Error("should not exist after delete") } } func TestJaccardSimilarity(t *testing.T) { if sim := jaccardSimilarity("用户喜欢喝咖啡", "用户喜欢喝咖啡"); sim < 0.9 { t.Errorf("identical: got %.3f", sim) } if sim := jaccardSimilarity("用户喜欢喝咖啡", "用户喜欢喝茶"); sim < 0.2 { t.Errorf("similar: got %.3f", sim) } if sim := jaccardSimilarity("", ""); sim != 1.0 { t.Errorf("both empty: got %.3f", sim) } } func TestExtractKw(t *testing.T) { kw := extractKeywords("用户喜欢喝咖啡,每天两杯") if len(kw) < 2 { t.Errorf("expected >=2 keywords, got %v", kw) } } func TestDecayCalc(t *testing.T) { m := &Memory{Decay: 1.0, LastAcc: time.Now().Add(-100 * 24 * time.Hour)} elapsed := time.Since(m.LastAcc) d := math.Exp(-0.01 * elapsed.Hours() / 24) if d < 0.3 || d > 0.4 { t.Errorf("decay after 100 days: got %.4f, expected ~0.37", d) } } func TestTrunc(t *testing.T) { if got := truncate("hello world", 5); got != "hello..." { t.Errorf("got %q", got) } if got := truncate("hi", 10); got != "hi" { t.Errorf("got %q", got) } }