- model: Memory struct with categories/decay/keywords - repository: PG CRUD + keyword search + table migration SQL - service: dedup (Jaccard), keyword extraction, decay formula - 7 tests (3 DB integration + 4 unit), 96 total, vet clean
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
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 TestJaccard(t *testing.T) {
|
|
if sim := jaccardSimilarity("用户喜欢喝咖啡", "用户喜欢喝咖啡"); sim != 1.0 {
|
|
t.Errorf("identical: got %.3f", sim)
|
|
}
|
|
if sim := jaccardSimilarity("用户喜欢喝咖啡", "用户喜欢喝茶"); sim < 0.2 {
|
|
t.Errorf("similar: got %.3f", sim)
|
|
}
|
|
if sim := jaccardSimilarity("", ""); sim != 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)
|
|
}
|
|
}
|