package claudecode import ( "strings" "testing" "github.com/chenhg5/cc-connect/core" ) func TestAgentUsageProbeEnv_AddsHostManagedFlagForCustomProvider(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "custom", BaseURL: "https://example.com/v1", APIKey: "secret", }, }, activeIdx: 0, } env := envSliceToMap(a.usageProbeEnv()) if got := env["ANTHROPIC_BASE_URL"]; got != "https://example.com/v1" { t.Fatalf("ANTHROPIC_BASE_URL = %q, want custom base URL", got) } if got := env["ANTHROPIC_AUTH_TOKEN"]; got != "secret" { t.Fatalf("ANTHROPIC_AUTH_TOKEN = %q, want injected bearer token", got) } if got := env["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; got != "1" { t.Fatalf("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = %q, want 1", got) } } func TestAgentUsageProbeEnv_DoesNotAddHostManagedFlagForModelOnlyProvider(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "model-only", Model: "claude-sonnet-4", }, }, activeIdx: 0, } env := envSliceToMap(a.usageProbeEnv()) if _, ok := env["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; ok { t.Fatalf("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST unexpectedly set: %v", env) } } func TestAgentUsageProbeEnv_AddsHostManagedFlagForProviderEnvRoutingOverrides(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "bedrock", Env: map[string]string{ "CLAUDE_CODE_USE_BEDROCK": "1", }, }, }, activeIdx: 0, } env := envSliceToMap(a.usageProbeEnv()) if got := env["CLAUDE_CODE_USE_BEDROCK"]; got != "1" { t.Fatalf("CLAUDE_CODE_USE_BEDROCK = %q, want 1", got) } if got := env["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; got != "1" { t.Fatalf("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = %q, want 1", got) } } func TestAgentUsageProbeEnv_AddsHostManagedFlagForSessionEnvRoutingOverrides(t *testing.T) { a := &Agent{ sessionEnv: []string{ "ANTHROPIC_BASE_URL=https://session.example/v1", }, } env := envSliceToMap(a.usageProbeEnv()) if got := env["ANTHROPIC_BASE_URL"]; got != "https://session.example/v1" { t.Fatalf("ANTHROPIC_BASE_URL = %q, want session override", got) } if got := env["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; got != "1" { t.Fatalf("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = %q, want 1", got) } } func TestAgentUsageProbeEnv_AddsHostManagedFlagForRouterOverrides(t *testing.T) { a := &Agent{ routerURL: "http://127.0.0.1:3456", routerAPIKey: "router-secret", } env := envSliceToMap(a.usageProbeEnv()) if got := env["ANTHROPIC_BASE_URL"]; got != "http://127.0.0.1:3456" { t.Fatalf("ANTHROPIC_BASE_URL = %q, want router URL", got) } if got := env["ANTHROPIC_API_KEY"]; got != "router-secret" { t.Fatalf("ANTHROPIC_API_KEY = %q, want router API key", got) } if got := env["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; got != "1" { t.Fatalf("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = %q, want 1", got) } } func TestProviderEnv_SetsAnthropicModel(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "provider-a", BaseURL: "https://a.example.com/v1", APIKey: "key-a", Model: "model-a", }, { Name: "provider-b", BaseURL: "https://b.example.com/v1", APIKey: "key-b", Model: "model-b", }, }, activeIdx: 0, } env := envSliceToMap(a.providerEnvLocked()) if got := env["ANTHROPIC_MODEL"]; got != "model-a" { t.Fatalf("ANTHROPIC_MODEL = %q, want %q", got, "model-a") } if got := env["ANTHROPIC_BASE_URL"]; got != "https://a.example.com/v1" { t.Fatalf("ANTHROPIC_BASE_URL = %q, want provider-a URL", got) } a.SetActiveProvider("provider-b") env = envSliceToMap(a.providerEnvLocked()) if got := env["ANTHROPIC_MODEL"]; got != "model-b" { t.Fatalf("after switch: ANTHROPIC_MODEL = %q, want %q", got, "model-b") } if got := env["ANTHROPIC_BASE_URL"]; got != "https://b.example.com/v1" { t.Fatalf("after switch: ANTHROPIC_BASE_URL = %q, want provider-b URL", got) } } func TestProviderEnv_NoModelWhenEmpty(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "no-model", BaseURL: "https://example.com/v1", APIKey: "key", }, }, activeIdx: 0, } env := envSliceToMap(a.providerEnvLocked()) if _, ok := env["ANTHROPIC_MODEL"]; ok { t.Fatalf("ANTHROPIC_MODEL should not be set when provider has no model") } } func TestProviderEnv_ClearReturnsNil(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ {Name: "p", BaseURL: "https://x.com", APIKey: "k", Model: "m"}, }, activeIdx: 0, } a.SetActiveProvider("") env := a.providerEnvLocked() if env != nil { t.Fatalf("expected nil env after clearing provider, got %v", env) } } func TestStartSession_UsesActiveProviderModel(t *testing.T) { a := &Agent{ model: "default-model", providers: []core.ProviderConfig{ {Name: "p1", Model: "provider-model-1"}, {Name: "p2", Model: "provider-model-2"}, }, activeIdx: 0, } a.mu.Lock() activeIdx := a.activeIdx model := a.model if activeIdx >= 0 && activeIdx < len(a.providers) { if m := a.providers[activeIdx].Model; m != "" { model = m } } a.mu.Unlock() if model != "provider-model-1" { t.Fatalf("model = %q, want %q", model, "provider-model-1") } a.SetActiveProvider("p2") a.mu.Lock() activeIdx = a.activeIdx model = a.model if activeIdx >= 0 && activeIdx < len(a.providers) { if m := a.providers[activeIdx].Model; m != "" { model = m } } a.mu.Unlock() if model != "provider-model-2" { t.Fatalf("after switch: model = %q, want %q", model, "provider-model-2") } } func envSliceToMap(env []string) map[string]string { out := make(map[string]string, len(env)) for _, entry := range env { key, value, ok := strings.Cut(entry, "=") if !ok { continue } out[key] = value } return out } func TestProviderEnv_BedrockThinkingRewrite(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "bedrock", Env: map[string]string{ "CLAUDE_CODE_USE_BEDROCK": "1", "AWS_PROFILE": "bedrock", }, Thinking: "disabled", }, }, activeIdx: 0, } env := envSliceToMap(a.providerEnvLocked()) // Should set ANTHROPIC_BEDROCK_BASE_URL to local proxy URL. baseURL := env["ANTHROPIC_BEDROCK_BASE_URL"] if baseURL == "" { t.Fatalf("ANTHROPIC_BEDROCK_BASE_URL should be set for Bedrock with thinking rewrite") } if !strings.HasPrefix(baseURL, "http://127.0.0.1:") { t.Fatalf("ANTHROPIC_BEDROCK_BASE_URL = %q, want local proxy URL", baseURL) } // Should preserve Bedrock env vars. if got := env["CLAUDE_CODE_USE_BEDROCK"]; got != "1" { t.Fatalf("CLAUDE_CODE_USE_BEDROCK = %q, want 1", got) } // Should set NO_PROXY for local proxy. if got := env["NO_PROXY"]; got != "127.0.0.1" { t.Fatalf("NO_PROXY = %q, want 127.0.0.1", got) } } func TestProviderEnv_VertexThinkingRewrite(t *testing.T) { a := &Agent{ providers: []core.ProviderConfig{ { Name: "vertex", Env: map[string]string{ "CLAUDE_CODE_USE_VERTEX": "1", "CLOUD_ML_REGION": "us-east1", }, Thinking: "disabled", }, }, activeIdx: 0, } env := envSliceToMap(a.providerEnvLocked()) // Should set ANTHROPIC_VERTEX_BASE_URL to local proxy URL. baseURL := env["ANTHROPIC_VERTEX_BASE_URL"] if baseURL == "" { t.Fatalf("ANTHROPIC_VERTEX_BASE_URL should be set for Vertex with thinking rewrite") } if !strings.HasPrefix(baseURL, "http://127.0.0.1:") { t.Fatalf("ANTHROPIC_VERTEX_BASE_URL = %q, want local proxy URL", baseURL) } } func TestProviderEnv_BedrockNoThinking(t *testing.T) { // Without thinking override, Bedrock provider should not use proxy. a := &Agent{ providers: []core.ProviderConfig{ { Name: "bedrock", Env: map[string]string{ "CLAUDE_CODE_USE_BEDROCK": "1", }, }, }, activeIdx: 0, } env := envSliceToMap(a.providerEnvLocked()) // Should NOT set ANTHROPIC_BEDROCK_BASE_URL when thinking is not set. if _, ok := env["ANTHROPIC_BEDROCK_BASE_URL"]; ok { t.Fatalf("ANTHROPIC_BEDROCK_BASE_URL should not be set without thinking override") } // Should preserve Bedrock env var. if got := env["CLAUDE_CODE_USE_BEDROCK"]; got != "1" { t.Fatalf("CLAUDE_CODE_USE_BEDROCK = %q, want 1", got) } } func TestDetectEnvOnlyProviderType(t *testing.T) { tests := []struct { env map[string]string expected string }{ {map[string]string{"CLAUDE_CODE_USE_BEDROCK": "1"}, "bedrock"}, {map[string]string{"CLAUDE_CODE_USE_VERTEX": "1"}, "vertex"}, {map[string]string{"CLAUDE_CODE_USE_FOUNDRY": "1"}, "foundry"}, {map[string]string{"CLAUDE_CODE_USE_BEDROCK": "0"}, ""}, {map[string]string{"OTHER_VAR": "1"}, ""}, {nil, ""}, } for _, tt := range tests { got := detectEnvOnlyProviderType(tt.env) if got != tt.expected { t.Errorf("detectEnvOnlyProviderType(%v) = %q, want %q", tt.env, got, tt.expected) } } }