Files
CLIProxyAPI/internal/watcher/synthesizer/helpers_test.go

265 lines
6.1 KiB
Go

package synthesizer
import (
"reflect"
"strings"
"testing"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
)
func TestNewStableIDGenerator(t *testing.T) {
gen := NewStableIDGenerator()
if gen == nil {
t.Fatal("expected non-nil generator")
}
if gen.counters == nil {
t.Fatal("expected non-nil counters map")
}
}
func TestStableIDGenerator_Next(t *testing.T) {
tests := []struct {
name string
kind string
parts []string
wantPrefix string
}{
{
name: "basic gemini apikey",
kind: "gemini:apikey",
parts: []string{"test-key", ""},
wantPrefix: "gemini:apikey:",
},
{
name: "claude with base url",
kind: "claude:apikey",
parts: []string{"sk-ant-xxx", "https://api.anthropic.com"},
wantPrefix: "claude:apikey:",
},
{
name: "empty parts",
kind: "codex:apikey",
parts: []string{},
wantPrefix: "codex:apikey:",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gen := NewStableIDGenerator()
id, short := gen.Next(tt.kind, tt.parts...)
if !strings.Contains(id, tt.wantPrefix) {
t.Errorf("expected id to contain %q, got %q", tt.wantPrefix, id)
}
if short == "" {
t.Error("expected non-empty short id")
}
if len(short) != 12 {
t.Errorf("expected short id length 12, got %d", len(short))
}
})
}
}
func TestStableIDGenerator_Stability(t *testing.T) {
gen1 := NewStableIDGenerator()
gen2 := NewStableIDGenerator()
id1, _ := gen1.Next("gemini:apikey", "test-key", "https://api.example.com")
id2, _ := gen2.Next("gemini:apikey", "test-key", "https://api.example.com")
if id1 != id2 {
t.Errorf("same inputs should produce same ID: got %q and %q", id1, id2)
}
}
func TestStableIDGenerator_CollisionHandling(t *testing.T) {
gen := NewStableIDGenerator()
id1, short1 := gen.Next("gemini:apikey", "same-key")
id2, short2 := gen.Next("gemini:apikey", "same-key")
if id1 == id2 {
t.Error("collision should be handled with suffix")
}
if short1 == short2 {
t.Error("short ids should differ")
}
if !strings.Contains(short2, "-1") {
t.Errorf("second short id should contain -1 suffix, got %q", short2)
}
}
func TestStableIDGenerator_NilReceiver(t *testing.T) {
var gen *StableIDGenerator = nil
id, short := gen.Next("test:kind", "part")
if id != "test:kind:000000000000" {
t.Errorf("expected test:kind:000000000000, got %q", id)
}
if short != "000000000000" {
t.Errorf("expected 000000000000, got %q", short)
}
}
func TestApplyAuthExcludedModelsMeta(t *testing.T) {
tests := []struct {
name string
auth *coreauth.Auth
cfg *config.Config
perKey []string
authKind string
wantHash bool
wantKind string
}{
{
name: "apikey with excluded models",
auth: &coreauth.Auth{
Provider: "gemini",
Attributes: make(map[string]string),
},
cfg: &config.Config{},
perKey: []string{"model-a", "model-b"},
authKind: "apikey",
wantHash: true,
wantKind: "apikey",
},
{
name: "oauth with provider excluded models",
auth: &coreauth.Auth{
Provider: "claude",
Attributes: make(map[string]string),
},
cfg: &config.Config{
OAuthExcludedModels: map[string][]string{
"claude": {"claude-2.0"},
},
},
perKey: nil,
authKind: "oauth",
wantHash: true,
wantKind: "oauth",
},
{
name: "nil auth",
auth: nil,
cfg: &config.Config{},
},
{
name: "nil config",
auth: &coreauth.Auth{Provider: "test"},
cfg: nil,
authKind: "apikey",
},
{
name: "nil attributes initialized",
auth: &coreauth.Auth{
Provider: "gemini",
Attributes: nil,
},
cfg: &config.Config{},
perKey: []string{"model-x"},
authKind: "apikey",
wantHash: true,
wantKind: "apikey",
},
{
name: "apikey with duplicate excluded models",
auth: &coreauth.Auth{
Provider: "gemini",
Attributes: make(map[string]string),
},
cfg: &config.Config{},
perKey: []string{"model-a", "MODEL-A", "model-b", "model-a"},
authKind: "apikey",
wantHash: true,
wantKind: "apikey",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ApplyAuthExcludedModelsMeta(tt.auth, tt.cfg, tt.perKey, tt.authKind)
if tt.auth != nil && tt.cfg != nil {
if tt.wantHash {
if _, ok := tt.auth.Attributes["excluded_models_hash"]; !ok {
t.Error("expected excluded_models_hash in attributes")
}
}
if tt.wantKind != "" {
if got := tt.auth.Attributes["auth_kind"]; got != tt.wantKind {
t.Errorf("expected auth_kind=%s, got %s", tt.wantKind, got)
}
}
}
})
}
}
func TestAddConfigHeadersToAttrs(t *testing.T) {
tests := []struct {
name string
headers map[string]string
attrs map[string]string
want map[string]string
}{
{
name: "basic headers",
headers: map[string]string{
"Authorization": "Bearer token",
"X-Custom": "value",
},
attrs: map[string]string{"existing": "key"},
want: map[string]string{
"existing": "key",
"header:Authorization": "Bearer token",
"header:X-Custom": "value",
},
},
{
name: "empty headers",
headers: map[string]string{},
attrs: map[string]string{"existing": "key"},
want: map[string]string{"existing": "key"},
},
{
name: "nil headers",
headers: nil,
attrs: map[string]string{"existing": "key"},
want: map[string]string{"existing": "key"},
},
{
name: "nil attrs",
headers: map[string]string{"key": "value"},
attrs: nil,
want: nil,
},
{
name: "skip empty keys and values",
headers: map[string]string{
"": "value",
"key": "",
" ": "value",
"valid": "valid-value",
},
attrs: make(map[string]string),
want: map[string]string{
"header:valid": "valid-value",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
addConfigHeadersToAttrs(tt.headers, tt.attrs)
if !reflect.DeepEqual(tt.attrs, tt.want) {
t.Errorf("expected %v, got %v", tt.want, tt.attrs)
}
})
}
}