Files
kandev/apps/backend/internal/agent/agents/passthrough_payload_test.go
T

83 lines
3.1 KiB
Go

package agents
import (
"testing"
"time"
)
func TestPlanPassthroughStdinWrites_SingleAtomicWrite(t *testing.T) {
cfg := PassthroughConfig{SubmitSequence: "\r"}
got := PlanPassthroughStdinWrites("line1\nline2", cfg)
if len(got) != 1 {
t.Fatalf("got %d chunks, want 1 atomic write: %#v", len(got), got)
}
want := "\x1b[200~line1\nline2\x1b[201~\r"
if got[0] != want {
t.Errorf("payload = %q, want %q", got[0], want)
}
}
func TestPlanPassthroughStdinWrites_SingleLine(t *testing.T) {
cfg := PassthroughConfig{SubmitSequence: "\r"}
got := PlanPassthroughStdinWrites("hello", cfg)
if len(got) != 1 || got[0] != "hello\r" {
t.Fatalf("got %#v, want [\"hello\\r\"]", got)
}
}
// Claude's PassthroughConfig sets SubmitDelay so the submit byte arrives as a
// discrete keystroke (defeats Ink's paste-burst detection). Single-line and
// multi-line prompts both emit body+submit as two chunks; the submit chunk
// carries DelayBefore = SubmitDelay.
func TestPlanPassthroughStdinChunks_ClaudeSplitsSubmit(t *testing.T) {
cfg := NewClaudeACP().PassthroughConfig()
if cfg.SubmitDelay <= 0 {
t.Fatalf("Claude config must set SubmitDelay > 0 for paste-burst workaround, got %v", cfg.SubmitDelay)
}
for _, prompt := range []string{"hello", "### Review Comments\n\n> fix"} {
chunks := PlanPassthroughStdinChunks(prompt, cfg)
if len(chunks) != 2 {
t.Fatalf("prompt %q: got %d chunks, want 2 (body, submit): %#v", prompt, len(chunks), chunks)
}
if chunks[0].Data != prompt {
t.Errorf("prompt %q: body chunk = %q, want verbatim prompt", prompt, chunks[0].Data)
}
if chunks[0].DelayBefore != 0 {
t.Errorf("prompt %q: body chunk DelayBefore = %v, want 0", prompt, chunks[0].DelayBefore)
}
if chunks[1].Data != "\r" {
t.Errorf("prompt %q: submit chunk = %q, want \\r", prompt, chunks[1].Data)
}
if chunks[1].DelayBefore != cfg.SubmitDelay {
t.Errorf("prompt %q: submit DelayBefore = %v, want %v", prompt, chunks[1].DelayBefore, cfg.SubmitDelay)
}
}
}
// Non-Claude TUIs (SubmitDelay == 0) keep the single-atomic-write semantics so we
// don't accidentally regress Cursor/Codex/OpenCode by splitting their submit.
func TestPlanPassthroughStdinChunks_AtomicWhenNoDelay(t *testing.T) {
cfg := PassthroughConfig{SubmitSequence: "\r"}
chunks := PlanPassthroughStdinChunks("hello", cfg)
if len(chunks) != 1 {
t.Fatalf("got %d chunks, want 1 atomic write: %#v", len(chunks), chunks)
}
if chunks[0].DelayBefore != 0 {
t.Errorf("atomic chunk DelayBefore = %v, want 0", chunks[0].DelayBefore)
}
if chunks[0].Data != "hello\r" {
t.Errorf("atomic chunk Data = %q, want \"hello\\r\"", chunks[0].Data)
}
}
// Sanity: a custom config with SubmitDelay set but no Claude-specific flags
// also splits — the field is the lever, not the Claude struct identity.
func TestPlanPassthroughStdinChunks_SubmitDelayDrivesSplit(t *testing.T) {
cfg := PassthroughConfig{SubmitSequence: "\r", DisableBracketedPaste: true, SubmitDelay: 50 * time.Millisecond}
chunks := PlanPassthroughStdinChunks("hi", cfg)
if len(chunks) != 2 || chunks[1].DelayBefore != 50*time.Millisecond || chunks[1].Data != "\r" {
t.Fatalf("expected split with 50ms delay before \\r, got %#v", chunks)
}
}