package service import ( "context" "io" "net/http" "strings" "testing" claude "github.com/Wei-Shaw/sub2api/internal/pkg/claude" "github.com/Wei-Shaw/sub2api/internal/pkg/tlsfingerprint" ) type bootstrapHTTPUpstreamStub struct { doCalled bool doWithTLSCalled bool lastProxyURL string lastAccountID int64 lastConcurrency int lastTLSProfile *tlsfingerprint.Profile lastRequestMethod string } func (s *bootstrapHTTPUpstreamStub) Do(req *http.Request, proxyURL string, accountID int64, accountConcurrency int) (*http.Response, error) { s.doCalled = true s.lastProxyURL = proxyURL s.lastAccountID = accountID s.lastConcurrency = accountConcurrency if req != nil { s.lastRequestMethod = req.Method } return &http.Response{ StatusCode: http.StatusNoContent, Body: io.NopCloser(strings.NewReader("")), Header: make(http.Header), }, nil } func (s *bootstrapHTTPUpstreamStub) DoWithTLS(req *http.Request, proxyURL string, accountID int64, accountConcurrency int, profile *tlsfingerprint.Profile) (*http.Response, error) { s.doWithTLSCalled = true s.lastProxyURL = proxyURL s.lastAccountID = accountID s.lastConcurrency = accountConcurrency s.lastTLSProfile = profile if req != nil { s.lastRequestMethod = req.Method } return &http.Response{ StatusCode: http.StatusNoContent, Body: io.NopCloser(strings.NewReader("")), Header: make(http.Header), }, nil } func TestBackgroundSimulatorApplyBackgroundHeaders_UsesClaudeCodeBackgroundProfile(t *testing.T) { bg := &backgroundSimulator{} state := &accountBackgroundState{ accessToken: "token-123", accountID: 42, instanceSalt: "salt-123", } req, err := http.NewRequest(http.MethodGet, "https://api.anthropic.com/api/claude_cli/bootstrap", nil) if err != nil { t.Fatalf("NewRequest() error = %v", err) } bg.applyBackgroundHeaders(req, state, "application/json") if got := req.Header.Get("Accept"); got != "application/json, text/plain, */*" { t.Fatalf("Accept = %q", got) } if got := req.Header.Get("Content-Type"); got != "application/json" { t.Fatalf("Content-Type = %q", got) } if got := req.Header.Get("User-Agent"); got != claude.DefaultCodeUserAgent() { t.Fatalf("User-Agent = %q, want %q", got, claude.DefaultCodeUserAgent()) } if got := req.Header.Get("Authorization"); got != "Bearer token-123" { t.Fatalf("Authorization = %q", got) } if got := req.Header.Get("anthropic-beta"); got != claude.BetaOAuth { t.Fatalf("anthropic-beta = %q", got) } if got := req.Header.Get("anthropic-version"); got != "" { t.Fatalf("anthropic-version = %q, want empty", got) } if got := req.Header.Get("x-app"); got != "" { t.Fatalf("x-app = %q, want empty", got) } if got := req.Header.Get("X-Claude-Code-Session-Id"); got != "" { t.Fatalf("X-Claude-Code-Session-Id = %q, want empty", got) } if got := req.Header.Get("x-client-request-id"); got != "" { t.Fatalf("x-client-request-id = %q, want empty", got) } } func TestBackgroundSimulatorDoRequest_UsesSharedUpstreamForHTTPS(t *testing.T) { stub := &bootstrapHTTPUpstreamStub{} bg := &backgroundSimulator{} profile := &tlsfingerprint.Profile{Name: "test-profile"} state := &accountBackgroundState{ accountID: 7, proxyURL: "http://127.0.0.1:8080", httpUpstream: stub, tlsProfile: profile, useSharedUpstream: true, } req, err := http.NewRequest(http.MethodGet, "https://api.anthropic.com/api/claude_code/policy_limits", nil) if err != nil { t.Fatalf("NewRequest() error = %v", err) } resp, err := bg.doRequest(context.Background(), state, req) if err != nil { t.Fatalf("doRequest() error = %v", err) } defer resp.Body.Close() if !stub.doWithTLSCalled { t.Fatal("expected DoWithTLS to be called for https background requests") } if stub.doCalled { t.Fatal("did not expect Do to be called for https background requests") } if stub.lastProxyURL != state.proxyURL { t.Fatalf("proxyURL = %q, want %q", stub.lastProxyURL, state.proxyURL) } if stub.lastAccountID != state.accountID { t.Fatalf("accountID = %d, want %d", stub.lastAccountID, state.accountID) } if stub.lastConcurrency != 0 { t.Fatalf("accountConcurrency = %d, want 0", stub.lastConcurrency) } if stub.lastTLSProfile != profile { t.Fatalf("TLS profile pointer mismatch") } if stub.lastRequestMethod != http.MethodGet { t.Fatalf("request method = %q, want %q", stub.lastRequestMethod, http.MethodGet) } }