package channels import ( "context" "errors" "testing" ) func TestFormatAnimatedToolFeedbackContent(t *testing.T) { got := formatAnimatedToolFeedbackContent("🔧 `read_file`\nReading config file", "running..") want := "🔧 `read_filerunning..`\nReading config file" if got != want { t.Fatalf("formatAnimatedToolFeedbackContent() = %q, want %q", got, want) } } func TestInitialAnimatedToolFeedbackContent(t *testing.T) { got := InitialAnimatedToolFeedbackContent("🔧 `exec`\nRunning command") want := "🔧 `exec`\nRunning command" if got != want { t.Fatalf("InitialAnimatedToolFeedbackContent() = %q, want %q", got, want) } } func TestFormatAnimatedToolFeedbackContent_WithoutCodeSpan(t *testing.T) { got := formatAnimatedToolFeedbackContent("hello", "running..") want := "hellorunning.." if got != want { t.Fatalf("formatAnimatedToolFeedbackContent() without code span = %q, want %q", got, want) } } func TestToolFeedbackAnimator_RecordCurrentAndClear(t *testing.T) { animator := NewToolFeedbackAnimator(nil) animator.Record("chat-1", "msg-1", "🔧 `read_file`") msgID, ok := animator.Current("chat-1") if !ok || msgID != "msg-1" { t.Fatalf("Current() = (%q, %v), want (msg-1, true)", msgID, ok) } animator.Clear("chat-1") msgID, ok = animator.Current("chat-1") if ok || msgID != "" { t.Fatalf("Current() after Clear = (%q, %v), want (\"\", false)", msgID, ok) } } func TestToolFeedbackAnimator_TakeStopsTrackingAndReturnsState(t *testing.T) { animator := NewToolFeedbackAnimator(nil) animator.Record("chat-1", "msg-1", "🔧 `read_file`\nChecking config") msgID, baseContent, ok := animator.Take("chat-1") if !ok { t.Fatal("Take() = not found, want tracked message") } if msgID != "msg-1" { t.Fatalf("Take() msgID = %q, want msg-1", msgID) } if baseContent != "🔧 `read_file`\nChecking config" { t.Fatalf("Take() baseContent = %q", baseContent) } if _, ok := animator.Current("chat-1"); ok { t.Fatal("expected tracked message to be removed after Take()") } } func TestToolFeedbackAnimator_UpdateStopsTrackingBeforeEdit(t *testing.T) { var animator *ToolFeedbackAnimator animator = NewToolFeedbackAnimator(func(_ context.Context, chatID, messageID, content string) error { if _, ok := animator.Current(chatID); ok { t.Fatal("expected tracked tool feedback to be stopped before edit") } if messageID != "msg-1" { t.Fatalf("messageID = %q, want msg-1", messageID) } if content != "🔧 `write_file`\nUpdating config" { t.Fatalf("content = %q, want updated animated content", content) } return nil }) defer animator.StopAll() animator.Record("chat-1", "msg-1", "🔧 `read_file`\nChecking config") msgID, handled, err := animator.Update(context.Background(), "chat-1", "🔧 `write_file`\nUpdating config") if err != nil { t.Fatalf("Update() error = %v", err) } if !handled { t.Fatal("Update() handled = false, want true") } if msgID != "msg-1" { t.Fatalf("Update() msgID = %q, want msg-1", msgID) } } func TestToolFeedbackAnimator_UpdateFailureRestoresTracking(t *testing.T) { editErr := errors.New("edit failed") animator := NewToolFeedbackAnimator(func(context.Context, string, string, string) error { return editErr }) defer animator.StopAll() animator.Record("chat-1", "msg-1", "🔧 `read_file`\nChecking config") msgID, handled, err := animator.Update(context.Background(), "chat-1", "🔧 `write_file`\nUpdating config") if !handled { t.Fatal("Update() handled = false, want true") } if !errors.Is(err, editErr) { t.Fatalf("Update() error = %v, want editErr", err) } if msgID != "" { t.Fatalf("Update() msgID = %q, want empty on failed edit", msgID) } if currentID, ok := animator.Current("chat-1"); !ok || currentID != "msg-1" { t.Fatalf("Current() after failed Update = (%q, %v), want (msg-1, true)", currentID, ok) } }