| @@ -8,7 +8,9 @@ import ( · 7 unmodified lines |
| 8 | "net/http" | 8 | "net/http" |
| 9 | "net/http/httptest" | 9 | "net/http/httptest" |
| 10 | "strings" | 10 | "strings" |
| | 11 | "sync" |
| 11 | "testing" | 12 | "testing" |
| | 13 | "time" |
| 12 | | 14 | |
| 13 | "github.com/gin-gonic/gin" | 15 | "github.com/gin-gonic/gin" |
| 14 | "github.com/klauspost/compress/zstd" | 16 | "github.com/klauspost/compress/zstd" |
| @@ -206,6 +208,97 @@ func TestApplyClaudeHeaders_DoesNotDowngradeConfiguredBaselineOnFirstClaudeClien · 191 unmodified lines |
| 206 | assertClaudeFingerprint(t, newerClaudeReq.Header, "claude-cli/2.1.71 (external, cli)", "0.81.0", "v24.6.0", "Linux", "x64") | 208 | assertClaudeFingerprint(t, newerClaudeReq.Header, "claude-cli/2.1.71 (external, cli)", "0.81.0", "v24.6.0", "Linux", "x64") |
| 207 | } | 209 | } |
| 208 | | 210 | |
| | 211 | func TestResolveClaudeDeviceProfile_RechecksCacheBeforeStoringCandidate(t *testing.T) { |
| | 212 | resetClaudeDeviceProfileCache() |
| | 213 | stabilize := true |
| | 214 | |
| | 215 | cfg := &config.Config{ |
| | 216 | ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{ |
| | 217 | UserAgent: "claude-cli/2.1.60 (external, cli)", |
| | 218 | PackageVersion: "0.70.0", |
| | 219 | RuntimeVersion: "v22.0.0", |
| | 220 | OS: "MacOS", |
| | 221 | Arch: "arm64", |
| | 222 | StabilizeDeviceProfile: &stabilize, |
| | 223 | }, |
| | 224 | } |
| | 225 | auth := &cliproxyauth.Auth{ |
| | 226 | ID: "auth-racy-upgrade", |
| | 227 | Attributes: map[string]string{ |
| | 228 | "api_key": "key-racy-upgrade", |
| | 229 | }, |
| | 230 | } |
| | 231 | |
| | 232 | lowPaused := make(chan struct{}) |
| | 233 | releaseLow := make(chan struct{}) |
| | 234 | var pauseOnce sync.Once |
| | 235 | var releaseOnce sync.Once |
| | 236 | |
| | 237 | claudeDeviceProfileBeforeCandidateStore = func(candidate claudeDeviceProfile) { |
| | 238 | if candidate.UserAgent != "claude-cli/2.1.62 (external, cli)" { |
| | 239 | return |
| | 240 | } |
| | 241 | pauseOnce.Do(func() { close(lowPaused) }) |
| | 242 | <-releaseLow |
| | 243 | } |
| | 244 | t.Cleanup(func() { |
| | 245 | claudeDeviceProfileBeforeCandidateStore = nil |
| | 246 | releaseOnce.Do(func() { close(releaseLow) }) |
| | 247 | }) |
| | 248 | |
| | 249 | lowResultCh := make(chan claudeDeviceProfile, 1) |
| | 250 | go func() { |
| | 251 | lowResultCh <- resolveClaudeDeviceProfile(auth, "key-racy-upgrade", http.Header{ |
| | 252 | "User-Agent": []string{"claude-cli/2.1.62 (external, cli)"}, |
| | 253 | "X-Stainless-Package-Version": []string{"0.74.0"}, |
| | 254 | "X-Stainless-Runtime-Version": []string{"v24.3.0"}, |
| | 255 | "X-Stainless-Os": []string{"Linux"}, |
| | 256 | "X-Stainless-Arch": []string{"x64"}, |
| | 257 | }, cfg) |
| | 258 | }() |
| | 259 | |
| | 260 | select { |
| | 261 | case <-lowPaused: |
| | 262 | case <-time.After(2 * time.Second): |
| | 263 | t.Fatal("timed out waiting for lower candidate to pause before storing") |
| | 264 | } |
| | 265 | |
| | 266 | highResult := resolveClaudeDeviceProfile(auth, "key-racy-upgrade", http.Header{ |
| | 267 | "User-Agent": []string{"claude-cli/2.1.63 (external, cli)"}, |
| | 268 | "X-Stainless-Package-Version": []string{"0.75.0"}, |
| | 269 | "X-Stainless-Runtime-Version": []string{"v24.4.0"}, |
| | 270 | "X-Stainless-Os": []string{"MacOS"}, |
| | 271 | "X-Stainless-Arch": []string{"arm64"}, |
| | 272 | }, cfg) |
| | 273 | releaseOnce.Do(func() { close(releaseLow) }) |
| | 274 | |
| | 275 | select { |
| | 276 | case lowResult := <-lowResultCh: |
| | 277 | if lowResult.UserAgent != "claude-cli/2.1.63 (external, cli)" { |
| | 278 | t.Fatalf("lowResult.UserAgent = %q, want %q", lowResult.UserAgent, "claude-cli/2.1.63 (external, cli)") |
| | 279 | } |
| | 280 | if lowResult.PackageVersion != "0.75.0" { |
| | 281 | t.Fatalf("lowResult.PackageVersion = %q, want %q", lowResult.PackageVersion, "0.75.0") |
| | 282 | } |
| | 283 | case <-time.After(2 * time.Second): |
| | 284 | t.Fatal("timed out waiting for lower candidate result") |
| | 285 | } |
| | 286 | |
| | 287 | if highResult.UserAgent != "claude-cli/2.1.63 (external, cli)" { |
| | 288 | t.Fatalf("highResult.UserAgent = %q, want %q", highResult.UserAgent, "claude-cli/2.1.63 (external, cli)") |
| | 289 | } |
| | 290 | |
| | 291 | cached := resolveClaudeDeviceProfile(auth, "key-racy-upgrade", http.Header{ |
| | 292 | "User-Agent": []string{"curl/8.7.1"}, |
| | 293 | }, cfg) |
| | 294 | if cached.UserAgent != "claude-cli/2.1.63 (external, cli)" { |
| | 295 | t.Fatalf("cached.UserAgent = %q, want %q", cached.UserAgent, "claude-cli/2.1.63 (external, cli)") |
| | 296 | } |
| | 297 | if cached.PackageVersion != "0.75.0" { |
| | 298 | t.Fatalf("cached.PackageVersion = %q, want %q", cached.PackageVersion, "0.75.0") |
| | 299 | } |
| | 300 | } |
| | 301 | |
| 209 | func TestApplyClaudeHeaders_DisableDeviceProfileStabilization(t *testing.T) { | 302 | func TestApplyClaudeHeaders_DisableDeviceProfileStabilization(t *testing.T) { |
| 210 | resetClaudeDeviceProfileCache() | 303 | resetClaudeDeviceProfileCache() |
| 211 | | 304 | |