package translator import ( "strings" "testing" ) func TestStreamState(t *testing.T) { t.Run("Reset clears all fields", func(t *testing.T) { s := StreamState{ ResponseID: "test-id", CreatedAt: 12345, Model: "gpt-4", SequenceNum: 10, FinishReason: "stop", NativeFinishReason: "end_turn", InputTokens: 100, OutputTokens: 50, UsageSeen: true, } s.Reset() if s.ResponseID != "" { t.Errorf("Expected ResponseID to be empty, got %s", s.ResponseID) } if s.CreatedAt != 0 { t.Errorf("Expected CreatedAt to be 0, got %d", s.CreatedAt) } if s.Model != "" { t.Errorf("Expected Model to be empty, got %s", s.Model) } if s.SequenceNum != 0 { t.Errorf("Expected SequenceNum to be 0, got %d", s.SequenceNum) } if s.FinishReason != "" { t.Errorf("Expected FinishReason to be empty, got %s", s.FinishReason) } if s.NativeFinishReason != "" { t.Errorf("Expected NativeFinishReason to be empty, got %s", s.NativeFinishReason) } if s.InputTokens != 0 { t.Errorf("Expected InputTokens to be 0, got %d", s.InputTokens) } if s.OutputTokens != 0 { t.Errorf("Expected OutputTokens to be 0, got %d", s.OutputTokens) } if s.UsageSeen { t.Error("Expected UsageSeen to be false") } }) t.Run("NextSequence increments correctly", func(t *testing.T) { s := StreamState{} if s.NextSequence() != 1 { t.Errorf("Expected first sequence to be 1, got %d", s.SequenceNum) } if s.NextSequence() != 2 { t.Errorf("Expected second sequence to be 2, got %d", s.SequenceNum) } if s.NextSequence() != 3 { t.Errorf("Expected third sequence to be 3, got %d", s.SequenceNum) } }) t.Run("EnsureTimestamp sets timestamp when zero", func(t *testing.T) { s := StreamState{} ts := s.EnsureTimestamp() if ts == 0 { t.Error("Expected timestamp to be set") } if s.CreatedAt != ts { t.Errorf("Expected CreatedAt to match returned timestamp, got %d vs %d", s.CreatedAt, ts) } }) t.Run("EnsureTimestamp preserves existing timestamp", func(t *testing.T) { s := StreamState{CreatedAt: 12345} ts := s.EnsureTimestamp() if ts != 12345 { t.Errorf("Expected timestamp to remain 12345, got %d", ts) } }) } func TestToolCallState(t *testing.T) { t.Run("GetArguments returns empty object when no arguments", func(t *testing.T) { tc := &ToolCallState{} args := tc.GetArguments() if args != "{}" { t.Errorf("Expected '{}', got %s", args) } }) t.Run("GetArguments returns accumulated arguments", func(t *testing.T) { tc := &ToolCallState{} tc.Arguments.WriteString(`{"key": "value"}`) args := tc.GetArguments() if args != `{"key": "value"}` { t.Errorf("Expected '{\"key\": \"value\"}', got %s", args) } }) t.Run("WriteArguments appends content", func(t *testing.T) { tc := &ToolCallState{} tc.WriteArguments(`{"a":`) tc.WriteArguments(`1}`) expected := `{"a":1}` if tc.Arguments.String() != expected { t.Errorf("Expected '%s', got %s", expected, tc.Arguments.String()) } }) } func TestToolCallAccumulator(t *testing.T) { t.Run("NewToolCallAccumulator creates empty accumulator", func(t *testing.T) { a := NewToolCallAccumulator() if a == nil { t.Fatal("Expected non-nil accumulator") } if a.HasAny() { t.Error("Expected HasAny to be false for new accumulator") } if a.Count() != 0 { t.Errorf("Expected Count to be 0, got %d", a.Count()) } }) t.Run("Get returns nil for non-existent index", func(t *testing.T) { a := NewToolCallAccumulator() if a.Get(0) != nil { t.Error("Expected Get to return nil for non-existent index") } }) t.Run("GetOrCreate creates new tool call", func(t *testing.T) { a := NewToolCallAccumulator() tc := a.GetOrCreate(0) if tc == nil { t.Fatal("Expected non-nil tool call") } if tc.Index != 0 { t.Errorf("Expected index 0, got %d", tc.Index) } if !a.HasAny() { t.Error("Expected HasAny to be true after creating tool call") } if a.Count() != 1 { t.Errorf("Expected Count to be 1, got %d", a.Count()) } }) t.Run("GetOrCreate returns existing tool call", func(t *testing.T) { a := NewToolCallAccumulator() tc1 := a.GetOrCreate(0) tc1.Name = "test-function" tc2 := a.GetOrCreate(0) if tc2.Name != "test-function" { t.Error("Expected to get existing tool call") } if a.Count() != 1 { t.Errorf("Expected Count to still be 1, got %d", a.Count()) } }) t.Run("CreateNext assigns incremental indices", func(t *testing.T) { a := NewToolCallAccumulator() tc1 := a.CreateNext("id1", "func1") tc2 := a.CreateNext("id2", "func2") if tc1.Index != 0 { t.Errorf("Expected first index to be 0, got %d", tc1.Index) } if tc2.Index != 1 { t.Errorf("Expected second index to be 1, got %d", tc2.Index) } if tc1.ID != "id1" || tc1.Name != "func1" { t.Error("Expected first tool call to have correct ID and Name") } if tc2.ID != "id2" || tc2.Name != "func2" { t.Error("Expected second tool call to have correct ID and Name") } }) t.Run("MarkComplete marks tool call as complete", func(t *testing.T) { a := NewToolCallAccumulator() a.CreateNext("id1", "func1") tc := a.MarkComplete(0) if tc == nil { t.Fatal("Expected non-nil tool call") } if !tc.Complete { t.Error("Expected tool call to be marked complete") } }) t.Run("MarkComplete returns nil for non-existent index", func(t *testing.T) { a := NewToolCallAccumulator() if a.MarkComplete(0) != nil { t.Error("Expected MarkComplete to return nil for non-existent index") } }) t.Run("Remove deletes tool call", func(t *testing.T) { a := NewToolCallAccumulator() a.CreateNext("id1", "func1") a.CreateNext("id2", "func2") a.Remove(0) if a.Count() != 1 { t.Errorf("Expected Count to be 1 after removal, got %d", a.Count()) } if a.Get(0) != nil { t.Error("Expected Get(0) to return nil after removal") } if a.Get(1) == nil { t.Error("Expected Get(1) to still exist") } }) t.Run("GetOrdered returns tool calls in creation order", func(t *testing.T) { a := NewToolCallAccumulator() a.CreateNext("id1", "func1") a.CreateNext("id2", "func2") a.CreateNext("id3", "func3") ordered := a.GetOrdered() if len(ordered) != 3 { t.Fatalf("Expected 3 ordered tool calls, got %d", len(ordered)) } if ordered[0].ID != "id1" || ordered[1].ID != "id2" || ordered[2].ID != "id3" { t.Error("Expected tool calls in creation order") } }) t.Run("Reset clears all state", func(t *testing.T) { a := NewToolCallAccumulator() a.CreateNext("id1", "func1") a.CreateNext("id2", "func2") a.Reset() if a.HasAny() { t.Error("Expected HasAny to be false after reset") } if a.Count() != 0 { t.Errorf("Expected Count to be 0 after reset, got %d", a.Count()) } }) } func TestContentBuffer(t *testing.T) { t.Run("HasContent returns false for empty buffer", func(t *testing.T) { cb := ContentBuffer{} if cb.HasContent() { t.Error("Expected HasContent to be false for empty buffer") } }) t.Run("HasContent returns true with text", func(t *testing.T) { cb := ContentBuffer{} cb.Text.WriteString("hello") if !cb.HasContent() { t.Error("Expected HasContent to be true with text") } }) t.Run("HasContent returns true with reasoning", func(t *testing.T) { cb := ContentBuffer{} cb.Reasoning.WriteString("thinking...") if !cb.HasContent() { t.Error("Expected HasContent to be true with reasoning") } }) t.Run("Reset clears both buffers", func(t *testing.T) { cb := ContentBuffer{} cb.Text.WriteString("text") cb.Reasoning.WriteString("reasoning") cb.Reset() if cb.Text.Len() != 0 { t.Error("Expected Text to be empty after reset") } if cb.Reasoning.Len() != 0 { t.Error("Expected Reasoning to be empty after reset") } if cb.HasContent() { t.Error("Expected HasContent to be false after reset") } }) } func TestStreamingState(t *testing.T) { t.Run("NewStreamingState creates initialized state", func(t *testing.T) { s := NewStreamingState() if s == nil { t.Fatal("Expected non-nil state") } if s.ToolCalls == nil { t.Error("Expected ToolCalls to be initialized") } }) t.Run("Reset clears all fields", func(t *testing.T) { s := NewStreamingState() s.ResponseID = "test-id" s.InTextBlock = true s.InToolBlock = true s.InReasoningBlock = true s.CurrentMessageID = "msg-id" s.CurrentToolCallID = "tool-id" s.CurrentReasoningID = "reasoning-id" s.ToolCalls.CreateNext("id", "func") s.Content.Text.WriteString("text") s.Reset() if s.ResponseID != "" { t.Error("Expected ResponseID to be cleared") } if s.InTextBlock || s.InToolBlock || s.InReasoningBlock { t.Error("Expected block flags to be false") } if s.CurrentMessageID != "" || s.CurrentToolCallID != "" || s.CurrentReasoningID != "" { t.Error("Expected current IDs to be cleared") } if s.ToolCalls.HasAny() { t.Error("Expected ToolCalls to be cleared") } if s.Content.HasContent() { t.Error("Expected Content to be cleared") } }) } func TestFinishReasonMapper(t *testing.T) { t.Run("ToOpenAI maps known reasons", func(t *testing.T) { m := NewFinishReasonMapper() testCases := []struct { input string expected string }{ {"end_turn", "stop"}, {"tool_use", "tool_calls"}, {"max_tokens", "length"}, {"STOP", "stop"}, {"MAX_TOKENS", "length"}, {"SAFETY", "content_filter"}, {"completed", "stop"}, } for _, tc := range testCases { result := m.ToOpenAI(tc.input) if result != tc.expected { t.Errorf("ToOpenAI(%s) = %s, expected %s", tc.input, result, tc.expected) } } }) t.Run("ToOpenAI returns default for unknown reasons", func(t *testing.T) { m := NewFinishReasonMapper() result := m.ToOpenAI("unknown_reason") if result != "stop" { t.Errorf("Expected default 'stop', got %s", result) } }) t.Run("FromOpenAI maps known reasons", func(t *testing.T) { m := NewFinishReasonMapper() testCases := []struct { input string expected string }{ {"stop", "end_turn"}, {"tool_calls", "tool_use"}, {"length", "max_tokens"}, {"content_filter", "SAFETY"}, } for _, tc := range testCases { result := m.FromOpenAI(tc.input) if result != tc.expected { t.Errorf("FromOpenAI(%s) = %s, expected %s", tc.input, result, tc.expected) } } }) t.Run("FromOpenAI returns default for unknown reasons", func(t *testing.T) { m := NewFinishReasonMapper() result := m.FromOpenAI("unknown") if result != "STOP" { t.Errorf("Expected default 'STOP', got %s", result) } }) t.Run("RegisterMapping adds new mapping", func(t *testing.T) { m := NewFinishReasonMapper() m.RegisterMapping("custom_reason", "custom_openai") if m.ToOpenAI("custom_reason") != "custom_openai" { t.Error("Expected new mapping to work in ToOpenAI direction") } if m.FromOpenAI("custom_openai") != "custom_reason" { t.Error("Expected new mapping to work in FromOpenAI direction") } }) } func TestGenerateResponseID(t *testing.T) { t.Run("generates unique IDs", func(t *testing.T) { id1 := GenerateResponseID("") id2 := GenerateResponseID("") if id1 == id2 { t.Error("Expected unique IDs") } }) t.Run("includes prefix when provided", func(t *testing.T) { id := GenerateResponseID("chatcmpl") if !strings.HasPrefix(id, "chatcmpl_") { t.Errorf("Expected ID to start with 'chatcmpl_', got %s", id) } }) t.Run("default prefix is resp_", func(t *testing.T) { id := GenerateResponseID("") if !strings.HasPrefix(id, "resp_") { t.Errorf("Expected ID to start with 'resp_', got %s", id) } }) } func TestGenerateFunctionCallID(t *testing.T) { t.Run("generates unique IDs", func(t *testing.T) { id1 := GenerateFunctionCallID("") id2 := GenerateFunctionCallID("") if id1 == id2 { t.Error("Expected unique IDs") } }) t.Run("includes name when provided", func(t *testing.T) { id := GenerateFunctionCallID("get_weather") if !strings.Contains(id, "get_weather") { t.Errorf("Expected ID to contain 'get_weather', got %s", id) } }) }