sub2api/backend/internal/pkg/windsurf/send_user_cascade_test.go

86 lines
2.4 KiB
Go

package windsurf
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
)
// Simulates a Windsurf LS that rejects SendUserCascadeMessage with
// "panel state not found". Verifies that allowRecreate=false bubbles the
// error up (so chatCascade can rebuild full-history text) while
// allowRecreate=true still triggers the internal recreate path.
func TestSendUserCascadeMessage_AllowRecreateGatesSilentRetry(t *testing.T) {
tests := []struct {
name string
allowRecreate bool
wantErr bool
// When allowRecreate=true the client tries ForceWarmup + StartCascade.
// When allowRecreate=false we expect no such attempts.
wantStartCascadeCalled bool
}{
{"disabled bubbles error", false, true, false},
{"enabled attempts recreate", true, true, true}, // test LS keeps rejecting so overall errors, but StartCascade must be attempted
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var mu sync.Mutex
var paths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
paths = append(paths, r.URL.Path)
mu.Unlock()
// Every RPC returns panel-not-found so we can detect whether
// StartCascade was attempted after the initial failure.
w.Header().Set("Content-Type", "application/grpc")
w.Header().Set("grpc-status", "5")
w.Header().Set("grpc-message", "panel state not found for session abc")
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
client := NewLocalLSClient(42099, "csrf")
client.BaseURL = server.URL
client.HTTP = server.Client()
// Pre-mark as warmed so the first SendUserCascadeMessage doesn't trigger
// warmup on its own path — keeps the test focused.
client.Warmed = true
_, err := client.SendUserCascadeMessage(
context.Background(),
"token",
"existing-cascade-id",
"hello",
"claude-sonnet-4",
"",
0,
nil,
tt.allowRecreate,
)
if (err != nil) != tt.wantErr {
t.Fatalf("err = %v, wantErr = %v", err, tt.wantErr)
}
mu.Lock()
defer mu.Unlock()
startCascadeCalled := false
for _, p := range paths {
if strings.HasSuffix(p, "/StartCascade") {
startCascadeCalled = true
break
}
}
if startCascadeCalled != tt.wantStartCascadeCalled {
t.Fatalf("StartCascade called = %v, want %v (paths: %v)",
startCascadeCalled, tt.wantStartCascadeCalled, paths)
}
})
}
}