// reminders_test.go - 系统提醒注入的单元测试. // // 覆盖场景: // - 日期变更检测 // - 文件修改检测 // - 提醒格式化 // - CollectReminders 集成测试 // - 记忆相关性检测 // - extractRecentUserText package engine import ( "context" "os" "path/filepath" "strings" "testing" "time" "git.flytoex.net/yuanwei/flyto-agent/pkg/query" ) // TestFormatReminder 测试提醒文本格式化 func TestFormatReminder(t *testing.T) { text := "Today's date is 2026-04-01." result := FormatReminder(text) if !strings.HasPrefix(result, "") { t.Errorf("提醒应以 开头,实际: %s", result) } if !strings.HasSuffix(result, "") { t.Errorf("提醒应以 结尾,实际: %s", result) } if !strings.Contains(result, text) { t.Errorf("提醒应包含原始文本,实际: %s", result) } } // TestFormatReminder_Empty 测试空文本格式化 func TestFormatReminder_Empty(t *testing.T) { result := FormatReminder("") expected := "\n\n" if result != expected { t.Errorf("空文本格式化: %q, 期望 %q", result, expected) } } // TestCheckDateChange_NoChange 测试日期未变化 func TestCheckDateChange_NoChange(t *testing.T) { rs := NewReminderSystem(nil, nil) // lastDate 初始化为今天,所以不应该触发 result := rs.CheckDateChange() if result != "" { t.Errorf("日期未变化时不应返回提醒,实际: %q", result) } } // TestCheckDateChange_Changed 测试日期变化 func TestCheckDateChange_Changed(t *testing.T) { rs := NewReminderSystem(nil, nil) // 手动设置为昨天 rs.lastDate = time.Now().AddDate(0, 0, -1).Format("2006-01-02") result := rs.CheckDateChange() if result == "" { t.Error("日期变化时应返回提醒") } // 检查包含当前日期 today := time.Now().Format("2006-01-02") if !strings.Contains(result, today) { t.Errorf("日期提醒应包含当前日期 %s,实际: %s", today, result) } // 检查包含 system-reminder 标签 if !strings.Contains(result, "") { t.Errorf("日期提醒应包含 标签,实际: %s", result) } } // TestCheckDateChange_UpdatesLastDate 测试日期变化后更新 lastDate func TestCheckDateChange_UpdatesLastDate(t *testing.T) { rs := NewReminderSystem(nil, nil) rs.lastDate = "2020-01-01" rs.CheckDateChange() today := time.Now().Format("2006-01-02") if rs.lastDate != today { t.Errorf("CheckDateChange 后 lastDate 应更新为 %s,实际: %s", today, rs.lastDate) } } // TestCheckDateChange_SecondCallNoReminder 测试连续调用不重复提醒 func TestCheckDateChange_SecondCallNoReminder(t *testing.T) { rs := NewReminderSystem(nil, nil) rs.lastDate = "2020-01-01" // 第一次应该有提醒 result1 := rs.CheckDateChange() if result1 == "" { t.Error("第一次调用应返回提醒") } // 第二次不应该有提醒(日期已更新) result2 := rs.CheckDateChange() if result2 != "" { t.Errorf("第二次调用不应返回提醒,实际: %q", result2) } } // TestCheckFileModifications_NilCache 测试 fileCache 为 nil 时 func TestCheckFileModifications_NilCache(t *testing.T) { rs := NewReminderSystem(nil, nil) result := rs.CheckFileModifications() if len(result) != 0 { t.Errorf("fileCache 为 nil 时不应返回提醒,实际: %v", result) } } // TestCheckFileModifications_NoFiles 测试无缓存文件时 func TestCheckFileModifications_NoFiles(t *testing.T) { cache := NewFileStateCache(100) rs := NewReminderSystem(cache, nil) result := rs.CheckFileModifications() if len(result) != 0 { t.Errorf("无缓存文件时不应返回提醒,实际: %v", result) } } // TestCheckFileModifications_ModifiedFile 测试文件被外部修改 func TestCheckFileModifications_ModifiedFile(t *testing.T) { // 创建临时文件 tmpDir := t.TempDir() tmpFile := filepath.Join(tmpDir, "test.go") if err := os.WriteFile(tmpFile, []byte("package main"), 0644); err != nil { t.Fatalf("创建临时文件失败: %v", err) } // 记录到缓存 cache := NewFileStateCache(100) cache.Record(tmpFile, []byte("package main")) // 修改文件(更改 mtime) time.Sleep(10 * time.Millisecond) futureTime := time.Now().Add(1 * time.Hour) os.Chtimes(tmpFile, futureTime, futureTime) // 检测修改 rs := NewReminderSystem(cache, nil) result := rs.CheckFileModifications() if len(result) == 0 { t.Error("文件被修改时应返回提醒") } if len(result) > 0 && !strings.Contains(result[0], tmpFile) { t.Errorf("提醒应包含被修改的文件路径 %s,实际: %s", tmpFile, result[0]) } } // TestCheckFileModifications_UnmodifiedFile 测试文件未被修改 func TestCheckFileModifications_UnmodifiedFile(t *testing.T) { // 创建临时文件 tmpDir := t.TempDir() tmpFile := filepath.Join(tmpDir, "test.go") if err := os.WriteFile(tmpFile, []byte("package main"), 0644); err != nil { t.Fatalf("创建临时文件失败: %v", err) } // 记录到缓存 cache := NewFileStateCache(100) cache.Record(tmpFile, []byte("package main")) // 不修改文件,直接检测 rs := NewReminderSystem(cache, nil) result := rs.CheckFileModifications() if len(result) != 0 { t.Errorf("文件未修改时不应返回提醒,实际: %v", result) } } // TestCheckMemoryRelevance_NilStore 测试 memStore 为 nil 时 func TestCheckMemoryRelevance_NilStore(t *testing.T) { rs := NewReminderSystem(nil, nil) result := rs.CheckMemoryRelevance(context.Background(), "test query") if result != "" { t.Errorf("memStore 为 nil 时不应返回提醒,实际: %q", result) } } // TestExtractRecentUserText 测试从消息历史中提取用户文本 func TestExtractRecentUserText(t *testing.T) { messages := []query.Message{ { Role: query.RoleUser, Content: []query.Content{{Type: query.ContentText, Text: "第一条消息"}}, }, { Role: query.RoleAssistant, Content: []query.Content{{Type: query.ContentText, Text: "助手回复"}}, }, { Role: query.RoleUser, Content: []query.Content{{Type: query.ContentText, Text: "最后一条消息"}}, }, } result := extractRecentUserText(messages) if result != "最后一条消息" { t.Errorf("应提取最后一条用户消息,实际: %q", result) } } // TestExtractRecentUserText_Empty 测试空消息历史 func TestExtractRecentUserText_Empty(t *testing.T) { result := extractRecentUserText(nil) if result != "" { t.Errorf("空消息历史应返回空字符串,实际: %q", result) } } // TestExtractRecentUserText_NoUserMessage 测试没有用户消息 func TestExtractRecentUserText_NoUserMessage(t *testing.T) { messages := []query.Message{ { Role: query.RoleAssistant, Content: []query.Content{{Type: query.ContentText, Text: "助手回复"}}, }, } result := extractRecentUserText(messages) if result != "" { t.Errorf("无用户消息时应返回空字符串,实际: %q", result) } } // TestCollectReminders_Basic 测试 CollectReminders 基础功能 func TestCollectReminders_Basic(t *testing.T) { rs := NewReminderSystem(nil, nil) // 设置为昨天的日期以触发日期变更 rs.lastDate = "2020-01-01" messages := []query.Message{ { Role: query.RoleUser, Content: []query.Content{{Type: query.ContentText, Text: "hello"}}, }, } reminders := rs.CollectReminders(context.Background(), messages, 1) if len(reminders) == 0 { t.Error("应该至少有日期变更提醒") } // 检查所有提醒都包含 system-reminder 标签 for i, r := range reminders { if !strings.Contains(r, "") { t.Errorf("提醒 %d 应包含 标签: %s", i, r) } } } // TestCollectReminders_NoReminders 测试没有任何提醒的情况 func TestCollectReminders_NoReminders(t *testing.T) { rs := NewReminderSystem(nil, nil) // lastDate 为今天,不会触发日期变更 reminders := rs.CollectReminders(context.Background(), nil, 1) if len(reminders) != 0 { t.Errorf("不应有任何提醒,实际: %v", reminders) } } // TestNewReminderSystem 测试创建提醒系统 func TestNewReminderSystem(t *testing.T) { cache := NewFileStateCache(100) rs := NewReminderSystem(cache, nil) if rs == nil { t.Fatal("NewReminderSystem 不应返回 nil") } if rs.fileCache != cache { t.Error("fileCache 未正确设置") } if rs.memStore != nil { t.Error("memStore 应为 nil") } today := time.Now().Format("2006-01-02") if rs.lastDate != today { t.Errorf("lastDate 应为今天 %s,实际: %s", today, rs.lastDate) } }