初始化仓库
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
package claudecode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/chenhg5/cc-connect/config"
|
||||
"github.com/chenhg5/cc-connect/core"
|
||||
)
|
||||
|
||||
// These integration tests use real provider credentials from ~/.cc-connect/config.toml.
|
||||
// They verify that provider switching correctly sets up env vars and that agent
|
||||
// sessions can be started with the right configuration.
|
||||
//
|
||||
// Run with: CC_RUN_PROVIDER_INTEGRATION=1 go test ./agent/claudecode -run TestIntegration -v
|
||||
// Skip explicitly with: CC_SKIP_INTEGRATION=1
|
||||
|
||||
func skipIfNoConfig(t *testing.T) *config.Config {
|
||||
t.Helper()
|
||||
if os.Getenv("CC_SKIP_INTEGRATION") == "1" {
|
||||
t.Skip("CC_SKIP_INTEGRATION=1")
|
||||
}
|
||||
if os.Getenv("CC_RUN_PROVIDER_INTEGRATION") != "1" {
|
||||
t.Skip("set CC_RUN_PROVIDER_INTEGRATION=1 to run provider integration tests")
|
||||
}
|
||||
cfgPath := os.ExpandEnv("$HOME/.cc-connect/config.toml")
|
||||
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
|
||||
t.Skipf("config not found at %s", cfgPath)
|
||||
}
|
||||
cfg, err := config.Load(cfgPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load config: %v", err)
|
||||
}
|
||||
cfg.ResolveProviderRefs()
|
||||
return cfg
|
||||
}
|
||||
|
||||
func configToCoreProv(p config.ProviderConfig) core.ProviderConfig {
|
||||
cp := core.ProviderConfig{
|
||||
Name: p.Name,
|
||||
APIKey: p.APIKey,
|
||||
BaseURL: p.BaseURL,
|
||||
Model: p.Model,
|
||||
Env: p.Env,
|
||||
}
|
||||
if p.Thinking != "" {
|
||||
cp.Thinking = p.Thinking
|
||||
}
|
||||
for _, m := range p.Models {
|
||||
cp.Models = append(cp.Models, core.ModelOption{Name: m.Model})
|
||||
}
|
||||
return cp
|
||||
}
|
||||
|
||||
func findProjectProviders(cfg *config.Config, agentType string) (projName string, providers []core.ProviderConfig, workDir string) {
|
||||
for i := range cfg.Projects {
|
||||
proj := &cfg.Projects[i]
|
||||
if proj.Agent.Type != agentType || len(proj.Agent.Providers) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, p := range proj.Agent.Providers {
|
||||
providers = append(providers, configToCoreProv(p))
|
||||
}
|
||||
projName = proj.Name
|
||||
if wd, ok := proj.Agent.Options["work_dir"].(string); ok {
|
||||
workDir = wd
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestIntegration_ProviderSwitch_EnvVars(t *testing.T) {
|
||||
cfg := skipIfNoConfig(t)
|
||||
|
||||
name, providers, _ := findProjectProviders(cfg, "claudecode")
|
||||
if name == "" {
|
||||
t.Skip("no claudecode project with providers found")
|
||||
}
|
||||
if len(providers) < 2 {
|
||||
t.Skipf("project %q has only %d provider(s), need at least 2", name, len(providers))
|
||||
}
|
||||
|
||||
a := &Agent{
|
||||
providers: providers,
|
||||
activeIdx: -1,
|
||||
}
|
||||
|
||||
p0, p1 := providers[0], providers[1]
|
||||
t.Logf("provider[0]: name=%s model=%s base_url=%s", p0.Name, p0.Model, p0.BaseURL)
|
||||
t.Logf("provider[1]: name=%s model=%s base_url=%s", p1.Name, p1.Model, p1.BaseURL)
|
||||
|
||||
a.SetActiveProvider(p0.Name)
|
||||
env0 := envSliceToMap(a.providerEnvLocked())
|
||||
|
||||
if p0.BaseURL != "" {
|
||||
if got := env0["ANTHROPIC_BASE_URL"]; got != p0.BaseURL {
|
||||
t.Errorf("provider[0] ANTHROPIC_BASE_URL = %q, want %q", got, p0.BaseURL)
|
||||
}
|
||||
}
|
||||
if p0.Model != "" {
|
||||
if got := env0["ANTHROPIC_MODEL"]; got != p0.Model {
|
||||
t.Errorf("provider[0] ANTHROPIC_MODEL = %q, want %q", got, p0.Model)
|
||||
}
|
||||
}
|
||||
|
||||
a.SetActiveProvider(p1.Name)
|
||||
env1 := envSliceToMap(a.providerEnvLocked())
|
||||
|
||||
if p1.BaseURL != "" {
|
||||
if got := env1["ANTHROPIC_BASE_URL"]; got != p1.BaseURL {
|
||||
t.Errorf("provider[1] ANTHROPIC_BASE_URL = %q, want %q", got, p1.BaseURL)
|
||||
}
|
||||
}
|
||||
if p1.Model != "" {
|
||||
if got := env1["ANTHROPIC_MODEL"]; got != p1.Model {
|
||||
t.Errorf("provider[1] ANTHROPIC_MODEL = %q, want %q", got, p1.Model)
|
||||
}
|
||||
}
|
||||
|
||||
if p0.BaseURL != p1.BaseURL {
|
||||
if env0["ANTHROPIC_BASE_URL"] == env1["ANTHROPIC_BASE_URL"] {
|
||||
t.Error("providers have different base_urls but env didn't change after switch")
|
||||
}
|
||||
}
|
||||
if p0.Model != p1.Model && p0.Model != "" && p1.Model != "" {
|
||||
if env0["ANTHROPIC_MODEL"] == env1["ANTHROPIC_MODEL"] {
|
||||
t.Error("providers have different models but ANTHROPIC_MODEL didn't change after switch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_ProviderSwitch_SessionStartModel(t *testing.T) {
|
||||
cfg := skipIfNoConfig(t)
|
||||
|
||||
name, providers, workDir := findProjectProviders(cfg, "claudecode")
|
||||
if name == "" {
|
||||
t.Skip("no claudecode project with providers found")
|
||||
}
|
||||
if len(providers) < 2 {
|
||||
t.Skipf("project %q has only %d provider(s), need at least 2", name, len(providers))
|
||||
}
|
||||
if workDir == "" {
|
||||
workDir = "/tmp"
|
||||
}
|
||||
|
||||
cliBin, err := exec.LookPath("claude")
|
||||
if err != nil {
|
||||
t.Skipf("claude CLI not found: %v", err)
|
||||
}
|
||||
|
||||
for _, prov := range providers[:2] {
|
||||
t.Run(prov.Name, func(t *testing.T) {
|
||||
a := &Agent{
|
||||
model: "default-model",
|
||||
providers: providers,
|
||||
activeIdx: -1,
|
||||
workDir: workDir,
|
||||
cliBin: cliBin,
|
||||
}
|
||||
a.SetActiveProvider(prov.Name)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
sess, err := a.StartSession(ctx, "")
|
||||
if err != nil {
|
||||
t.Fatalf("StartSession failed: %v", err)
|
||||
}
|
||||
defer sess.Close()
|
||||
|
||||
cs := sess.(*claudeSession)
|
||||
envMap := envSliceToMap(cs.cmd.Env)
|
||||
|
||||
if prov.Model != "" {
|
||||
found := false
|
||||
for _, arg := range cs.cmd.Args {
|
||||
if arg == prov.Model {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("--model %q not found in args: %v", prov.Model, cs.cmd.Args)
|
||||
}
|
||||
}
|
||||
|
||||
if prov.BaseURL != "" {
|
||||
gotURL := envMap["ANTHROPIC_BASE_URL"]
|
||||
if gotURL == "" {
|
||||
t.Error("ANTHROPIC_BASE_URL not set in session env")
|
||||
} else if !strings.Contains(gotURL, "127.0.0.1") && gotURL != prov.BaseURL {
|
||||
t.Errorf("ANTHROPIC_BASE_URL = %q, want %q (or proxy URL)", gotURL, prov.BaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
if prov.Model != "" {
|
||||
if got := envMap["ANTHROPIC_MODEL"]; got != prov.Model {
|
||||
t.Errorf("ANTHROPIC_MODEL = %q, want %q", got, prov.Model)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("session started OK: pid=%d, model=%s, env_model=%s, env_base_url=%s",
|
||||
cs.cmd.Process.Pid,
|
||||
prov.Model,
|
||||
envMap["ANTHROPIC_MODEL"],
|
||||
envMap["ANTHROPIC_BASE_URL"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_CodexProvider_EnvVars(t *testing.T) {
|
||||
cfg := skipIfNoConfig(t)
|
||||
|
||||
name, providers, _ := findProjectProviders(cfg, "codex")
|
||||
if name == "" {
|
||||
t.Skip("no codex project with providers found")
|
||||
}
|
||||
|
||||
for _, prov := range providers {
|
||||
t.Run(prov.Name, func(t *testing.T) {
|
||||
t.Logf("codex provider: name=%s base_url=%s model=%s", prov.Name, prov.BaseURL, prov.Model)
|
||||
if prov.BaseURL == "" {
|
||||
t.Error("codex provider should have a base_url")
|
||||
}
|
||||
if prov.Model == "" {
|
||||
t.Error("codex provider should have a model")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_AgentTypeChange_FiltersProviders(t *testing.T) {
|
||||
cfg := skipIfNoConfig(t)
|
||||
|
||||
if len(cfg.Providers) == 0 {
|
||||
t.Skip("no global providers in config")
|
||||
}
|
||||
|
||||
globalByName := make(map[string]config.ProviderConfig, len(cfg.Providers))
|
||||
for _, p := range cfg.Providers {
|
||||
globalByName[p.Name] = p
|
||||
}
|
||||
|
||||
for i := range cfg.Projects {
|
||||
proj := &cfg.Projects[i]
|
||||
if proj.Agent.Type != "claudecode" || len(proj.Agent.ProviderRefs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var compatible, incompatible []string
|
||||
for _, ref := range proj.Agent.ProviderRefs {
|
||||
gp, ok := globalByName[ref]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(gp.AgentTypes) > 0 {
|
||||
hasCodex := false
|
||||
for _, at := range gp.AgentTypes {
|
||||
if at == "codex" {
|
||||
hasCodex = true
|
||||
}
|
||||
}
|
||||
if hasCodex {
|
||||
compatible = append(compatible, ref)
|
||||
} else {
|
||||
incompatible = append(incompatible, ref)
|
||||
}
|
||||
} else {
|
||||
compatible = append(compatible, ref)
|
||||
}
|
||||
}
|
||||
|
||||
if len(incompatible) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("project %q: switching claudecode→codex: keep=%v remove=%v",
|
||||
proj.Name, compatible, incompatible)
|
||||
t.Logf("provider filtering validated: %d provider(s) would be removed on agent type change", len(incompatible))
|
||||
return
|
||||
}
|
||||
t.Skip("no project with incompatible providers found for agent type change test")
|
||||
}
|
||||
Reference in New Issue
Block a user