375 lines · 10.5 KB · Go
RawDownload
package amp import (	"testing" 	"github.com/router-for-me/CLIProxyAPI/v7/internal/config"	"github.com/router-for-me/CLIProxyAPI/v7/internal/registry") func TestNewModelMapper(t *testing.T) {	mappings := []config.AmpModelMapping{		{From: "claude-opus-4.5", To: "claude-sonnet-4"},		{From: "gpt-5", To: "gemini-2.5-pro"},	} 	mapper := NewModelMapper(mappings)	if mapper == nil {		t.Fatal("Expected non-nil mapper")	} 	result := mapper.GetMappings()	if len(result) != 2 {		t.Errorf("Expected 2 mappings, got %d", len(result))	}} func TestNewModelMapper_Empty(t *testing.T) {	mapper := NewModelMapper(nil)	if mapper == nil {		t.Fatal("Expected non-nil mapper")	} 	result := mapper.GetMappings()	if len(result) != 0 {		t.Errorf("Expected 0 mappings, got %d", len(result))	}} func TestModelMapper_MapModel_NoProvider(t *testing.T) {	mappings := []config.AmpModelMapping{		{From: "claude-opus-4.5", To: "claude-sonnet-4"},	} 	mapper := NewModelMapper(mappings) 	// Without a registered provider for the target, mapping should return empty	result := mapper.MapModel("claude-opus-4.5")	if result != "" {		t.Errorf("Expected empty result when target has no provider, got %s", result)	}} func TestModelMapper_MapModel_WithProvider(t *testing.T) {	// Register a mock provider for the target model	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client", "claude", []*registry.ModelInfo{		{ID: "claude-sonnet-4", OwnedBy: "anthropic", Type: "claude"},	})	defer reg.UnregisterClient("test-client") 	mappings := []config.AmpModelMapping{		{From: "claude-opus-4.5", To: "claude-sonnet-4"},	} 	mapper := NewModelMapper(mappings) 	// With a registered provider, mapping should work	result := mapper.MapModel("claude-opus-4.5")	if result != "claude-sonnet-4" {		t.Errorf("Expected claude-sonnet-4, got %s", result)	}} func TestModelMapper_MapModel_TargetWithThinkingSuffix(t *testing.T) {	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client-thinking", "codex", []*registry.ModelInfo{		{ID: "gpt-5.2", OwnedBy: "openai", Type: "codex"},	})	defer reg.UnregisterClient("test-client-thinking") 	mappings := []config.AmpModelMapping{		{From: "gpt-5.2-alias", To: "gpt-5.2(xhigh)"},	} 	mapper := NewModelMapper(mappings) 	result := mapper.MapModel("gpt-5.2-alias")	if result != "gpt-5.2(xhigh)" {		t.Errorf("Expected gpt-5.2(xhigh), got %s", result)	}} func TestModelMapper_MapModel_CaseInsensitive(t *testing.T) {	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client2", "claude", []*registry.ModelInfo{		{ID: "claude-sonnet-4", OwnedBy: "anthropic", Type: "claude"},	})	defer reg.UnregisterClient("test-client2") 	mappings := []config.AmpModelMapping{		{From: "Claude-Opus-4.5", To: "claude-sonnet-4"},	} 	mapper := NewModelMapper(mappings) 	// Should match case-insensitively	result := mapper.MapModel("claude-opus-4.5")	if result != "claude-sonnet-4" {		t.Errorf("Expected claude-sonnet-4, got %s", result)	}} func TestModelMapper_MapModel_NotFound(t *testing.T) {	mappings := []config.AmpModelMapping{		{From: "claude-opus-4.5", To: "claude-sonnet-4"},	} 	mapper := NewModelMapper(mappings) 	// Unknown model should return empty	result := mapper.MapModel("unknown-model")	if result != "" {		t.Errorf("Expected empty for unknown model, got %s", result)	}} func TestModelMapper_MapModel_EmptyInput(t *testing.T) {	mappings := []config.AmpModelMapping{		{From: "claude-opus-4.5", To: "claude-sonnet-4"},	} 	mapper := NewModelMapper(mappings) 	result := mapper.MapModel("")	if result != "" {		t.Errorf("Expected empty for empty input, got %s", result)	}} func TestModelMapper_UpdateMappings(t *testing.T) {	mapper := NewModelMapper(nil) 	// Initially empty	if len(mapper.GetMappings()) != 0 {		t.Error("Expected 0 initial mappings")	} 	// Update with new mappings	mapper.UpdateMappings([]config.AmpModelMapping{		{From: "model-a", To: "model-b"},		{From: "model-c", To: "model-d"},	}) 	result := mapper.GetMappings()	if len(result) != 2 {		t.Errorf("Expected 2 mappings after update, got %d", len(result))	} 	// Update again should replace, not append	mapper.UpdateMappings([]config.AmpModelMapping{		{From: "model-x", To: "model-y"},	}) 	result = mapper.GetMappings()	if len(result) != 1 {		t.Errorf("Expected 1 mapping after second update, got %d", len(result))	}} func TestModelMapper_UpdateMappings_SkipsInvalid(t *testing.T) {	mapper := NewModelMapper(nil) 	mapper.UpdateMappings([]config.AmpModelMapping{		{From: "", To: "model-b"},        // Invalid: empty from		{From: "model-a", To: ""},        // Invalid: empty to		{From: "  ", To: "model-b"},      // Invalid: whitespace from		{From: "model-c", To: "model-d"}, // Valid	}) 	result := mapper.GetMappings()	if len(result) != 1 {		t.Errorf("Expected 1 valid mapping, got %d", len(result))	}} func TestModelMapper_GetMappings_ReturnsCopy(t *testing.T) {	mappings := []config.AmpModelMapping{		{From: "model-a", To: "model-b"},	} 	mapper := NewModelMapper(mappings) 	// Get mappings and modify the returned map	result := mapper.GetMappings()	result["new-key"] = "new-value" 	// Original should be unchanged	original := mapper.GetMappings()	if len(original) != 1 {		t.Errorf("Expected original to have 1 mapping, got %d", len(original))	}	if _, exists := original["new-key"]; exists {		t.Error("Original map was modified")	}} func TestModelMapper_Regex_MatchBaseWithoutParens(t *testing.T) {	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client-regex-1", "gemini", []*registry.ModelInfo{		{ID: "gemini-2.5-pro", OwnedBy: "google", Type: "gemini"},	})	defer reg.UnregisterClient("test-client-regex-1") 	mappings := []config.AmpModelMapping{		{From: "^gpt-5$", To: "gemini-2.5-pro", Regex: true},	} 	mapper := NewModelMapper(mappings) 	// Incoming model has reasoning suffix, regex matches base, suffix is preserved	result := mapper.MapModel("gpt-5(high)")	if result != "gemini-2.5-pro(high)" {		t.Errorf("Expected gemini-2.5-pro(high), got %s", result)	}} func TestModelMapper_Regex_ExactPrecedence(t *testing.T) {	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client-regex-2", "claude", []*registry.ModelInfo{		{ID: "claude-sonnet-4", OwnedBy: "anthropic", Type: "claude"},	})	reg.RegisterClient("test-client-regex-3", "gemini", []*registry.ModelInfo{		{ID: "gemini-2.5-pro", OwnedBy: "google", Type: "gemini"},	})	defer reg.UnregisterClient("test-client-regex-2")	defer reg.UnregisterClient("test-client-regex-3") 	mappings := []config.AmpModelMapping{		{From: "gpt-5", To: "claude-sonnet-4"},                 // exact		{From: "^gpt-5.*$", To: "gemini-2.5-pro", Regex: true}, // regex	} 	mapper := NewModelMapper(mappings) 	// Exact match should win over regex	result := mapper.MapModel("gpt-5")	if result != "claude-sonnet-4" {		t.Errorf("Expected claude-sonnet-4, got %s", result)	}} func TestModelMapper_Regex_InvalidPattern_Skipped(t *testing.T) {	// Invalid regex should be skipped and not cause panic	mappings := []config.AmpModelMapping{		{From: "(", To: "target", Regex: true},	} 	mapper := NewModelMapper(mappings) 	result := mapper.MapModel("anything")	if result != "" {		t.Errorf("Expected empty result due to invalid regex, got %s", result)	}} func TestModelMapper_Regex_CaseInsensitive(t *testing.T) {	reg := registry.GetGlobalRegistry()	reg.RegisterClient("test-client-regex-4", "claude", []*registry.ModelInfo{		{ID: "claude-sonnet-4", OwnedBy: "anthropic", Type: "claude"},	})	defer reg.UnregisterClient("test-client-regex-4") 	mappings := []config.AmpModelMapping{		{From: "^CLAUDE-OPUS-.*$", To: "claude-sonnet-4", Regex: true},	} 	mapper := NewModelMapper(mappings) 	result := mapper.MapModel("claude-opus-4.5")	if result != "claude-sonnet-4" {		t.Errorf("Expected claude-sonnet-4, got %s", result)	}} func TestModelMapper_SuffixPreservation(t *testing.T) {	reg := registry.GetGlobalRegistry() 	// Register test models	reg.RegisterClient("test-client-suffix", "gemini", []*registry.ModelInfo{		{ID: "gemini-2.5-pro", OwnedBy: "google", Type: "gemini"},	})	reg.RegisterClient("test-client-suffix-2", "claude", []*registry.ModelInfo{		{ID: "claude-sonnet-4", OwnedBy: "anthropic", Type: "claude"},	})	defer reg.UnregisterClient("test-client-suffix")	defer reg.UnregisterClient("test-client-suffix-2") 	tests := []struct {		name     string		mappings []config.AmpModelMapping		input    string		want     string	}{		{			name:     "numeric suffix preserved",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p(8192)",			want:     "gemini-2.5-pro(8192)",		},		{			name:     "level suffix preserved",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p(high)",			want:     "gemini-2.5-pro(high)",		},		{			name:     "no suffix unchanged",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p",			want:     "gemini-2.5-pro",		},		{			name:     "config suffix takes priority",			mappings: []config.AmpModelMapping{{From: "alias", To: "gemini-2.5-pro(medium)"}},			input:    "alias(high)",			want:     "gemini-2.5-pro(medium)",		},		{			name:     "regex with suffix preserved",			mappings: []config.AmpModelMapping{{From: "^g25.*", To: "gemini-2.5-pro", Regex: true}},			input:    "g25p(8192)",			want:     "gemini-2.5-pro(8192)",		},		{			name:     "auto suffix preserved",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p(auto)",			want:     "gemini-2.5-pro(auto)",		},		{			name:     "none suffix preserved",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p(none)",			want:     "gemini-2.5-pro(none)",		},		{			name:     "case insensitive base lookup with suffix",			mappings: []config.AmpModelMapping{{From: "G25P", To: "gemini-2.5-pro"}},			input:    "g25p(high)",			want:     "gemini-2.5-pro(high)",		},		{			name:     "empty suffix filtered out",			mappings: []config.AmpModelMapping{{From: "g25p", To: "gemini-2.5-pro"}},			input:    "g25p()",			want:     "gemini-2.5-pro",		},		{			name:     "incomplete suffix treated as no suffix",			mappings: []config.AmpModelMapping{{From: "g25p(high", To: "gemini-2.5-pro"}},			input:    "g25p(high",			want:     "gemini-2.5-pro",		},	} 	for _, tt := range tests {		t.Run(tt.name, func(t *testing.T) {			mapper := NewModelMapper(tt.mappings)			got := mapper.MapModel(tt.input)			if got != tt.want {				t.Errorf("MapModel(%q) = %q, want %q", tt.input, got, tt.want)			}		})	}}