// 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)
}
}