package context import ( "encoding/json" "testing" ) // TestGroupByAPIRound_Empty 测试空消息 func TestGroupByAPIRound_Empty(t *testing.T) { groups := GroupByAPIRound(nil) if len(groups) != 0 { t.Errorf("空消息应返回空, 实际 %d 组", len(groups)) } } // TestGroupByAPIRound_PreambleOnly 测试只有前言消息 func TestGroupByAPIRound_PreambleOnly(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("hello")}, {Role: "user", Content: jsonStr("world")}, } groups := GroupByAPIRound(msgs) if len(groups) != 1 { t.Fatalf("只有前言应返回 1 组, 实际 %d 组", len(groups)) } if groups[0].Index != 0 { t.Errorf("前言组 Index 应为 0, 实际 %d", groups[0].Index) } if len(groups[0].Messages) != 2 { t.Errorf("前言组应有 2 条消息, 实际 %d", len(groups[0].Messages)) } } // TestGroupByAPIRound_SingleRound 测试单轮对话 func TestGroupByAPIRound_SingleRound(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("hello")}, {Role: "assistant", Content: jsonStr("hi there")}, } groups := GroupByAPIRound(msgs) if len(groups) != 2 { t.Fatalf("期望 2 组(前言 + 一轮), 实际 %d 组", len(groups)) } // Group 0: user "hello" if len(groups[0].Messages) != 1 { t.Errorf("前言组应有 1 条消息, 实际 %d", len(groups[0].Messages)) } // Group 1: assistant "hi there" if len(groups[1].Messages) != 1 { t.Errorf("第一轮应有 1 条消息, 实际 %d", len(groups[1].Messages)) } } // TestGroupByAPIRound_MultiRound 测试多轮对话含 tool_result func TestGroupByAPIRound_MultiRound(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("do something")}, {Role: "assistant", Content: jsonStr("calling tool...")}, {Role: "user", Content: jsonStr("tool result")}, // tool_result 跟随 assistant {Role: "assistant", Content: jsonStr("done")}, {Role: "user", Content: jsonStr("thanks")}, } groups := GroupByAPIRound(msgs) // Group 0: user "do something" // Group 1: assistant "calling tool..." + user "tool result" // Group 2: assistant "done" + user "thanks" if len(groups) != 3 { t.Fatalf("期望 3 组, 实际 %d 组", len(groups)) } if len(groups[0].Messages) != 1 { t.Errorf("前言组应有 1 条消息, 实际 %d", len(groups[0].Messages)) } if len(groups[1].Messages) != 2 { t.Errorf("第一轮应有 2 条消息(assistant + tool_result), 实际 %d", len(groups[1].Messages)) } if len(groups[2].Messages) != 2 { t.Errorf("第二轮应有 2 条消息, 实际 %d", len(groups[2].Messages)) } } // TestGroupByAPIRound_TokenEstimation 测试分组的 token 估算 func TestGroupByAPIRound_TokenEstimation(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("hello world this is a test message")}, {Role: "assistant", Content: jsonStr("response to your message")}, } groups := GroupByAPIRound(msgs) for _, g := range groups { if g.Tokens <= 0 { t.Errorf("分组 %d 的 token 数应 > 0, 实际 %d", g.Index, g.Tokens) } } } // TestGroupByAPIRound_ConsecutiveAssistant 测试连续的 assistant 消息 func TestGroupByAPIRound_ConsecutiveAssistant(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("start")}, {Role: "assistant", Content: jsonStr("first response")}, {Role: "assistant", Content: jsonStr("second response")}, } groups := GroupByAPIRound(msgs) // Group 0: user "start" // Group 1: assistant "first response" // Group 2: assistant "second response" if len(groups) != 3 { t.Fatalf("期望 3 组, 实际 %d 组", len(groups)) } } // TestDetectOrphanedToolResults_NoOrphans 测试无孤立 tool_result func TestDetectOrphanedToolResults_NoOrphans(t *testing.T) { toolUseBlock := []map[string]any{ {"type": "tool_use", "id": "tu_123", "name": "read", "input": map[string]any{}}, } toolUseJSON, _ := json.Marshal(toolUseBlock) toolResultBlock := []map[string]any{ {"type": "tool_result", "tool_use_id": "tu_123", "content": "file content"}, } toolResultJSON, _ := json.Marshal(toolResultBlock) msgs := []CompactMessage{ {Role: "user", Content: jsonStr("read file")}, {Role: "assistant", Content: toolUseJSON}, {Role: "user", Content: toolResultJSON}, } orphaned := DetectOrphanedToolResults(msgs) if len(orphaned) != 0 { t.Errorf("不应有孤立 tool_result, 实际 %d 个", len(orphaned)) } } // TestDetectOrphanedToolResults_WithOrphans 测试有孤立 tool_result func TestDetectOrphanedToolResults_WithOrphans(t *testing.T) { // tool_result 但没有对应的 tool_use toolResultBlock := []map[string]any{ {"type": "tool_result", "tool_use_id": "tu_orphan", "content": "orphaned result"}, } toolResultJSON, _ := json.Marshal(toolResultBlock) msgs := []CompactMessage{ {Role: "user", Content: jsonStr("some message")}, {Role: "user", Content: toolResultJSON}, } orphaned := DetectOrphanedToolResults(msgs) if len(orphaned) != 1 { t.Fatalf("期望 1 个孤立 tool_result, 实际 %d", len(orphaned)) } if orphaned[0] != "tu_orphan" { t.Errorf("孤立 ID 应为 tu_orphan, 实际 %s", orphaned[0]) } } // TestDetectOrphanedToolResults_Empty 测试空消息 func TestDetectOrphanedToolResults_Empty(t *testing.T) { orphaned := DetectOrphanedToolResults(nil) if len(orphaned) != 0 { t.Errorf("空消息不应有孤立 tool_result") } } // TestDetectOrphanedToolResults_PlainMessages 测试纯文本消息 func TestDetectOrphanedToolResults_PlainMessages(t *testing.T) { msgs := []CompactMessage{ {Role: "user", Content: jsonStr("hello")}, {Role: "assistant", Content: jsonStr("hi")}, } orphaned := DetectOrphanedToolResults(msgs) if len(orphaned) != 0 { t.Errorf("纯文本消息不应有孤立 tool_result") } }