package config import ( "encoding/json" "os" "path/filepath" "strings" "testing" ) func TestBuiltinPresets(t *testing.T) { // Ensure all built-in presets are accessible (E2E tested agents only) presets := []AgentPreset{AgentClaude, AgentGemini, AgentCodex} for _, preset := range presets { info := GetAgentPreset(preset) if info == nil { t.Errorf("GetAgentPreset(%s) returned nil", preset) continue } if info.Command == "" { t.Errorf("preset %s has empty Command", preset) } } } func TestGetAgentPresetByName(t *testing.T) { tests := []struct { name string want AgentPreset wantNil bool }{ {"claude", AgentClaude, false}, {"gemini", AgentGemini, false}, {"codex", AgentCodex, false}, {"aider", "", true}, // Not built-in, can be added via config {"opencode", "", true}, // Not built-in, can be added via config {"unknown", "", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := GetAgentPresetByName(tt.name) if tt.wantNil && got != nil { t.Errorf("GetAgentPresetByName(%s) = %v, want nil", tt.name, got) } if !tt.wantNil && got == nil { t.Errorf("GetAgentPresetByName(%s) = nil, want preset", tt.name) } if !tt.wantNil && got != nil && got.Name != tt.want { t.Errorf("GetAgentPresetByName(%s).Name = %v, want %v", tt.name, got.Name, tt.want) } }) } } func TestRuntimeConfigFromPreset(t *testing.T) { tests := []struct { preset AgentPreset wantCommand string }{ {AgentClaude, "claude"}, {AgentGemini, "gemini"}, {AgentCodex, "codex"}, } for _, tt := range tests { t.Run(string(tt.preset), func(t *testing.T) { rc := RuntimeConfigFromPreset(tt.preset) if rc.Command != tt.wantCommand { t.Errorf("RuntimeConfigFromPreset(%s).Command = %v, want %v", tt.preset, rc.Command, tt.wantCommand) } }) } } func TestIsKnownPreset(t *testing.T) { tests := []struct { name string want bool }{ {"claude", true}, {"gemini", true}, {"codex", true}, {"aider", false}, // Not built-in, can be added via config {"opencode", false}, // Not built-in, can be added via config {"unknown", false}, {"chatgpt", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := IsKnownPreset(tt.name); got != tt.want { t.Errorf("IsKnownPreset(%s) = %v, want %v", tt.name, got, tt.want) } }) } } func TestLoadAgentRegistry(t *testing.T) { // Create temp directory for test config tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "agents.json") // Write custom agent config customRegistry := AgentRegistry{ Version: CurrentAgentRegistryVersion, Agents: map[string]*AgentPresetInfo{ "my-agent": { Name: "my-agent", Command: "my-agent-bin", Args: []string{"--auto"}, }, }, } data, err := json.Marshal(customRegistry) if err != nil { t.Fatalf("failed to marshal test config: %v", err) } if err := os.WriteFile(configPath, data, 0644); err != nil { t.Fatalf("failed to write test config: %v", err) } // Reset global registry for test isolation ResetRegistryForTesting() // Load the custom registry if err := LoadAgentRegistry(configPath); err != nil { t.Fatalf("LoadAgentRegistry failed: %v", err) } // Check custom agent is available myAgent := GetAgentPresetByName("my-agent") if myAgent == nil { t.Fatal("custom agent 'my-agent' not found after loading registry") } if myAgent.Command != "my-agent-bin" { t.Errorf("my-agent.Command = %v, want my-agent-bin", myAgent.Command) } // Check built-ins still accessible claude := GetAgentPresetByName("claude") if claude == nil { t.Fatal("built-in 'claude' not found after loading registry") } // Reset for other tests ResetRegistryForTesting() } func TestAgentPresetYOLOFlags(t *testing.T) { // Verify YOLO flags are set correctly for each E2E tested agent tests := []struct { preset AgentPreset wantArg string // At least this arg should be present }{ {AgentClaude, "--dangerously-skip-permissions"}, {AgentGemini, "yolo"}, // Part of "--approval-mode yolo" {AgentCodex, "--yolo"}, } for _, tt := range tests { t.Run(string(tt.preset), func(t *testing.T) { info := GetAgentPreset(tt.preset) if info == nil { t.Fatalf("preset %s not found", tt.preset) } found := false for _, arg := range info.Args { if arg == tt.wantArg || (tt.preset == AgentGemini && arg == "yolo") { found = true break } } if !found { t.Errorf("preset %s args %v missing expected %s", tt.preset, info.Args, tt.wantArg) } }) } } func TestMergeWithPreset(t *testing.T) { // Test that user config overrides preset defaults userConfig := &RuntimeConfig{ Command: "/custom/claude", Args: []string{"--custom-arg"}, } merged := userConfig.MergeWithPreset(AgentClaude) if merged.Command != "/custom/claude" { t.Errorf("merged command should be user value, got %s", merged.Command) } if len(merged.Args) != 1 || merged.Args[0] != "--custom-arg" { t.Errorf("merged args should be user value, got %v", merged.Args) } // Test nil config gets preset defaults var nilConfig *RuntimeConfig merged = nilConfig.MergeWithPreset(AgentClaude) if merged.Command != "claude" { t.Errorf("nil config merge should get preset command, got %s", merged.Command) } // Test empty config gets preset defaults emptyConfig := &RuntimeConfig{} merged = emptyConfig.MergeWithPreset(AgentGemini) if merged.Command != "gemini" { t.Errorf("empty config merge should get preset command, got %s", merged.Command) } } func TestBuildResumeCommand(t *testing.T) { tests := []struct { name string agentName string sessionID string wantEmpty bool contains []string // strings that should appear in result }{ { name: "claude with session", agentName: "claude", sessionID: "session-123", wantEmpty: false, contains: []string{"claude", "--dangerously-skip-permissions", "--resume", "session-123"}, }, { name: "gemini with session", agentName: "gemini", sessionID: "gemini-sess-456", wantEmpty: false, contains: []string{"gemini", "--approval-mode", "yolo", "--resume", "gemini-sess-456"}, }, { name: "codex subcommand style", agentName: "codex", sessionID: "codex-sess-789", wantEmpty: false, contains: []string{"codex", "resume", "codex-sess-789", "--yolo"}, }, { name: "empty session ID", agentName: "claude", sessionID: "", wantEmpty: true, }, { name: "unknown agent", agentName: "unknown-agent", sessionID: "session-123", wantEmpty: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := BuildResumeCommand(tt.agentName, tt.sessionID) if tt.wantEmpty { if result != "" { t.Errorf("BuildResumeCommand(%s, %s) = %q, want empty", tt.agentName, tt.sessionID, result) } return } for _, s := range tt.contains { if !strings.Contains(result, s) { t.Errorf("BuildResumeCommand(%s, %s) = %q, missing %q", tt.agentName, tt.sessionID, result, s) } } }) } } func TestSupportsSessionResume(t *testing.T) { tests := []struct { agentName string want bool }{ {"claude", true}, {"gemini", true}, {"codex", true}, {"unknown", false}, } for _, tt := range tests { t.Run(tt.agentName, func(t *testing.T) { if got := SupportsSessionResume(tt.agentName); got != tt.want { t.Errorf("SupportsSessionResume(%s) = %v, want %v", tt.agentName, got, tt.want) } }) } } func TestGetSessionIDEnvVar(t *testing.T) { tests := []struct { agentName string want string }{ {"claude", "CLAUDE_SESSION_ID"}, {"gemini", "GEMINI_SESSION_ID"}, {"codex", ""}, // Codex uses JSONL output instead {"unknown", ""}, } for _, tt := range tests { t.Run(tt.agentName, func(t *testing.T) { if got := GetSessionIDEnvVar(tt.agentName); got != tt.want { t.Errorf("GetSessionIDEnvVar(%s) = %q, want %q", tt.agentName, got, tt.want) } }) } }