| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package provider
- import (
- "errors"
- "reflect"
- "sort"
- "strings"
- "testing"
- "github.com/kotoyuuko/cc-switch-cli/internal/config"
- )
- func TestUnionEnvKeys(t *testing.T) {
- in := map[string]config.Provider{
- "a": {Env: map[string]string{"X": "1", "Y": "2"}},
- "b": {Env: map[string]string{"Y": "3", "Z": "4"}},
- }
- got := UnionEnvKeys(in)
- want := []string{"X", "Y", "Z"}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("UnionEnvKeys = %v, want %v", got, want)
- }
- }
- func TestResolveEnvRefs_Literal(t *testing.T) {
- p := config.Provider{Env: map[string]string{"A": "plain", "B": "$HOME/api"}}
- got, err := ResolveEnvRefs(p, map[string]string{"HOME": "/root"})
- if err != nil {
- t.Fatal(err)
- }
- if got["A"] != "plain" {
- t.Errorf("A: %q", got["A"])
- }
- if got["B"] != "$HOME/api" {
- t.Errorf("B shouldn't be shell-expanded: %q", got["B"])
- }
- }
- func TestResolveEnvRefs_Ref(t *testing.T) {
- p := config.Provider{Env: map[string]string{
- "ANTHROPIC_API_KEY": "env:MY_KEY",
- "ANTHROPIC_BASE_URL": "https://x",
- }}
- snap := map[string]string{"MY_KEY": "sk-ok"}
- got, err := ResolveEnvRefs(p, snap)
- if err != nil {
- t.Fatal(err)
- }
- if got["ANTHROPIC_API_KEY"] != "sk-ok" {
- t.Errorf("ref not resolved: %q", got["ANTHROPIC_API_KEY"])
- }
- if got["ANTHROPIC_BASE_URL"] != "https://x" {
- t.Errorf("literal changed: %q", got["ANTHROPIC_BASE_URL"])
- }
- }
- func TestResolveEnvRefs_Missing(t *testing.T) {
- p := config.Provider{Env: map[string]string{"K": "env:MISSING"}}
- _, err := ResolveEnvRefs(p, map[string]string{})
- if err == nil {
- t.Fatal("expected error")
- }
- var ref *EnvRefError
- if !errors.As(err, &ref) {
- t.Fatalf("wrong error type: %T %v", err, err)
- }
- if ref.Key != "K" || ref.Var != "MISSING" {
- t.Errorf("unexpected: %#v", ref)
- }
- if !strings.Contains(err.Error(), "MISSING") {
- t.Errorf("message should name missing var: %q", err.Error())
- }
- }
- func TestResolveEnvRefs_NoChain(t *testing.T) {
- // `env:A` → snap has A="env:B", B="plain". We should get "env:B" literally.
- p := config.Provider{Env: map[string]string{"K": "env:A"}}
- snap := map[string]string{"A": "env:B", "B": "plain"}
- got, err := ResolveEnvRefs(p, snap)
- if err != nil {
- t.Fatal(err)
- }
- if got["K"] != "env:B" {
- t.Errorf("chain should NOT be followed; got %q", got["K"])
- }
- }
- func TestBuildChildEnv_CleansUnion(t *testing.T) {
- parent := []string{"HOME=/root", "ANTHROPIC_API_KEY=old", "PATH=/usr/bin"}
- union := []string{"ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL"}
- resolved := map[string]string{} // selected provider has NONE of those
- got := BuildChildEnv(parent, union, resolved)
- for _, kv := range got {
- if strings.HasPrefix(kv, "ANTHROPIC_API_KEY=") {
- t.Errorf("old API key leaked: %q", kv)
- }
- }
- // HOME/PATH should survive.
- want := map[string]bool{"HOME=/root": true, "PATH=/usr/bin": true}
- for _, kv := range got {
- delete(want, kv)
- }
- if len(want) != 0 {
- t.Errorf("unrelated vars dropped: %v", want)
- }
- }
- func TestBuildChildEnv_InjectOverrides(t *testing.T) {
- parent := []string{"HOME=/root", "ANTHROPIC_MODEL=x"}
- union := []string{} // empty — model not in anyone's provider env
- resolved := map[string]string{"ANTHROPIC_MODEL": "y"}
- got := BuildChildEnv(parent, union, resolved)
- // HOME present once, model = y exactly once
- var home, model int
- for _, kv := range got {
- if kv == "HOME=/root" {
- home++
- }
- if strings.HasPrefix(kv, "ANTHROPIC_MODEL=") {
- if kv != "ANTHROPIC_MODEL=y" {
- t.Errorf("wrong model value: %q", kv)
- }
- model++
- }
- }
- if home != 1 {
- t.Errorf("HOME count = %d", home)
- }
- if model != 1 {
- t.Errorf("model count = %d", model)
- }
- }
- func TestBuildChildEnv_DeterministicOrder(t *testing.T) {
- parent := []string{}
- union := []string{}
- resolved := map[string]string{"B": "2", "A": "1", "C": "3"}
- got := BuildChildEnv(parent, union, resolved)
- // The injected tail should be sorted.
- sorted := make([]string, len(got))
- copy(sorted, got)
- sort.Strings(sorted)
- if !reflect.DeepEqual(got, sorted) {
- t.Errorf("injected env not sorted: %v", got)
- }
- }
- func TestSnapshotEnv(t *testing.T) {
- got := SnapshotEnv([]string{"A=1", "B=2=3", "MALFORMED", "A=override"})
- if got["A"] != "override" {
- t.Errorf("last-wins: %q", got["A"])
- }
- if got["B"] != "2=3" {
- t.Errorf("first = only: %q", got["B"])
- }
- if _, ok := got["MALFORMED"]; ok {
- t.Error("malformed entry should be dropped")
- }
- }
|