| | package stream
|
| |
|
| | import (
|
| | "bytes"
|
| | "os"
|
| | "strings"
|
| | "testing"
|
| | )
|
| |
|
| |
|
| |
|
| | func TestNativeToolCallConversion(t *testing.T) {
|
| |
|
| | testData, err := os.ReadFile("/Users/leokun/Desktop/opus-api/一个完整的任务日志/4_upstream_response.txt")
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data: %v", err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | if !strings.Contains(outputStr, "event: message_start") {
|
| | t.Error("Missing message_start event")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, "event: message_stop") {
|
| | t.Error("Missing message_stop event")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, "event: content_block_start") {
|
| | t.Error("Missing content_block_start event")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"type":"tool_use"`) {
|
| | t.Error("Missing tool_use content blocks")
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Glob"`) {
|
| | t.Error("Missing Glob tool call - XML tools not converted")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Bash"`) {
|
| | t.Error("Missing Bash tool call - XML tools not converted")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"input":{`) {
|
| | t.Error("Tool input not properly formatted")
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, "function_calls") {
|
| | t.Error("Output should not contain XML function_calls tags")
|
| | }
|
| |
|
| | if strings.Contains(outputStr, "<invoke") {
|
| | t.Error("Output should not contain XML invoke tags")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"stop_reason":"tool_use"`) {
|
| | t.Error("Stop reason should be tool_use")
|
| | }
|
| |
|
| | t.Logf("Total output length: %d bytes", len(outputStr))
|
| |
|
| |
|
| | if len(outputStr) > 2000 {
|
| | t.Logf("Output preview:\n%s\n...", outputStr[:2000])
|
| | } else {
|
| | t.Logf("Output:\n%s", outputStr)
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| | func TestMultipleFunctionCallsBlocks(t *testing.T) {
|
| | testData, err := os.ReadFile("../../test/fixtures/multiple_function_calls.txt")
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data: %v", err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | if !strings.Contains(outputStr, "event: message_start") {
|
| | t.Error("Missing message_start event")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, "event: message_stop") {
|
| | t.Error("Missing message_stop event")
|
| | }
|
| |
|
| |
|
| | toolUseCount := strings.Count(outputStr, `"type":"tool_use"`)
|
| | t.Logf("Found %d tool_use blocks", toolUseCount)
|
| |
|
| | if toolUseCount == 0 {
|
| | t.Error("Should have converted XML tool calls to tool_use blocks")
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, "function_calls") {
|
| | t.Error("Output should not contain XML function_calls tags")
|
| | }
|
| |
|
| | t.Logf("Output preview:\n%s", outputStr[:min(2000, len(outputStr))])
|
| | }
|
| |
|
| | func min(a, b int) int {
|
| | if a < b {
|
| | return a
|
| | }
|
| | return b
|
| | }
|
| |
|
| |
|
| |
|
| | func TestRealWarpGrepToolCall(t *testing.T) {
|
| |
|
| |
|
| | testData, err := os.ReadFile("../../test/fixtures/real_warp_grep_call.txt")
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data: %v", err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | if !strings.Contains(outputStr, "event: message_start") {
|
| | t.Error("Missing message_start event")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, "event: message_stop") {
|
| | t.Error("Missing message_stop event")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"type":"tool_use"`) {
|
| | t.Error("Missing tool_use content blocks")
|
| | }
|
| |
|
| |
|
| |
|
| | if strings.Contains(outputStr, `"input":{}`) {
|
| | t.Error("CRITICAL BUG: Tool input is empty {}! Parameters were lost during conversion")
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, `"name":"Glob"`) {
|
| |
|
| | if !strings.Contains(outputStr, `"pattern"`) {
|
| | t.Error("Glob tool call missing pattern parameter")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, "package.json") {
|
| | t.Error("Missing expected pattern 'package.json' in Glob call")
|
| | }
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, `"name":"Bash"`) {
|
| | if !strings.Contains(outputStr, `"command"`) {
|
| | t.Error("Bash tool call missing command parameter")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, "find") || !strings.Contains(outputStr, "ls") {
|
| | t.Error("Missing expected commands in Bash calls")
|
| | }
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"stop_reason":"tool_use"`) {
|
| | t.Error("Stop reason should be tool_use")
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, "function_calls") {
|
| | t.Error("Output should not contain XML function_calls tags")
|
| | }
|
| |
|
| | t.Logf("Total output length: %d bytes", len(outputStr))
|
| |
|
| |
|
| | lines := strings.Split(outputStr, "\n")
|
| | t.Logf("Total lines: %d", len(lines))
|
| |
|
| |
|
| | for i, line := range lines {
|
| | if strings.Contains(line, `"type":"tool_use"`) ||
|
| | strings.Contains(line, `"input"`) ||
|
| | strings.Contains(line, `"name":"Glob"`) ||
|
| | strings.Contains(line, `"name":"Bash"`) {
|
| | t.Logf("Line %d: %s", i, line)
|
| | }
|
| | }
|
| | }
|
| |
|
| |
|
| | func TestLatestMorphResponse(t *testing.T) {
|
| | testData, err := os.ReadFile("../../test/fixtures/latest_morph_response.txt")
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data: %v", err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | toolUseCount := strings.Count(outputStr, `"type":"tool_use"`)
|
| | t.Logf("Found %d tool_use blocks", toolUseCount)
|
| |
|
| | if toolUseCount == 0 {
|
| | t.Error("Expected tool_use blocks but found none")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Read"`) {
|
| | t.Error("Missing Read tool call")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Glob"`) {
|
| | t.Error("Missing Glob tool call")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"stop_reason":"tool_use"`) {
|
| | t.Error("Stop reason should be tool_use, not end_turn")
|
| | }
|
| |
|
| |
|
| | lines := strings.Split(outputStr, "\n")
|
| | t.Logf("Total lines: %d", len(lines))
|
| | for i, line := range lines {
|
| | if strings.Contains(line, `"type":"tool_use"`) ||
|
| | strings.Contains(line, `"stop_reason"`) {
|
| | t.Logf("Line %d: %s", i, line)
|
| | }
|
| | }
|
| | }
|
| |
|
| |
|
| | func TestToolCallsWithTextAfter(t *testing.T) {
|
| | testData, err := os.ReadFile("../../test/fixtures/morph_with_text_after_tools.txt")
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data: %v", err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | toolUseCount := strings.Count(outputStr, `"type":"tool_use"`)
|
| | t.Logf("Found %d tool_use blocks", toolUseCount)
|
| |
|
| | if toolUseCount == 0 {
|
| | t.Error("CRITICAL: Expected tool_use blocks but found none - tools after text are being ignored!")
|
| | }
|
| |
|
| |
|
| | if strings.Contains(outputStr, "\\u003c") {
|
| | t.Error("CRITICAL: XML tags are being escaped and output as text instead of being parsed as tools!")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"stop_reason":"tool_use"`) {
|
| | t.Error("Stop reason should be tool_use when tools are present")
|
| | }
|
| |
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Read"`) {
|
| | t.Error("Missing Read tool calls")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Glob"`) {
|
| | t.Error("Missing Glob tool calls")
|
| | }
|
| |
|
| | if !strings.Contains(outputStr, `"name":"Write"`) {
|
| | t.Error("Missing Write tool call")
|
| | }
|
| |
|
| |
|
| | t.Logf("Total output length: %d bytes", len(outputStr))
|
| | lines := strings.Split(outputStr, "\n")
|
| | t.Logf("Total lines: %d", len(lines))
|
| | }
|
| |
|
| |
|
| |
|
| | func TestTransformFromAbsolutePath(t *testing.T) {
|
| |
|
| | inputPath := os.Getenv("UPSTREAM_STREAM_FILE")
|
| | if inputPath == "" {
|
| | t.Skip("Set UPSTREAM_STREAM_FILE environment variable to test with custom file")
|
| | }
|
| |
|
| | testData, err := os.ReadFile(inputPath)
|
| | if err != nil {
|
| | t.Fatalf("Failed to read test data from %s: %v", inputPath, err)
|
| | }
|
| |
|
| | input := bytes.NewReader(testData)
|
| | var output bytes.Buffer
|
| |
|
| | err = TransformMorphToClaudeStream(input, "claude-sonnet-4-5", 0, &output, nil)
|
| | if err != nil {
|
| | t.Errorf("Transform failed: %v", err)
|
| | }
|
| |
|
| | outputStr := output.String()
|
| |
|
| |
|
| | outputPath := "../../client_response.txt"
|
| | if err := os.WriteFile(outputPath, []byte(outputStr), 0644); err != nil {
|
| | t.Fatalf("Failed to write output to %s: %v", outputPath, err)
|
| | }
|
| |
|
| | t.Logf("Successfully transformed %d bytes from %s", len(testData), inputPath)
|
| | t.Logf("Output written to %s (%d bytes)", outputPath, len(outputStr))
|
| | }
|
| |
|