package context import ( "encoding/json" "testing" ) // TestDefaultCodePolicy_Name 测试策略名称 func TestDefaultCodePolicy_Name(t *testing.T) { p := &DefaultCodePolicy{} if p.Name() != "code" { t.Errorf("Name() = %q, 期望 %q", p.Name(), "code") } } // TestDefaultCodePolicy_PreserveKeywords 测试编程场景的保留关键词 func TestDefaultCodePolicy_PreserveKeywords(t *testing.T) { p := &DefaultCodePolicy{} kw := p.PreserveKeywords() if kw == "" { t.Error("PreserveKeywords 不应为空") } // 应包含编程相关关键词 for _, expected := range []string{"file paths", "function names", "error messages"} { if !contains(kw, expected) { t.Errorf("PreserveKeywords 应包含 %q", expected) } } } // TestDefaultCodePolicy_ScoreMessageImportance 测试消息重要性评分 func TestDefaultCodePolicy_ScoreMessageImportance(t *testing.T) { p := &DefaultCodePolicy{} tests := []struct { name string role string content string minScore float64 maxScore float64 }{ { name: "普通短消息", role: "user", content: "hello world", minScore: 0.1, maxScore: 0.5, }, { name: "含错误信息", role: "assistant", content: "Error: cannot find module 'foo'", minScore: 0.5, maxScore: 1.0, }, { name: "含文件路径", role: "user", content: "please read /src/main.go", minScore: 0.3, maxScore: 0.7, }, { name: "含决策语言", role: "assistant", content: "I decided to use approach A instead of B because of performance", minScore: 0.4, maxScore: 1.0, }, { name: "含错误+决策+文件路径", role: "assistant", content: "Error in /src/main.go: I decided to fix it by approach X", minScore: 0.7, maxScore: 1.0, }, { name: "超长低重要内容", role: "user", content: string(make([]byte, 15000)), minScore: 0.0, maxScore: 0.3, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { score := p.ScoreMessageImportance(tt.role, tt.content) if score < tt.minScore || score > tt.maxScore { t.Errorf("ScoreMessageImportance = %.2f, 期望在 [%.2f, %.2f] 范围内", score, tt.minScore, tt.maxScore) } }) } } // TestDefaultCodePolicy_MaxRecentRoundsToKeep 测试保留轮数 func TestDefaultCodePolicy_MaxRecentRoundsToKeep(t *testing.T) { p := &DefaultCodePolicy{} rounds := p.MaxRecentRoundsToKeep() if rounds < 1 || rounds > 20 { t.Errorf("MaxRecentRoundsToKeep = %d, 应在合理范围内", rounds) } } // TestDefaultCodePolicy_PreprocessForCompaction 测试预处理 func TestDefaultCodePolicy_PreprocessForCompaction(t *testing.T) { p := &DefaultCodePolicy{} // 测试图像块移除 imageBlocks := []map[string]any{ {"type": "image", "source": map[string]any{"data": "base64..."}}, {"type": "text", "text": "hello"}, } imageJSON, _ := json.Marshal(imageBlocks) msgs := []CompactMessage{ {Role: "user", Content: imageJSON}, } result := p.PreprocessForCompaction(msgs) if len(result) != 1 { t.Fatalf("消息数量不应变化: %d", len(result)) } // 解析结果,检查图像块是否被移除 var blocks []map[string]any if err := json.Unmarshal(result[0].Content, &blocks); err != nil { t.Fatalf("解析结果失败: %v", err) } // 图像块应被移除,只剩 text 块 if len(blocks) != 1 { t.Errorf("预处理后应只有 1 个块, 实际 %d", len(blocks)) } // 测试超长工具结果截断 longContent := string(make([]byte, 20000)) toolBlocks := []map[string]any{ {"type": "tool_result", "content": longContent}, } toolJSON, _ := json.Marshal(toolBlocks) msgs2 := []CompactMessage{ {Role: "user", Content: toolJSON}, } result2 := p.PreprocessForCompaction(msgs2) var blocks2 []map[string]any if err := json.Unmarshal(result2[0].Content, &blocks2); err != nil { t.Fatalf("解析结果失败: %v", err) } if len(blocks2) != 1 { t.Fatalf("块数量不应变化: %d", len(blocks2)) } content2, _ := blocks2[0]["content"].(string) if len(content2) >= 20000 { t.Error("超长工具结果应被截断") } } // TestContainsErrorPattern 测试错误模式检测 func TestContainsErrorPattern(t *testing.T) { tests := []struct { content string want bool }{ {"Error: something went wrong", true}, {"failed to compile", true}, {"panic: runtime error", true}, {"hello world", false}, {"Cannot find file", true}, {"unable to connect", true}, } for _, tt := range tests { got := containsErrorPattern(tt.content) if got != tt.want { t.Errorf("containsErrorPattern(%q) = %v, 期望 %v", tt.content, got, tt.want) } } } // TestContainsDecisionLanguage 测试决策语言检测 func TestContainsDecisionLanguage(t *testing.T) { tests := []struct { content string want bool }{ {"I decided to use X", true}, {"the approach is better", true}, {"instead of using Y", true}, {"hello world", false}, {"the trade-off is clear", true}, } for _, tt := range tests { got := containsDecisionLanguage(tt.content) if got != tt.want { t.Errorf("containsDecisionLanguage(%q) = %v, 期望 %v", tt.content, got, tt.want) } } } // TestClamp 测试值范围限制 func TestClamp(t *testing.T) { tests := []struct { v, min, max, want float64 }{ {0.5, 0.0, 1.0, 0.5}, {-0.1, 0.0, 1.0, 0.0}, {1.5, 0.0, 1.0, 1.0}, {0.0, 0.0, 1.0, 0.0}, {1.0, 0.0, 1.0, 1.0}, } for _, tt := range tests { got := clamp(tt.v, tt.min, tt.max) if got != tt.want { t.Errorf("clamp(%.1f, %.1f, %.1f) = %.1f, 期望 %.1f", tt.v, tt.min, tt.max, got, tt.want) } } } func contains(s, substr string) bool { return len(s) >= len(substr) && containsStr(s, substr) } func containsStr(s, sub string) bool { for i := 0; i <= len(s)-len(sub); i++ { if s[i:i+len(sub)] == sub { return true } } return false }