package engine import ( "context" "testing" ) // TestToolSummaryGenerator_ShortOutput 测试短输出直接做摘要 func TestToolSummaryGenerator_ShortOutput(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") // 短输出(<100 字符)应直接返回 summary := gen.GenerateSummary(context.Background(), "Bash", "ls", "file1.txt\nfile2.txt") if summary == "" { t.Error("短输出的摘要不应为空") } // 短输出只保留第一行 if summary != "file1.txt" { t.Errorf("短输出应只保留第一行, 期望 'file1.txt', 实际: '%s'", summary) } } // TestToolSummaryGenerator_EmptyOutput 测试空输出 func TestToolSummaryGenerator_EmptyOutput(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") summary := gen.GenerateSummary(context.Background(), "Bash", "ls", "") // 空输出,短路径返回空字符串 if summary != "" { t.Errorf("空输出的摘要应为空, 实际: '%s'", summary) } } // TestToolSummaryGenerator_LongOutput_NoAPI 测试长输出但无 API 配置时返回工具名 func TestToolSummaryGenerator_LongOutput_NoAPI(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") // 生成超过 100 字符的输出 longOutput := "" for i := 0; i < 20; i++ { longOutput += "this is a long output line\n" } summary := gen.GenerateSummary(context.Background(), "Grep", "search", longOutput) // 无 API 配置时应 fallback 到工具名 if summary != "Grep" { t.Errorf("无 API 配置时长输出应返回工具名, 期望 'Grep', 实际: '%s'", summary) } } // TestToolSummaryGenerator_Truncation 测试摘要截断 func TestToolSummaryGenerator_Truncation(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") // 创建一个刚好 99 字符的输出(短于阈值) output := "a" for len(output) < 99 { output += "b" } summary := gen.GenerateSummary(context.Background(), "Bash", "cmd", output) // 应被截断到 maxSummaryLength runes := []rune(summary) if len(runes) > maxSummaryLength { t.Errorf("摘要应不超过 %d 字符, 实际: %d", maxSummaryLength, len(runes)) } } // TestToolSummaryGenerator_ChineseOutput 测试中文输出的截断 func TestToolSummaryGenerator_ChineseOutput(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") // 中文短输出 summary := gen.GenerateSummary(context.Background(), "Bash", "ls", "文件已创建成功") if summary != "文件已创建成功" { t.Errorf("中文短输出应直接返回, 期望 '文件已创建成功', 实际: '%s'", summary) } } // TestToolSummaryGenerator_WhitespaceOutput 测试空白输出 func TestToolSummaryGenerator_WhitespaceOutput(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") summary := gen.GenerateSummary(context.Background(), "Bash", "echo", " \n \n ") // 纯空白输出清理后为空 if summary != "" { t.Errorf("纯空白输出的摘要应为空, 实际: '%s'", summary) } } // TestToolSummaryGenerator_SingleLineOutput 测试单行输出 func TestToolSummaryGenerator_SingleLineOutput(t *testing.T) { gen := NewToolSummaryGenerator(nil, "") summary := gen.GenerateSummary(context.Background(), "Glob", "*.go", "found 5 files") if summary != "found 5 files" { t.Errorf("单行短输出应直接返回, 期望 'found 5 files', 实际: '%s'", summary) } } // TestToolSummaryEvent 测试 ToolSummaryEvent 事件类型 func TestToolSummaryEvent(t *testing.T) { evt := &ToolSummaryEvent{ ID: "test-id", ToolName: "Bash", Summary: "执行完成", } if evt.EventType() != "tool_summary" { t.Errorf("事件类型应为 'tool_summary', 实际: '%s'", evt.EventType()) } if evt.ID != "test-id" { t.Errorf("事件 ID 应为 'test-id', 实际: '%s'", evt.ID) } if evt.ToolName != "Bash" { t.Errorf("工具名应为 'Bash', 实际: '%s'", evt.ToolName) } if evt.Summary != "执行完成" { t.Errorf("摘要应为 '执行完成', 实际: '%s'", evt.Summary) } } // TestCleanOutput 测试输出清理函数 func TestCleanOutput(t *testing.T) { tests := []struct { input string expected string }{ {"hello\nworld", "hello"}, {" hello ", "hello"}, {" hello\nworld ", "hello"}, {"", ""}, {"single", "single"}, } for _, tt := range tests { result := cleanOutput(tt.input) if result != tt.expected { t.Errorf("cleanOutput(%q) = %q, 期望 %q", tt.input, result, tt.expected) } } } // TestNewToolSummaryGenerator_Defaults 测试默认值 func TestNewToolSummaryGenerator_Defaults(t *testing.T) { gen := NewToolSummaryGenerator(nil, "model-id") if gen.modelID != "model-id" { t.Errorf("modelID 应为 'model-id', 实际: %s", gen.modelID) } if gen.provider != nil { t.Error("provider 应为 nil(未传入时)") } }