2026-06-13 15:29:13 +08:00
|
|
|
|
package session
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/hhs/camtalk/internal/logger"
|
|
|
|
|
|
"github.com/hhs/camtalk/internal/models"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
|
logger.Init("debug", "console")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestCreateAndGet(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
config := models.DefaultConfig()
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, err := m.Create(ctx, "", config)
|
2026-06-13 15:29:13 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("Create: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if id == "" {
|
|
|
|
|
|
t.Fatal("Create returned empty ID")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sess, err := m.Get(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("Get: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if sess.ID != id {
|
|
|
|
|
|
t.Errorf("ID = %q, want %q", sess.ID, id)
|
|
|
|
|
|
}
|
|
|
|
|
|
if sess.Config.Language != "zh-CN" {
|
|
|
|
|
|
t.Errorf("Language = %q, want %q", sess.Config.Language, "zh-CN")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestGetNotFound(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
_, err := m.Get(ctx, "nonexistent")
|
|
|
|
|
|
if err != ErrSessionNotFound {
|
|
|
|
|
|
t.Errorf("Get nonexistent: err = %v, want ErrSessionNotFound", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestExpire(t *testing.T) {
|
|
|
|
|
|
// 使用极短 TTL 测试过期
|
|
|
|
|
|
m := NewMemoryManager(50*time.Millisecond, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 未过期时应能获取
|
|
|
|
|
|
_, err := m.Get(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("Get before expire: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 等待过期
|
|
|
|
|
|
time.Sleep(80 * time.Millisecond)
|
|
|
|
|
|
|
|
|
|
|
|
_, err = m.Get(ctx, id)
|
|
|
|
|
|
if err != ErrSessionNotFound {
|
|
|
|
|
|
t.Errorf("Get after expire: err = %v, want ErrSessionNotFound", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestDestroy(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
if err := m.Destroy(ctx, id); err != nil {
|
|
|
|
|
|
t.Fatalf("Destroy: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_, err := m.Get(ctx, id)
|
|
|
|
|
|
if err != ErrSessionNotFound {
|
|
|
|
|
|
t.Errorf("Get after Destroy: err = %v, want ErrSessionNotFound", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestDestroyNotFound(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
err := m.Destroy(ctx, "nonexistent")
|
|
|
|
|
|
if err != ErrSessionNotFound {
|
|
|
|
|
|
t.Errorf("Destroy nonexistent: err = %v, want ErrSessionNotFound", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestAppendMessageAndGetHistory(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
msgs := []models.Message{
|
|
|
|
|
|
{Role: "user", Content: "你好"},
|
|
|
|
|
|
{Role: "assistant", Content: "你好!有什么可以帮你的吗?"},
|
|
|
|
|
|
{Role: "user", Content: "这是什么?"},
|
|
|
|
|
|
{Role: "assistant", Content: "这是一朵花。"},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, msg := range msgs {
|
|
|
|
|
|
if err := m.AppendMessage(ctx, id, msg); err != nil {
|
|
|
|
|
|
t.Fatalf("AppendMessage: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
history, err := m.GetHistory(ctx, id, 0)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("GetHistory: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(history) != 4 {
|
|
|
|
|
|
t.Fatalf("GetHistory len = %d, want 4", len(history))
|
|
|
|
|
|
}
|
|
|
|
|
|
if history[0].Content != "你好" {
|
|
|
|
|
|
t.Errorf("history[0] = %q, want %q", history[0].Content, "你好")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestGetHistoryLimit(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "msg"})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
history, err := m.GetHistory(ctx, id, 3)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("GetHistory: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(history) != 3 {
|
|
|
|
|
|
t.Fatalf("GetHistory limit=3: len = %d, want 3", len(history))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestHistoryLimit(t *testing.T) {
|
|
|
|
|
|
const maxHistory = 5
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, maxHistory)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 插入超过上限的消息
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
|
m.AppendMessage(ctx, id, models.Message{Role: "user", Content: "msg"})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
history, err := m.GetHistory(ctx, id, 0)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("GetHistory: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(history) != maxHistory {
|
|
|
|
|
|
t.Fatalf("GetHistory after overflow: len = %d, want %d", len(history), maxHistory)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestUpdateConfig(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
ttsEnabled := false
|
|
|
|
|
|
detailLevel := "high"
|
|
|
|
|
|
patch := models.SessionConfigPatch{
|
|
|
|
|
|
TTSEnabled: &ttsEnabled,
|
|
|
|
|
|
DetailLevel: &detailLevel,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := m.UpdateConfig(ctx, id, patch); err != nil {
|
|
|
|
|
|
t.Fatalf("UpdateConfig: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sess, _ := m.Get(ctx, id)
|
|
|
|
|
|
if sess.Config.TTSEnabled != false {
|
|
|
|
|
|
t.Errorf("TTSEnabled = %v, want false", sess.Config.TTSEnabled)
|
|
|
|
|
|
}
|
|
|
|
|
|
if sess.Config.DetailLevel != "high" {
|
|
|
|
|
|
t.Errorf("DetailLevel = %q, want %q", sess.Config.DetailLevel, "high")
|
|
|
|
|
|
}
|
|
|
|
|
|
// Language 未传,应保持原值
|
|
|
|
|
|
if sess.Config.Language != "zh-CN" {
|
|
|
|
|
|
t.Errorf("Language = %q, want %q", sess.Config.Language, "zh-CN")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestActiveRequest(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始应为空
|
|
|
|
|
|
reqID, err := m.GetActiveRequestID(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("GetActiveRequestID: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqID != "" {
|
|
|
|
|
|
t.Errorf("initial active request = %q, want empty", reqID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置
|
|
|
|
|
|
if err := m.SetActiveRequest(ctx, id, "req-123"); err != nil {
|
|
|
|
|
|
t.Fatalf("SetActiveRequest: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
reqID, _ = m.GetActiveRequestID(ctx, id)
|
|
|
|
|
|
if reqID != "req-123" {
|
|
|
|
|
|
t.Errorf("active request = %q, want %q", reqID, "req-123")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清除
|
|
|
|
|
|
if err := m.ClearActiveRequest(ctx, id); err != nil {
|
|
|
|
|
|
t.Fatalf("ClearActiveRequest: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
reqID, _ = m.GetActiveRequestID(ctx, id)
|
|
|
|
|
|
if reqID != "" {
|
|
|
|
|
|
t.Errorf("active request after clear = %q, want empty", reqID)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestTouchRefreshesTTL(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(100*time.Millisecond, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
id, _ := m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 50ms 后 Touch,应重置 TTL
|
|
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
|
|
if err := m.Touch(ctx, id); err != nil {
|
|
|
|
|
|
t.Fatalf("Touch: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 再等 70ms(距创建 120ms,但距 Touch 只有 70ms),不应过期
|
|
|
|
|
|
time.Sleep(70 * time.Millisecond)
|
|
|
|
|
|
_, err := m.Get(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Errorf("Get after Touch: %v, want nil (should not expire yet)", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 再等 50ms(距 Touch 120ms),应过期
|
|
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
|
|
_, err = m.Get(ctx, id)
|
|
|
|
|
|
if err != ErrSessionNotFound {
|
|
|
|
|
|
t.Errorf("Get after TTL: err = %v, want ErrSessionNotFound", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestActiveCount(t *testing.T) {
|
|
|
|
|
|
m := NewMemoryManager(30*time.Minute, 20)
|
|
|
|
|
|
defer m.Stop()
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
if m.ActiveCount() != 0 {
|
|
|
|
|
|
t.Errorf("initial ActiveCount = %d, want 0", m.ActiveCount())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-14 17:35:20 +08:00
|
|
|
|
m.Create(ctx, "", models.DefaultConfig())
|
|
|
|
|
|
m.Create(ctx, "", models.DefaultConfig())
|
2026-06-13 15:29:13 +08:00
|
|
|
|
if m.ActiveCount() != 2 {
|
|
|
|
|
|
t.Errorf("ActiveCount = %d, want 2", m.ActiveCount())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-14 17:36:38 +08:00
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|