mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
451db2f5d8
* feat(channels): unify tool feedback animation across discord telegram and feishu * fix(tool-feedback): unify fallback and single-message delivery * fix(channels): finalize tool feedback in place * fix ci * feat: improve tool feedback * fix review blockers in pico token cache and tool feedback fix(provider): preserve function thought signatures fix(feishu): recover tool feedback after edit fallback * * delete dead code * fix(pico): clean up tool feedback progress state * fix ci * fix(web): preserve tool feedback line breaks in chat * fix(channels): preserve tool feedback progress state fix(pico): preserve context usage when finalizing tool feedback chore: record branch review pass fix: preserve tool feedback finalization state fix(web): handle pico history update fallback * fix ci
122 lines
3.8 KiB
Go
122 lines
3.8 KiB
Go
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)
|
|
}
|
|
}
|