package service import ( "context" "net/http" "net/http/httptest" "testing" "github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey" "github.com/stretchr/testify/require" ) func TestClaudeCodeValidator_ProbeBypass(t *testing.T) { validator := NewClaudeCodeValidator() req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/messages", nil) req.Header.Set("User-Agent", "claude-cli/1.2.3 (darwin; arm64)") req = req.WithContext(context.WithValue(req.Context(), ctxkey.IsMaxTokensOneHaikuRequest, true)) ok := validator.Validate(req, map[string]any{ "model": "claude-haiku-4-5", "max_tokens": 1, }) require.True(t, ok) } func TestClaudeCodeValidator_ProbeBypassRequiresUA(t *testing.T) { validator := NewClaudeCodeValidator() req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/messages", nil) req.Header.Set("User-Agent", "curl/8.0.0") req = req.WithContext(context.WithValue(req.Context(), ctxkey.IsMaxTokensOneHaikuRequest, true)) ok := validator.Validate(req, map[string]any{ "model": "claude-haiku-4-5", "max_tokens": 1, }) require.False(t, ok) } func TestClaudeCodeValidator_MessagesWithoutProbeStillNeedStrictValidation(t *testing.T) { validator := NewClaudeCodeValidator() req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/messages", nil) req.Header.Set("User-Agent", "claude-cli/1.2.3 (darwin; arm64)") ok := validator.Validate(req, map[string]any{ "model": "claude-haiku-4-5", "max_tokens": 1, }) require.False(t, ok) } func TestClaudeCodeValidator_NonMessagesPathUAOnly(t *testing.T) { validator := NewClaudeCodeValidator() req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/models", nil) req.Header.Set("User-Agent", "claude-cli/1.2.3 (darwin; arm64)") ok := validator.Validate(req, nil) require.True(t, ok) } func TestExtractVersion(t *testing.T) { v := NewClaudeCodeValidator() tests := []struct { ua string want string }{ {"claude-cli/2.1.22 (darwin; arm64)", "2.1.22"}, {"claude-cli/1.0.0", "1.0.0"}, {"Claude-CLI/3.10.5 (linux; x86_64)", "3.10.5"}, // 大小写不敏感 {"curl/8.0.0", ""}, // 非 Claude CLI {"", ""}, // 空字符串 {"claude-cli/", ""}, // 无版本号 {"claude-cli/2.1.22-beta", "2.1.22"}, // 带后缀仍提取主版本号 } for _, tt := range tests { got := v.ExtractVersion(tt.ua) require.Equal(t, tt.want, got, "ExtractVersion(%q)", tt.ua) } } func TestCompareVersions(t *testing.T) { tests := []struct { a, b string want int }{ {"2.1.0", "2.1.0", 0}, // 相等 {"2.1.1", "2.1.0", 1}, // patch 更大 {"2.0.0", "2.1.0", -1}, // minor 更小 {"3.0.0", "2.99.99", 1}, // major 更大 {"1.0.0", "2.0.0", -1}, // major 更小 {"0.0.1", "0.0.0", 1}, // patch 差异 {"", "1.0.0", -1}, // 空字符串 vs 正常版本 {"v2.1.0", "2.1.0", 0}, // v 前缀处理 } for _, tt := range tests { got := CompareVersions(tt.a, tt.b) require.Equal(t, tt.want, got, "CompareVersions(%q, %q)", tt.a, tt.b) } } func TestSetGetClaudeCodeVersion(t *testing.T) { ctx := context.Background() require.Equal(t, "", GetClaudeCodeVersion(ctx), "empty context should return empty string") ctx = SetClaudeCodeVersion(ctx, "2.1.63") require.Equal(t, "2.1.63", GetClaudeCodeVersion(ctx)) }