Files
picoclaw/pkg/providers/codex_cli_provider_integration_test.go
T
Leandro Barbosa 447c17aeb1 merge: sync upstream/main (PR #213) into feat/multi-agent-routing
Resolve conflicts in pkg/providers/types.go and pkg/agent/loop.go:
- types.go: use protocoltypes aliases from PR #213, keep fallback types
- loop.go: drop old single-agent createToolRegistry (replaced by multi-agent pattern)

Refactor to align with PR #213 patterns:
- instance.go: use NewExecToolWithConfig (accept full config for deny patterns)
- registry.go: pass full config to NewAgentInstance
- loop.go: add Perplexity web search options to registerSharedTools
2026-02-18 11:39:14 -03:00

120 lines
3.1 KiB
Go

//go:build integration
package providers
import (
"context"
exec "os/exec"
"strings"
"testing"
"time"
)
// TestIntegration_RealCodexCLI tests the CodexCliProvider with a real codex CLI.
// Run with: go test -tags=integration ./pkg/providers/...
func TestIntegration_RealCodexCLI(t *testing.T) {
path, err := exec.LookPath("codex")
if err != nil {
t.Skip("codex CLI not found in PATH, skipping integration test")
}
t.Logf("Using codex CLI at: %s", path)
p := NewCodexCliProvider(t.TempDir())
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
resp, err := p.Chat(ctx, []Message{
{Role: "user", Content: "Respond with only the word 'pong'. Nothing else."},
}, nil, "", nil)
if err != nil {
t.Fatalf("Chat() with real CLI error = %v", err)
}
if resp.Content == "" {
t.Error("Content is empty")
}
if resp.FinishReason != "stop" {
t.Errorf("FinishReason = %q, want %q", resp.FinishReason, "stop")
}
if resp.Usage != nil {
t.Logf("Usage: prompt=%d, completion=%d, total=%d",
resp.Usage.PromptTokens, resp.Usage.CompletionTokens, resp.Usage.TotalTokens)
}
t.Logf("Response content: %q", resp.Content)
if !strings.Contains(strings.ToLower(resp.Content), "pong") {
t.Errorf("Content = %q, expected to contain 'pong'", resp.Content)
}
}
func TestIntegration_RealCodexCLI_WithSystemPrompt(t *testing.T) {
if _, err := exec.LookPath("codex"); err != nil {
t.Skip("codex CLI not found in PATH")
}
p := NewCodexCliProvider(t.TempDir())
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
resp, err := p.Chat(ctx, []Message{
{Role: "system", Content: "You are a calculator. Only respond with numbers. No text."},
{Role: "user", Content: "What is 2+2?"},
}, nil, "", nil)
if err != nil {
t.Fatalf("Chat() error = %v", err)
}
t.Logf("Response: %q", resp.Content)
if !strings.Contains(resp.Content, "4") {
t.Errorf("Content = %q, expected to contain '4'", resp.Content)
}
}
func TestIntegration_RealCodexCLI_ParsesRealJSONL(t *testing.T) {
if _, err := exec.LookPath("codex"); err != nil {
t.Skip("codex CLI not found in PATH")
}
// Run codex directly and verify our parser handles real output
cmd := exec.Command("codex", "exec",
"--json",
"--dangerously-bypass-approvals-and-sandbox",
"--skip-git-repo-check",
"--color", "never",
"-C", t.TempDir(),
"-")
cmd.Stdin = strings.NewReader("Say hi")
output, err := cmd.Output()
if err != nil {
// codex may write diagnostic noise to stderr but still produce valid output
if len(output) == 0 {
t.Fatalf("codex CLI failed: %v", err)
}
}
t.Logf("Raw CLI output (first 500 chars): %s", string(output[:min(len(output), 500)]))
// Verify our parser can handle real output
p := NewCodexCliProvider("")
resp, err := p.parseJSONLEvents(string(output))
if err != nil {
t.Fatalf("parseJSONLEvents() failed on real CLI output: %v", err)
}
if resp.Content == "" {
t.Error("parsed Content is empty")
}
if resp.FinishReason != "stop" {
t.Errorf("FinishReason = %q, want stop", resp.FinishReason)
}
t.Logf("Parsed: content=%q, finish=%s, usage=%+v", resp.Content, resp.FinishReason, resp.Usage)
}