feat: 构建用户模块,实现用户对话历史持久化,完善接口文档 #96
@@ -284,3 +284,198 @@ func TestActiveCount(t *testing.T) {
|
|||||||
t.Errorf("ActiveCount = %d, want 2", m.ActiveCount())
|
t.Errorf("ActiveCount = %d, want 2", m.ActiveCount())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateWithUserID(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
id, err := m.Create(ctx, "user-123", models.DefaultConfig())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Create: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sess, err := m.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Get: %v", err)
|
||||||
|
}
|
||||||
|
if sess.UserID != "user-123" {
|
||||||
|
t.Errorf("UserID = %q, want %q", sess.UserID, "user-123")
|
||||||
|
}
|
||||||
|
if sess.Title != models.DefaultSessionTitle {
|
||||||
|
t.Errorf("Title = %q, want %q", sess.Title, models.DefaultSessionTitle)
|
||||||
|
}
|
||||||
|
if sess.UpdatedAt.IsZero() {
|
||||||
|
t.Error("UpdatedAt should not be zero")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateTitle(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
id, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
|
||||||
|
if err := m.UpdateTitle(ctx, id, "自定义标题"); err != nil {
|
||||||
|
t.Fatalf("UpdateTitle: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sess, _ := m.Get(ctx, id)
|
||||||
|
if sess.Title != "自定义标题" {
|
||||||
|
t.Errorf("Title = %q, want %q", sess.Title, "自定义标题")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateTitleNotFound(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
err := m.UpdateTitle(ctx, "nonexistent", "标题")
|
||||||
|
if err != ErrSessionNotFound {
|
||||||
|
t.Errorf("UpdateTitle nonexistent: err = %v, want ErrSessionNotFound", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoTitleOnFirstMessage(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
id, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
|
||||||
|
// 首条 user 消息应自动更新标题
|
||||||
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "你好世界"})
|
||||||
|
|
||||||
|
sess, _ := m.Get(ctx, id)
|
||||||
|
if sess.Title != "你好世界" {
|
||||||
|
t.Errorf("Title = %q, want %q", sess.Title, "你好世界")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoTitleLongMessage(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
id, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
|
||||||
|
// 超过 20 字符的消息应截断
|
||||||
|
longMsg := "这是一条很长很长很长很长很长很长很长很长的消息"
|
||||||
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: longMsg})
|
||||||
|
|
||||||
|
sess, _ := m.Get(ctx, id)
|
||||||
|
expected := string([]rune(longMsg)[:20]) + "…"
|
||||||
|
if sess.Title != expected {
|
||||||
|
t.Errorf("Title = %q, want %q", sess.Title, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoTitleNotOverwritten(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
id, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
|
||||||
|
// 首条消息设置标题
|
||||||
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "第一条消息"})
|
||||||
|
// 第二条消息不应覆盖已有的标题
|
||||||
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "第二条消息"})
|
||||||
|
|
||||||
|
sess, _ := m.Get(ctx, id)
|
||||||
|
if sess.Title != "第一条消息" {
|
||||||
|
t.Errorf("Title = %q, want %q", sess.Title, "第一条消息")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListByUser(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 创建两个用户的不同会话
|
||||||
|
id1, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
m.AppendMessage(ctx, id1, models.Message{Role: "user", Content: "会话1"})
|
||||||
|
id2, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
m.AppendMessage(ctx, id2, models.Message{Role: "user", Content: "会话2"})
|
||||||
|
m.Create(ctx, "user-2", models.DefaultConfig()) // 其他用户的会话
|
||||||
|
|
||||||
|
list, total, err := m.ListByUser(ctx, "user-1", 1, 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListByUser: %v", err)
|
||||||
|
}
|
||||||
|
if total != 2 {
|
||||||
|
t.Errorf("total = %d, want 2", total)
|
||||||
|
}
|
||||||
|
if len(list) != 2 {
|
||||||
|
t.Fatalf("len = %d, want 2", len(list))
|
||||||
|
}
|
||||||
|
// 按 UpdatedAt 降序,id2 应在前
|
||||||
|
if list[0].ID != id2 {
|
||||||
|
t.Errorf("list[0].ID = %q, want %q", list[0].ID, id2)
|
||||||
|
}
|
||||||
|
if list[0].Title != "会话2" {
|
||||||
|
t.Errorf("list[0].Title = %q, want %q", list[0].Title, "会话2")
|
||||||
|
}
|
||||||
|
if list[0].LastMessage != "会话2" {
|
||||||
|
t.Errorf("list[0].LastMessage = %q, want %q", list[0].LastMessage, "会话2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListByUserPagination(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 创建 5 个会话
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
id, _ := m.Create(ctx, "user-1", models.DefaultConfig())
|
||||||
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "msg"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第 1 页,每页 2 条
|
||||||
|
list, total, _ := m.ListByUser(ctx, "user-1", 1, 2)
|
||||||
|
if total != 5 {
|
||||||
|
t.Errorf("total = %d, want 5", total)
|
||||||
|
}
|
||||||
|
if len(list) != 2 {
|
||||||
|
t.Errorf("page 1 len = %d, want 2", len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第 2 页
|
||||||
|
list, _, _ = m.ListByUser(ctx, "user-1", 2, 2)
|
||||||
|
if len(list) != 2 {
|
||||||
|
t.Errorf("page 2 len = %d, want 2", len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第 3 页(最后一页)
|
||||||
|
list, _, _ = m.ListByUser(ctx, "user-1", 3, 2)
|
||||||
|
if len(list) != 1 {
|
||||||
|
t.Errorf("page 3 len = %d, want 1", len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 超出范围的页
|
||||||
|
list, _, _ = m.ListByUser(ctx, "user-1", 10, 2)
|
||||||
|
if len(list) != 0 {
|
||||||
|
t.Errorf("out of range page len = %d, want 0", len(list))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListByUserEmpty(t *testing.T) {
|
||||||
|
m := NewMemoryManager(30*time.Minute, 20)
|
||||||
|
defer m.Stop()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
list, total, err := m.ListByUser(ctx, "no-such-user", 1, 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListByUser: %v", err)
|
||||||
|
}
|
||||||
|
if total != 0 {
|
||||||
|
t.Errorf("total = %d, want 0", total)
|
||||||
|
}
|
||||||
|
if len(list) != 0 {
|
||||||
|
t.Errorf("len = %d, want 0", len(list))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user