From cc796ced8818dcd5c4c8b49d97187e6d2b7acb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 15 Mar 2025 14:59:19 +0100 Subject: [PATCH 1/7] Respect transport of custom http client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- core/auth/auth.go | 29 ++++++++++--- core/auth/auth_test.go | 13 +++--- core/clients/key_flow.go | 41 +++++++++---------- .../key_flow_continuous_refresh_test.go | 11 +++-- core/clients/key_flow_test.go | 24 +++++++---- core/clients/no_auth_flow.go | 17 +++++--- core/clients/no_auth_flow_test.go | 14 ++++--- core/clients/token_flow.go | 23 +++++------ core/clients/token_flow_test.go | 8 ++-- 9 files changed, 107 insertions(+), 73 deletions(-) diff --git a/core/auth/auth.go b/core/auth/auth.go index 19e400f94..50a096d50 100644 --- a/core/auth/auth.go +++ b/core/auth/auth.go @@ -45,7 +45,7 @@ func SetupAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { if cfg.CustomAuth != nil { return cfg.CustomAuth, nil } else if cfg.NoAuth { - noAuthRoundTripper, err := NoAuth() + noAuthRoundTripper, err := NoAuth(cfg) if err != nil { return nil, fmt.Errorf("configuring no auth client: %w", err) } @@ -98,10 +98,17 @@ func DefaultAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { // NoAuth configures a flow without authentication and returns an http.RoundTripper // that can be used to make unauthenticated requests -func NoAuth() (rt http.RoundTripper, err error) { +func NoAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { noAuthConfig := clients.NoAuthFlowConfig{} noAuthRoundTripper := &clients.NoAuthFlow{} - if err := noAuthRoundTripper.Init(noAuthConfig); err != nil { + + if cfg.HTTPClient == nil { + cfg.HTTPClient = &http.Client{ + Timeout: clients.DefaultClientTimeout, + } + } + + if err := noAuthRoundTripper.Init(noAuthConfig, cfg.HTTPClient.Transport); err != nil { return nil, fmt.Errorf("initializing client: %w", err) } return noAuthRoundTripper, nil @@ -130,8 +137,14 @@ func TokenAuth(cfg *config.Configuration) (http.RoundTripper, error) { ServiceAccountToken: cfg.Token, } + if cfg.HTTPClient == nil { + cfg.HTTPClient = &http.Client{ + Timeout: clients.DefaultClientTimeout, + } + } + client := &clients.TokenFlow{} - if err := client.Init(&tokenCfg); err != nil { + if err := client.Init(&tokenCfg, cfg.HTTPClient.Transport); err != nil { return nil, fmt.Errorf("error initializing client: %w", err) } @@ -187,8 +200,14 @@ func KeyAuth(cfg *config.Configuration) (http.RoundTripper, error) { BackgroundTokenRefreshContext: cfg.BackgroundTokenRefreshContext, } + if cfg.HTTPClient == nil { + cfg.HTTPClient = &http.Client{ + Timeout: clients.DefaultClientTimeout, + } + } + client := &clients.KeyFlow{} - if err := client.Init(&keyCfg); err != nil { + if err := client.Init(&keyCfg, cfg.HTTPClient.Transport); err != nil { return nil, fmt.Errorf("error initializing client: %w", err) } diff --git a/core/auth/auth_test.go b/core/auth/auth_test.go index 65647bd68..b94af6d03 100644 --- a/core/auth/auth_test.go +++ b/core/auth/auth_test.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/json" "encoding/pem" + "net/http" "os" "reflect" "testing" @@ -125,6 +126,7 @@ func TestSetupAuth(t *testing.T) { t.Fatalf("Creating temporary file: %s", err) } defer func() { + _ = credentialsKeyFile.Close() err := os.Remove(credentialsKeyFile.Name()) if err != nil { t.Fatalf("Removing temporary file: %s", err) @@ -361,6 +363,7 @@ func TestDefaultAuth(t *testing.T) { t.Fatalf("Creating temporary file: %s", err) } defer func() { + _ = saKeyFile.Close() err := os.Remove(saKeyFile.Name()) if err != nil { t.Fatalf("Removing temporary file: %s", err) @@ -377,19 +380,13 @@ func TestDefaultAuth(t *testing.T) { t.Fatalf("Writing private key to temporary file: %s", err) } - defer func() { - err := saKeyFile.Close() - if err != nil { - t.Fatalf("Removing temporary file: %s", err) - } - }() - // create a credentials file with saKey and private key credentialsKeyFile, errs := os.CreateTemp("", "temp-*.txt") if errs != nil { t.Fatalf("Creating temporary file: %s", err) } defer func() { + _ = credentialsKeyFile.Close() err := os.Remove(credentialsKeyFile.Name()) if err != nil { t.Fatalf("Removing temporary file: %s", err) @@ -681,7 +678,7 @@ func TestNoAuth(t *testing.T) { } { t.Run(test.desc, func(t *testing.T) { setTemporaryHome(t) // Get the default authentication client and ensure that it's not nil - authClient, err := NoAuth() + authClient, err := NoAuth(&config.Configuration{HTTPClient: http.DefaultClient}) if err != nil { t.Fatalf("Test returned error on valid test case: %v", err) } diff --git a/core/clients/key_flow.go b/core/clients/key_flow.go index 59c90bd41..87b865109 100644 --- a/core/clients/key_flow.go +++ b/core/clients/key_flow.go @@ -34,9 +34,9 @@ const ( // KeyFlow handles auth with SA key type KeyFlow struct { - client *http.Client + rt http.RoundTripper + authClient *http.Client config *KeyFlowConfig - doer func(req *http.Request) (resp *http.Response, err error) key *ServiceAccountKeyResponse privateKey *rsa.PrivateKey privateKeyPEM []byte @@ -116,7 +116,7 @@ func (c *KeyFlow) GetToken() TokenResponseBody { return *c.token } -func (c *KeyFlow) Init(cfg *KeyFlowConfig) error { +func (c *KeyFlow) Init(cfg *KeyFlowConfig, rt http.RoundTripper) error { // No concurrency at this point, so no mutex check needed c.token = &TokenResponseBody{} c.config = cfg @@ -124,7 +124,17 @@ func (c *KeyFlow) Init(cfg *KeyFlowConfig) error { if c.config.TokenUrl == "" { c.config.TokenUrl = tokenAPI } - c.configureHTTPClient() + + if rt == nil { + rt = http.DefaultTransport + } + + c.rt = rt + c.authClient = &http.Client{ + Transport: rt, + Timeout: DefaultClientTimeout, + } + err := c.validate() if err != nil { return err @@ -163,7 +173,7 @@ func (c *KeyFlow) SetToken(accessToken, refreshToken string) error { // Roundtrip performs the request func (c *KeyFlow) RoundTrip(req *http.Request) (*http.Response, error) { - if c.client == nil { + if c.rt == nil { return nil, fmt.Errorf("please run Init()") } @@ -172,13 +182,13 @@ func (c *KeyFlow) RoundTrip(req *http.Request) (*http.Response, error) { return nil, err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken)) - return c.doer(req) + return c.rt.RoundTrip(req) } // GetAccessToken returns a short-lived access token and saves the access and refresh tokens in the token field func (c *KeyFlow) GetAccessToken() (string, error) { - if c.client == nil { - return "", fmt.Errorf("nil http client, please run Init()") + if c.rt == nil { + return "", fmt.Errorf("nil http round tripper, please run Init()") } c.tokenMutex.RLock() @@ -203,14 +213,6 @@ func (c *KeyFlow) GetAccessToken() (string, error) { return accessToken, nil } -// configureHTTPClient configures the HTTP client -func (c *KeyFlow) configureHTTPClient() { - client := &http.Client{} - client.Timeout = DefaultClientTimeout - c.client = client - c.doer = c.client.Do -} - // validate the client is configured well func (c *KeyFlow) validate() error { if c.config.ServiceAccountKey == nil { @@ -279,10 +281,6 @@ func (c *KeyFlow) createAccessToken() (err error) { // createAccessTokenWithRefreshToken creates an access token using // an existing pre-validated refresh token func (c *KeyFlow) createAccessTokenWithRefreshToken() (err error) { - if c.client == nil { - return fmt.Errorf("nil http client, please run Init()") - } - c.tokenMutex.RLock() refreshToken := c.token.RefreshToken c.tokenMutex.RUnlock() @@ -334,7 +332,8 @@ func (c *KeyFlow) requestToken(grant, assertion string) (*http.Response, error) return nil, err } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - return c.doer(req) + + return c.authClient.Do(req) } // parseTokenResponse parses the response from the server diff --git a/core/clients/key_flow_continuous_refresh_test.go b/core/clients/key_flow_continuous_refresh_test.go index 960086636..76b29f55a 100644 --- a/core/clients/key_flow_continuous_refresh_test.go +++ b/core/clients/key_flow_continuous_refresh_test.go @@ -137,8 +137,9 @@ func TestContinuousRefreshToken(t *testing.T) { config: &KeyFlowConfig{ BackgroundTokenRefreshContext: ctx, }, - client: &http.Client{}, - doer: mockDo, + authClient: &http.Client{ + Transport: mockTransportFn{mockDo}, + }, token: &TokenResponseBody{ AccessToken: accessToken, RefreshToken: refreshToken, @@ -328,11 +329,13 @@ func TestContinuousRefreshTokenConcurrency(t *testing.T) { } keyFlow := &KeyFlow{ - client: &http.Client{}, config: &KeyFlowConfig{ BackgroundTokenRefreshContext: ctx, }, - doer: mockDo, + authClient: &http.Client{ + Transport: mockTransportFn{mockDo}, + }, + rt: mockTransportFn{mockDo}, token: &TokenResponseBody{ AccessToken: accessTokenFirst, RefreshToken: refreshToken, diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index 33645a2c6..15a4985d5 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -5,6 +5,7 @@ import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "fmt" "io" "net/http" @@ -113,7 +114,7 @@ func TestKeyFlowInit(t *testing.T) { } cfg.ServiceAccountKey = tt.serviceAccountKey - if err := c.Init(cfg); (err != nil) != tt.wantErr { + if err := c.Init(cfg, http.DefaultTransport); (err != nil) != tt.wantErr { t.Errorf("KeyFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } if c.config == nil { @@ -268,13 +269,14 @@ func TestRequestToken(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - mockDo := func(_ *http.Request) (resp *http.Response, err error) { - return tt.mockResponse, tt.mockError - } - c := &KeyFlow{ + authClient: &http.Client{ + Transport: mockTransportFn{func(_ *http.Request) (*http.Response, error) { + return tt.mockResponse, tt.mockError + }}, + }, config: &KeyFlowConfig{}, - doer: mockDo, + rt: http.DefaultTransport, } res, err := c.requestToken(tt.grant, tt.assertion) @@ -289,7 +291,7 @@ func TestRequestToken(t *testing.T) { if tt.expectedError != nil { if err == nil { t.Errorf("Expected error '%v' but no error was returned", tt.expectedError) - } else if tt.expectedError.Error() != err.Error() { + } else if errors.Is(err, tt.expectedError) { t.Errorf("Error is not correct. Expected %v, got %v", tt.expectedError, err) } } else { @@ -303,3 +305,11 @@ func TestRequestToken(t *testing.T) { }) } } + +type mockTransportFn struct { + fn func(req *http.Request) (*http.Response, error) +} + +func (m mockTransportFn) RoundTrip(req *http.Request) (*http.Response, error) { + return m.fn(req) +} diff --git a/core/clients/no_auth_flow.go b/core/clients/no_auth_flow.go index 4db1bf156..a0e06916f 100644 --- a/core/clients/no_auth_flow.go +++ b/core/clients/no_auth_flow.go @@ -6,7 +6,7 @@ import ( ) type NoAuthFlow struct { - client *http.Client + rt http.RoundTripper config *NoAuthFlowConfig } @@ -24,18 +24,23 @@ func (c *NoAuthFlow) GetConfig() NoAuthFlowConfig { return *c.config } -func (c *NoAuthFlow) Init(_ NoAuthFlowConfig) error { +func (c *NoAuthFlow) Init(_ NoAuthFlowConfig, rt http.RoundTripper) error { c.config = &NoAuthFlowConfig{} - c.client = &http.Client{ - Timeout: DefaultClientTimeout, + + if rt == nil { + rt = http.DefaultTransport } + + c.rt = rt + return nil } // Roundtrip performs the request func (c *NoAuthFlow) RoundTrip(req *http.Request) (*http.Response, error) { - if c.client == nil { + if c.rt == nil { return nil, fmt.Errorf("please run Init()") } - return c.client.Do(req) + + return c.rt.RoundTrip(req) } diff --git a/core/clients/no_auth_flow_test.go b/core/clients/no_auth_flow_test.go index c3d6f15ad..4a8970495 100644 --- a/core/clients/no_auth_flow_test.go +++ b/core/clients/no_auth_flow_test.go @@ -25,7 +25,7 @@ func TestNoAuthFlow_Init(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &NoAuthFlow{} - if err := c.Init(tt.args.cfg); (err != nil) != tt.wantErr { + if err := c.Init(tt.args.cfg, http.DefaultTransport); (err != nil) != tt.wantErr { t.Errorf("NoAuthFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } }) @@ -34,7 +34,7 @@ func TestNoAuthFlow_Init(t *testing.T) { func TestNoAuthFlow_Do(t *testing.T) { type fields struct { - client *http.Client + rt http.RoundTripper } type args struct{} tests := []struct { @@ -45,8 +45,10 @@ func TestNoAuthFlow_Do(t *testing.T) { wantErr bool }{ { - name: "fail", - fields: fields{nil}, + name: "fail", + fields: fields{ + nil, + }, args: args{}, want: 0, wantErr: true, @@ -54,7 +56,7 @@ func TestNoAuthFlow_Do(t *testing.T) { { name: "success", fields: fields{ - &http.Client{}, + http.DefaultTransport, }, args: args{}, want: http.StatusOK, @@ -64,7 +66,7 @@ func TestNoAuthFlow_Do(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &NoAuthFlow{ - client: tt.fields.client, + rt: tt.fields.rt, } handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/core/clients/token_flow.go b/core/clients/token_flow.go index 3748c4dc8..2d5a69497 100644 --- a/core/clients/token_flow.go +++ b/core/clients/token_flow.go @@ -13,7 +13,7 @@ const ( // TokenFlow handles auth with SA static token type TokenFlow struct { - client *http.Client + rt http.RoundTripper config *TokenFlowConfig } @@ -34,17 +34,16 @@ func (c *TokenFlow) GetConfig() TokenFlowConfig { return *c.config } -func (c *TokenFlow) Init(cfg *TokenFlowConfig) error { +func (c *TokenFlow) Init(cfg *TokenFlowConfig, rt http.RoundTripper) error { c.config = cfg - c.configureHTTPClient() - return c.validate() -} -// configureHTTPClient configures the HTTP client -func (c *TokenFlow) configureHTTPClient() { - client := &http.Client{} - client.Timeout = DefaultClientTimeout - c.client = client + if rt == nil { + rt = http.DefaultTransport + } + + c.rt = rt + + return c.validate() } // validate the client is configured well @@ -57,9 +56,9 @@ func (c *TokenFlow) validate() error { // Roundtrip performs the request func (c *TokenFlow) RoundTrip(req *http.Request) (*http.Response, error) { - if c.client == nil { + if c.rt == nil { return nil, fmt.Errorf("please run Init()") } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.config.ServiceAccountToken)) - return c.client.Do(req) + return c.rt.RoundTrip(req) } diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index 8294709cd..1fffd2e6b 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -35,7 +35,7 @@ func TestTokenFlow_Init(t *testing.T) { if err != nil { t.Fatalf("Setting service account token: %s", err) } - if err := c.Init(tt.args.cfg); (err != nil) != tt.wantErr { + if err := c.Init(tt.args.cfg, http.DefaultTransport); (err != nil) != tt.wantErr { t.Errorf("TokenFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } err = os.Setenv(ServiceAccountToken, b) @@ -51,7 +51,7 @@ func TestTokenFlow_Init(t *testing.T) { func TestTokenFlow_Do(t *testing.T) { type fields struct { - client *http.Client + rt http.RoundTripper config *TokenFlowConfig } type args struct{} @@ -63,12 +63,12 @@ func TestTokenFlow_Do(t *testing.T) { wantErr bool }{ {"fail", fields{nil, nil}, args{}, 0, true}, - {"success", fields{&http.Client{}, &TokenFlowConfig{}}, args{}, http.StatusOK, false}, + {"success", fields{http.DefaultTransport, &TokenFlowConfig{}}, args{}, http.StatusOK, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &TokenFlow{ - client: tt.fields.client, + rt: tt.fields.rt, config: tt.fields.config, } handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { From ddd89c0f34ca59e6f4c77b53d8c6b6da4017bfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 17 Mar 2025 17:46:31 +0100 Subject: [PATCH 2/7] try to no break existing code --- core/auth/auth.go | 34 ++++++++++++++++--------------- core/auth/auth_test.go | 22 ++++++++++++++++++++ core/clients/key_flow.go | 17 +++++++++------- core/clients/key_flow_test.go | 2 +- core/clients/no_auth_flow.go | 13 ++++++------ core/clients/no_auth_flow_test.go | 3 ++- core/clients/token_flow.go | 13 ++++++------ core/clients/token_flow_test.go | 6 +++++- 8 files changed, 70 insertions(+), 40 deletions(-) diff --git a/core/auth/auth.go b/core/auth/auth.go index 50a096d50..7da6c9687 100644 --- a/core/auth/auth.go +++ b/core/auth/auth.go @@ -98,17 +98,23 @@ func DefaultAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { // NoAuth configures a flow without authentication and returns an http.RoundTripper // that can be used to make unauthenticated requests -func NoAuth(cfg *config.Configuration) (rt http.RoundTripper, err error) { +func NoAuth(cfgs ...*config.Configuration) (rt http.RoundTripper, err error) { noAuthConfig := clients.NoAuthFlowConfig{} noAuthRoundTripper := &clients.NoAuthFlow{} - if cfg.HTTPClient == nil { - cfg.HTTPClient = &http.Client{ - Timeout: clients.DefaultClientTimeout, - } + var cfg *config.Configuration + + if len(cfgs) > 0 { + cfg = cfgs[0] + } else { + cfg = &config.Configuration{} } - if err := noAuthRoundTripper.Init(noAuthConfig, cfg.HTTPClient.Transport); err != nil { + if cfg.HTTPClient != nil && cfg.HTTPClient.Transport != nil { + noAuthConfig.HTTPTransport = cfg.HTTPClient.Transport + } + + if err := noAuthRoundTripper.Init(noAuthConfig); err != nil { return nil, fmt.Errorf("initializing client: %w", err) } return noAuthRoundTripper, nil @@ -137,14 +143,12 @@ func TokenAuth(cfg *config.Configuration) (http.RoundTripper, error) { ServiceAccountToken: cfg.Token, } - if cfg.HTTPClient == nil { - cfg.HTTPClient = &http.Client{ - Timeout: clients.DefaultClientTimeout, - } + if cfg.HTTPClient != nil && cfg.HTTPClient.Transport != nil { + tokenCfg.HTTPTransport = cfg.HTTPClient.Transport } client := &clients.TokenFlow{} - if err := client.Init(&tokenCfg, cfg.HTTPClient.Transport); err != nil { + if err := client.Init(&tokenCfg); err != nil { return nil, fmt.Errorf("error initializing client: %w", err) } @@ -200,14 +204,12 @@ func KeyAuth(cfg *config.Configuration) (http.RoundTripper, error) { BackgroundTokenRefreshContext: cfg.BackgroundTokenRefreshContext, } - if cfg.HTTPClient == nil { - cfg.HTTPClient = &http.Client{ - Timeout: clients.DefaultClientTimeout, - } + if cfg.HTTPClient != nil && cfg.HTTPClient.Transport != nil { + keyCfg.HTTPTransport = cfg.HTTPClient.Transport } client := &clients.KeyFlow{} - if err := client.Init(&keyCfg, cfg.HTTPClient.Transport); err != nil { + if err := client.Init(&keyCfg); err != nil { return nil, fmt.Errorf("error initializing client: %w", err) } diff --git a/core/auth/auth_test.go b/core/auth/auth_test.go index b94af6d03..413399bde 100644 --- a/core/auth/auth_test.go +++ b/core/auth/auth_test.go @@ -669,6 +669,28 @@ func TestKeyAuth(t *testing.T) { } func TestNoAuth(t *testing.T) { + for _, test := range []struct { + desc string + }{ + { + desc: "valid_case", + }, + } { + t.Run(test.desc, func(t *testing.T) { + setTemporaryHome(t) // Get the default authentication client and ensure that it's not nil + authClient, err := NoAuth() + if err != nil { + t.Fatalf("Test returned error on valid test case: %v", err) + } + + if authClient == nil { + t.Fatalf("Client returned is nil for valid test case") + } + }) + } +} + +func TestNoAuthWithConfig(t *testing.T) { for _, test := range []struct { desc string }{ diff --git a/core/clients/key_flow.go b/core/clients/key_flow.go index 87b865109..eb9458414 100644 --- a/core/clients/key_flow.go +++ b/core/clients/key_flow.go @@ -53,6 +53,8 @@ type KeyFlowConfig struct { ClientRetry *RetryConfig TokenUrl string BackgroundTokenRefreshContext context.Context // Functionality is enabled if this isn't nil + HTTPTransport http.RoundTripper + AuthHTTPClient *http.Client } // TokenResponseBody is the API response @@ -116,7 +118,7 @@ func (c *KeyFlow) GetToken() TokenResponseBody { return *c.token } -func (c *KeyFlow) Init(cfg *KeyFlowConfig, rt http.RoundTripper) error { +func (c *KeyFlow) Init(cfg *KeyFlowConfig) error { // No concurrency at this point, so no mutex check needed c.token = &TokenResponseBody{} c.config = cfg @@ -125,14 +127,15 @@ func (c *KeyFlow) Init(cfg *KeyFlowConfig, rt http.RoundTripper) error { c.config.TokenUrl = tokenAPI } - if rt == nil { - rt = http.DefaultTransport + if c.rt = cfg.HTTPTransport; c.rt == nil { + c.rt = http.DefaultTransport } - c.rt = rt - c.authClient = &http.Client{ - Transport: rt, - Timeout: DefaultClientTimeout, + if c.authClient = cfg.AuthHTTPClient; cfg.AuthHTTPClient == nil { + c.authClient = &http.Client{ + Transport: c.rt, + Timeout: DefaultClientTimeout, + } } err := c.validate() diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index 15a4985d5..6f482c3ca 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -114,7 +114,7 @@ func TestKeyFlowInit(t *testing.T) { } cfg.ServiceAccountKey = tt.serviceAccountKey - if err := c.Init(cfg, http.DefaultTransport); (err != nil) != tt.wantErr { + if err := c.Init(cfg); (err != nil) != tt.wantErr { t.Errorf("KeyFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } if c.config == nil { diff --git a/core/clients/no_auth_flow.go b/core/clients/no_auth_flow.go index a0e06916f..b0b2406ce 100644 --- a/core/clients/no_auth_flow.go +++ b/core/clients/no_auth_flow.go @@ -13,7 +13,8 @@ type NoAuthFlow struct { // NoAuthFlowConfig holds the configuration for the unauthenticated flow type NoAuthFlowConfig struct { // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. - ClientRetry *RetryConfig + ClientRetry *RetryConfig + HTTPTransport http.RoundTripper } // GetConfig returns the flow configuration @@ -24,19 +25,17 @@ func (c *NoAuthFlow) GetConfig() NoAuthFlowConfig { return *c.config } -func (c *NoAuthFlow) Init(_ NoAuthFlowConfig, rt http.RoundTripper) error { +func (c *NoAuthFlow) Init(cfg NoAuthFlowConfig) error { c.config = &NoAuthFlowConfig{} - if rt == nil { - rt = http.DefaultTransport + if c.rt = cfg.HTTPTransport; c.rt == nil { + c.rt = http.DefaultTransport } - c.rt = rt - return nil } -// Roundtrip performs the request +// RoundTrip performs the request func (c *NoAuthFlow) RoundTrip(req *http.Request) (*http.Response, error) { if c.rt == nil { return nil, fmt.Errorf("please run Init()") diff --git a/core/clients/no_auth_flow_test.go b/core/clients/no_auth_flow_test.go index 4a8970495..82c854ea6 100644 --- a/core/clients/no_auth_flow_test.go +++ b/core/clients/no_auth_flow_test.go @@ -21,11 +21,12 @@ func TestNoAuthFlow_Init(t *testing.T) { wantErr bool }{ {"ok", args{context.Background(), NoAuthFlowConfig{}}, false}, + {"with transport", args{context.Background(), NoAuthFlowConfig{HTTPTransport: http.DefaultTransport}}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &NoAuthFlow{} - if err := c.Init(tt.args.cfg, http.DefaultTransport); (err != nil) != tt.wantErr { + if err := c.Init(tt.args.cfg); (err != nil) != tt.wantErr { t.Errorf("NoAuthFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/core/clients/token_flow.go b/core/clients/token_flow.go index 2d5a69497..ac1ff779a 100644 --- a/core/clients/token_flow.go +++ b/core/clients/token_flow.go @@ -23,7 +23,8 @@ type TokenFlowConfig struct { ServiceAccountEmail string ServiceAccountToken string // Deprecated: retry options were removed to reduce complexity of the client. If this functionality is needed, you can provide your own custom HTTP client. - ClientRetry *RetryConfig + ClientRetry *RetryConfig + HTTPTransport http.RoundTripper } // GetConfig returns the flow configuration @@ -34,15 +35,13 @@ func (c *TokenFlow) GetConfig() TokenFlowConfig { return *c.config } -func (c *TokenFlow) Init(cfg *TokenFlowConfig, rt http.RoundTripper) error { +func (c *TokenFlow) Init(cfg *TokenFlowConfig) error { c.config = cfg - if rt == nil { - rt = http.DefaultTransport + if c.rt = cfg.HTTPTransport; c.rt == nil { + c.rt = http.DefaultTransport } - c.rt = rt - return c.validate() } @@ -54,7 +53,7 @@ func (c *TokenFlow) validate() error { return nil } -// Roundtrip performs the request +// RoundTrip performs the request func (c *TokenFlow) RoundTrip(req *http.Request) (*http.Response, error) { if c.rt == nil { return nil, fmt.Errorf("please run Init()") diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index 1fffd2e6b..aac626501 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -22,6 +22,10 @@ func TestTokenFlow_Init(t *testing.T) { {"ok", args{&TokenFlowConfig{ ServiceAccountToken: "efg", }}, false}, + {"with transport", args{&TokenFlowConfig{ + ServiceAccountToken: "efg", + HTTPTransport: http.DefaultTransport, + }}, false}, {"error 1", args{&TokenFlowConfig{ ServiceAccountToken: "", }}, true}, @@ -35,7 +39,7 @@ func TestTokenFlow_Init(t *testing.T) { if err != nil { t.Fatalf("Setting service account token: %s", err) } - if err := c.Init(tt.args.cfg, http.DefaultTransport); (err != nil) != tt.wantErr { + if err := c.Init(tt.args.cfg); (err != nil) != tt.wantErr { t.Errorf("TokenFlow.Init() error = %v, wantErr %v", err, tt.wantErr) } err = os.Setenv(ServiceAccountToken, b) From f88f91ed17773ca92915bfd90225f8beefcdd415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 21 Mar 2025 17:44:55 +0100 Subject: [PATCH 3/7] Add CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- CHANGELOG.md | 3 ++- core/CHANGELOG.md | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b8a9bbf7..dc1a065fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ - **New:** API for application load balancer - `cdn`: [0.1.0](services/cdn/CHANGELOG.md#v010-2025-xx-yy) - **New:** Introduce new API for content delivery - +- `core`: [v0.16.2](core/CHANGELOG.md#v0162-2025-03-21) + - **New:** If a custom http.Client is provided, the http.Transport is respected. This allows customizing the http.Client with custom timeouts or instrumentation. ## Release (2025-03-14) - `certificates`: [v1.0.0](services/certificates/CHANGELOG.md#v100-2025-03-14) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 194b06fe6..cd48cbe6d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.16.2 (2025-03-21) +- **New:** If a custom http.Client is provided, the http.Transport is respected. This allows customizing the http.Client with custom timeouts or instrumentation. + ## v0.16.1 (2025-02-25) - **Bugfix:** STACKIT_PRIVATE_KEY and STACKIT_SERVICE_ACCOUNT_KEY can be set via environment variable or via credentials file. From 87ad063f15c8e11d3d01c15623716f6234ffea4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 21 Mar 2025 19:42:30 +0100 Subject: [PATCH 4/7] Add test cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- core/clients/key_flow.go | 12 +- core/clients/key_flow_continuous_refresh.go | 5 +- core/clients/key_flow_test.go | 185 ++++++++++++++++++++ core/clients/no_auth_flow_test.go | 151 ++++++++++------ core/clients/token_flow_test.go | 166 +++++++++++++----- core/go.mod | 7 + core/go.sum | 10 ++ 7 files changed, 433 insertions(+), 103 deletions(-) diff --git a/core/clients/key_flow.go b/core/clients/key_flow.go index eb9458414..448c2bd44 100644 --- a/core/clients/key_flow.go +++ b/core/clients/key_flow.go @@ -194,8 +194,12 @@ func (c *KeyFlow) GetAccessToken() (string, error) { return "", fmt.Errorf("nil http round tripper, please run Init()") } + var accessToken string + c.tokenMutex.RLock() - accessToken := c.token.AccessToken + if c.token != nil { + accessToken = c.token.AccessToken + } c.tokenMutex.RUnlock() accessTokenExpired, err := tokenExpired(accessToken) @@ -247,8 +251,12 @@ func (c *KeyFlow) validate() error { // recreateAccessToken is used to create a new access token // when the existing one isn't valid anymore func (c *KeyFlow) recreateAccessToken() error { + var refreshToken string + c.tokenMutex.RLock() - refreshToken := c.token.RefreshToken + if c.token != nil { + refreshToken = c.token.RefreshToken + } c.tokenMutex.RUnlock() refreshTokenExpired, err := tokenExpired(refreshToken) diff --git a/core/clients/key_flow_continuous_refresh.go b/core/clients/key_flow_continuous_refresh.go index dfafc10ea..f5129aa02 100644 --- a/core/clients/key_flow_continuous_refresh.go +++ b/core/clients/key_flow_continuous_refresh.go @@ -46,9 +46,12 @@ func (refresher *continuousTokenRefresher) continuousRefreshToken() error { // Compute timestamp where we'll refresh token // Access token may be empty at this point, we have to check it var startRefreshTimestamp time.Time + var accessToken string refresher.keyFlow.tokenMutex.RLock() - accessToken := refresher.keyFlow.token.AccessToken + if refresher.keyFlow.token != nil { + accessToken = refresher.keyFlow.token.AccessToken + } refresher.keyFlow.tokenMutex.RUnlock() if accessToken == "" { startRefreshTimestamp = time.Now() diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index 6f482c3ca..f96757c20 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -1,14 +1,18 @@ package clients import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/json" "encoding/pem" "errors" "fmt" "io" "net/http" + "net/http/httptest" + "net/url" "strings" "testing" "time" @@ -16,12 +20,16 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/google/go-cmp/cmp" "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( testSigningKey = []byte(`Test`) ) +const testBearerToken = "eyJhbGciOiJub25lIn0.eyJleHAiOjIxNDc0ODM2NDd9." + func fixtureServiceAccountKey(mods ...func(*ServiceAccountKeyResponse)) *ServiceAccountKeyResponse { validUntil := time.Now().Add(time.Hour) serviceAccountKeyResponse := &ServiceAccountKeyResponse{ @@ -306,6 +314,183 @@ func TestRequestToken(t *testing.T) { } } +func TestKeyFlow_Do(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + keyFlow *KeyFlow + handlerFn func(tb testing.TB) http.HandlerFunc + want int + wantErr bool + }{ + { + name: "success", + keyFlow: &KeyFlow{rt: http.DefaultTransport, config: &KeyFlowConfig{}}, + handlerFn: func(tb testing.TB) http.HandlerFunc { + tb.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(tb, "Bearer "+testBearerToken, r.Header.Get("Authorization")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: http.StatusOK, + wantErr: false, + }, + { + name: "success with code 500", + keyFlow: &KeyFlow{rt: http.DefaultTransport, config: &KeyFlowConfig{}}, + handlerFn: func(_ testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, `Internal Server Error`) + } + }, + want: http.StatusInternalServerError, + wantErr: false, + }, + { + name: "success with custom transport", + keyFlow: &KeyFlow{ + rt: mockTransportFn{ + fn: func(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", "custom_transport") + + return http.DefaultTransport.RoundTrip(req) + }, + }, + config: &KeyFlowConfig{}, + }, + handlerFn: func(tb testing.TB) http.HandlerFunc { + tb.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: http.StatusOK, + wantErr: false, + }, + { + name: "fail with custom proxy", + keyFlow: &KeyFlow{ + rt: &http.Transport{ + Proxy: func(_ *http.Request) (*url.URL, error) { + return nil, fmt.Errorf("proxy error") + }, + }, + config: &KeyFlowConfig{}, + }, + handlerFn: func(testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: 0, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + t.Cleanup(cancel) // This cancels the refresher goroutine + + privateKeyBytes, err := generatePrivateKey() + require.NoError(t, err) + + tt.keyFlow.config.ServiceAccountKey = fixtureServiceAccountKey() + tt.keyFlow.config.PrivateKey = string(privateKeyBytes) + tt.keyFlow.config.BackgroundTokenRefreshContext = ctx + tt.keyFlow.authClient = &http.Client{ + Transport: mockTransportFn{ + fn: func(req *http.Request) (*http.Response, error) { + res := httptest.NewRecorder() + res.WriteHeader(http.StatusOK) + res.Header().Set("Content-Type", "application/json") + + token := &TokenResponseBody{ + AccessToken: testBearerToken, + ExpiresIn: 2147483647, + RefreshToken: testBearerToken, + TokenType: "Bearer", + } + + assert.NoError(t, json.NewEncoder(res.Body).Encode(token)) + + return res.Result(), nil + }, + }, + } + + require.NoError(t, tt.keyFlow.validate()) + + go continuousRefreshToken(tt.keyFlow) + + tokenCtx, tokenCancel := context.WithTimeout(context.Background(), 1*time.Second) + + token: + for { + select { + case <-tokenCtx.Done(): + require.Fail(t, tokenCtx.Err().Error()) + case <-time.After(50 * time.Millisecond): + tt.keyFlow.tokenMutex.RLock() + if tt.keyFlow.token != nil { + tt.keyFlow.tokenMutex.RUnlock() + tokenCancel() + break token + } + + tt.keyFlow.tokenMutex.RUnlock() + } + } + + server := httptest.NewServer(tt.handlerFn(t)) + t.Cleanup(server.Close) + + u, err := url.Parse(server.URL) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) + require.NoError(t, err) + + httpClient := &http.Client{ + Transport: tt.keyFlow, + } + + res, err := httpClient.Do(req) + + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + + assert.Equal(t, tt.want, res.StatusCode) + + // Defer discard and close the body + t.Cleanup(func() { + _, err := io.Copy(io.Discard, res.Body) + require.NoError(t, err) + require.NoError(t, res.Body.Close()) + }) + } + }) + } +} + type mockTransportFn struct { fn func(req *http.Request) (*http.Response, error) } diff --git a/core/clients/no_auth_flow_test.go b/core/clients/no_auth_flow_test.go index 82c854ea6..4884635a4 100644 --- a/core/clients/no_auth_flow_test.go +++ b/core/clients/no_auth_flow_test.go @@ -8,6 +8,9 @@ import ( "net/http/httptest" "net/url" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNoAuthFlow_Init(t *testing.T) { @@ -34,76 +37,118 @@ func TestNoAuthFlow_Init(t *testing.T) { } func TestNoAuthFlow_Do(t *testing.T) { - type fields struct { - rt http.RoundTripper - } - type args struct{} + t.Parallel() + tests := []struct { - name string - fields fields - args args - want int - wantErr bool + name string + noAuthFlow *NoAuthFlow + handlerFn func(tb testing.TB) http.HandlerFunc + want int + wantErr bool }{ { - name: "fail", - fields: fields{ - nil, + name: "success with rt", + noAuthFlow: &NoAuthFlow{http.DefaultTransport, &NoAuthFlowConfig{}}, + handlerFn: func(_ testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } }, - args: args{}, - want: 0, - wantErr: true, + want: http.StatusOK, + wantErr: false, + }, + { + name: "success with code 500", + noAuthFlow: &NoAuthFlow{http.DefaultTransport, &NoAuthFlowConfig{}}, + handlerFn: func(_ testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, `Internal Server Error`) + } + }, + want: http.StatusInternalServerError, + wantErr: false, }, { - name: "success", - fields: fields{ - http.DefaultTransport, + name: "success with custom transport", + noAuthFlow: &NoAuthFlow{ + mockTransportFn{ + fn: func(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", "custom_transport") + + return http.DefaultTransport.RoundTrip(req) + }, + }, + &NoAuthFlowConfig{}, + }, + handlerFn: func(tb testing.TB) http.HandlerFunc { + tb.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } }, - args: args{}, want: http.StatusOK, wantErr: false, }, + { + name: "fail with custom proxy", + noAuthFlow: &NoAuthFlow{ + &http.Transport{ + Proxy: func(_ *http.Request) (*url.URL, error) { + return nil, fmt.Errorf("proxy error") + }, + }, + &NoAuthFlowConfig{}, + }, + handlerFn: func(testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: 0, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := &NoAuthFlow{ - rt: tt.fields.rt, - } - handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprintln(w, `{"status":"ok"}`) - }) - server := httptest.NewServer(handler) - defer server.Close() + server := httptest.NewServer(tt.handlerFn(t)) + t.Cleanup(server.Close) + u, err := url.Parse(server.URL) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - if err != nil { - t.Error(err) - return + require.NoError(t, err) + + httpClient := &http.Client{ + Transport: tt.noAuthFlow, } - got, err := c.RoundTrip(req) - if err == nil { + + res, err := httpClient.Do(req) + + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + + assert.Equal(t, tt.want, res.StatusCode) + // Defer discard and close the body - defer func() { - if _, discardErr := io.Copy(io.Discard, got.Body); discardErr != nil && err == nil { - err = discardErr - } - if closeErr := got.Body.Close(); closeErr != nil && err == nil { - err = closeErr - } - }() - } - if (err != nil) != tt.wantErr { - t.Errorf("NoAuthFlow.Do() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil && got.StatusCode != tt.want { - t.Errorf("NoAuthFlow.Do() = %v, want %v", got.StatusCode, tt.want) + t.Cleanup(func() { + _, err := io.Copy(io.Discard, res.Body) + require.NoError(t, err) + require.NoError(t, res.Body.Close()) + }) } }) } diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index aac626501..9643866eb 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -8,6 +8,9 @@ import ( "net/url" "os" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTokenFlow_Init(t *testing.T) { @@ -54,62 +57,131 @@ func TestTokenFlow_Init(t *testing.T) { } func TestTokenFlow_Do(t *testing.T) { - type fields struct { - rt http.RoundTripper - config *TokenFlowConfig - } - type args struct{} + t.Parallel() + tests := []struct { - name string - fields fields - args args - want int - wantErr bool + name string + tokenFlow *TokenFlow + handlerFn func(tb testing.TB) http.HandlerFunc + want int + wantErr bool }{ - {"fail", fields{nil, nil}, args{}, 0, true}, - {"success", fields{http.DefaultTransport, &TokenFlowConfig{}}, args{}, http.StatusOK, false}, + { + name: "success", + tokenFlow: &TokenFlow{http.DefaultTransport, &TokenFlowConfig{ + ServiceAccountToken: "efg", + }}, + handlerFn: func(tb testing.TB) http.HandlerFunc { + tb.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(tb, "Bearer efg", r.Header.Get("Authorization")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: http.StatusOK, + wantErr: false, + }, + { + name: "success with code 500", + tokenFlow: &TokenFlow{http.DefaultTransport, &TokenFlowConfig{ + ServiceAccountToken: "efg", + }}, + handlerFn: func(testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, `Internal Server Error`) + } + }, + want: http.StatusInternalServerError, + wantErr: false, + }, + { + name: "success with custom transport", + tokenFlow: &TokenFlow{ + mockTransportFn{ + fn: func(req *http.Request) (*http.Response, error) { + req.Header.Set("User-Agent", "custom_transport") + + return http.DefaultTransport.RoundTrip(req) + }, + }, + &TokenFlowConfig{ + ServiceAccountToken: "efg", + }, + }, + handlerFn: func(tb testing.TB) http.HandlerFunc { + tb.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(tb, "Bearer efg", r.Header.Get("Authorization")) + assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: http.StatusOK, + wantErr: false, + }, + { + name: "fail with custom proxy", + tokenFlow: &TokenFlow{ + &http.Transport{ + Proxy: func(_ *http.Request) (*url.URL, error) { + return nil, fmt.Errorf("proxy error") + }, + }, + &TokenFlowConfig{ + ServiceAccountToken: "efg", + }, + }, + handlerFn: func(testing.TB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, `{"status":"ok"}`) + } + }, + want: 0, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := &TokenFlow{ - rt: tt.fields.rt, - config: tt.fields.config, - } - handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprintln(w, `{"status":"ok"}`) - }) - server := httptest.NewServer(handler) - defer server.Close() + server := httptest.NewServer(tt.handlerFn(t)) + t.Cleanup(server.Close) + u, err := url.Parse(server.URL) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - if err != nil { - t.Error(err) - return + require.NoError(t, err) + + httpClient := &http.Client{ + Transport: tt.tokenFlow, } - got, err := c.RoundTrip(req) - if err == nil { + + res, err := httpClient.Do(req) + + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + + assert.Equal(t, tt.want, res.StatusCode) + // Defer discard and close the body - defer func() { - if _, discardErr := io.Copy(io.Discard, got.Body); discardErr != nil && err == nil { - err = discardErr - } - if closeErr := got.Body.Close(); closeErr != nil && err == nil { - err = closeErr - } - }() - } - if (err != nil) != tt.wantErr { - t.Errorf("TokenFlow.Do() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != nil && got.StatusCode != tt.want { - t.Errorf("TokenFlow.Do() = %v, want %v", got.StatusCode, tt.want) + t.Cleanup(func() { + _, err := io.Copy(io.Discard, res.Body) + require.NoError(t, err) + require.NoError(t, res.Body.Close()) + }) } }) } diff --git a/core/go.mod b/core/go.mod index 0d209142b..508a866d8 100644 --- a/core/go.mod +++ b/core/go.mod @@ -6,4 +6,11 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 + github.com/stretchr/testify v1.10.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/core/go.sum b/core/go.sum index d6abe9afa..4d619ef11 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,6 +1,16 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 94107bb13a8d896d46e59f39ea98bfddcf661ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 21 Mar 2025 19:59:57 +0100 Subject: [PATCH 5/7] avoid testify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- core/clients/key_flow_test.go | 56 +++++++++++++++++++++---------- core/clients/no_auth_flow_test.go | 43 +++++++++++++++--------- core/clients/token_flow_test.go | 46 +++++++++++++++++-------- core/go.mod | 7 ---- core/go.sum | 10 ------ 5 files changed, 99 insertions(+), 63 deletions(-) diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index f96757c20..f642dd966 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -20,8 +20,6 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/google/go-cmp/cmp" "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var ( @@ -331,7 +329,9 @@ func TestKeyFlow_Do(t *testing.T) { tb.Helper() return func(w http.ResponseWriter, r *http.Request) { - assert.Equal(tb, "Bearer "+testBearerToken, r.Header.Get("Authorization")) + if r.Header.Get("Authorization") != "Bearer "+testBearerToken { + tb.Errorf("expected Authorization header to be 'Bearer %s', but got %s", testBearerToken, r.Header.Get("Authorization")) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -370,7 +370,9 @@ func TestKeyFlow_Do(t *testing.T) { tb.Helper() return func(w http.ResponseWriter, r *http.Request) { - assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + if r.Header.Get("User-Agent") != "custom_transport" { + tb.Errorf("expected User-Agent header to be 'custom_transport', but got %s", r.Header.Get("User-Agent")) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -409,14 +411,16 @@ func TestKeyFlow_Do(t *testing.T) { t.Cleanup(cancel) // This cancels the refresher goroutine privateKeyBytes, err := generatePrivateKey() - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } tt.keyFlow.config.ServiceAccountKey = fixtureServiceAccountKey() tt.keyFlow.config.PrivateKey = string(privateKeyBytes) tt.keyFlow.config.BackgroundTokenRefreshContext = ctx tt.keyFlow.authClient = &http.Client{ Transport: mockTransportFn{ - fn: func(req *http.Request) (*http.Response, error) { + fn: func(_ *http.Request) (*http.Response, error) { res := httptest.NewRecorder() res.WriteHeader(http.StatusOK) res.Header().Set("Content-Type", "application/json") @@ -428,14 +432,18 @@ func TestKeyFlow_Do(t *testing.T) { TokenType: "Bearer", } - assert.NoError(t, json.NewEncoder(res.Body).Encode(token)) + if err := json.NewEncoder(res.Body).Encode(token); err != nil { + t.Logf("no error is expected, but got %v", err) + } return res.Result(), nil }, }, } - require.NoError(t, tt.keyFlow.validate()) + if err := tt.keyFlow.validate(); err != nil { + t.Errorf("no error is expected, but got %v", err) + } go continuousRefreshToken(tt.keyFlow) @@ -445,7 +453,7 @@ func TestKeyFlow_Do(t *testing.T) { for { select { case <-tokenCtx.Done(): - require.Fail(t, tokenCtx.Err().Error()) + t.Error(tokenCtx.Err()) case <-time.After(50 * time.Millisecond): tt.keyFlow.tokenMutex.RLock() if tt.keyFlow.token != nil { @@ -462,10 +470,14 @@ func TestKeyFlow_Do(t *testing.T) { t.Cleanup(server.Close) u, err := url.Parse(server.URL) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } httpClient := &http.Client{ Transport: tt.keyFlow, @@ -474,17 +486,27 @@ func TestKeyFlow_Do(t *testing.T) { res, err := httpClient.Do(req) if tt.wantErr { - require.Error(t, err) + if err == nil { + t.Errorf("error is expected, but got %v", err) + } } else { - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } - assert.Equal(t, tt.want, res.StatusCode) + if res.StatusCode != tt.want { + t.Errorf("expected status code %d, but got %d", tt.want, res.StatusCode) + } // Defer discard and close the body t.Cleanup(func() { - _, err := io.Copy(io.Discard, res.Body) - require.NoError(t, err) - require.NoError(t, res.Body.Close()) + if _, err := io.Copy(io.Discard, res.Body); err != nil { + t.Errorf("no error is expected, but got %v", err) + } + + if err := res.Body.Close(); err != nil { + t.Errorf("no error is expected, but got %v", err) + } }) } }) diff --git a/core/clients/no_auth_flow_test.go b/core/clients/no_auth_flow_test.go index 4884635a4..5d7abc293 100644 --- a/core/clients/no_auth_flow_test.go +++ b/core/clients/no_auth_flow_test.go @@ -8,9 +8,6 @@ import ( "net/http/httptest" "net/url" "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestNoAuthFlow_Init(t *testing.T) { @@ -50,7 +47,7 @@ func TestNoAuthFlow_Do(t *testing.T) { name: "success with rt", noAuthFlow: &NoAuthFlow{http.DefaultTransport, &NoAuthFlowConfig{}}, handlerFn: func(_ testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprintln(w, `{"status":"ok"}`) @@ -63,7 +60,7 @@ func TestNoAuthFlow_Do(t *testing.T) { name: "success with code 500", noAuthFlow: &NoAuthFlow{http.DefaultTransport, &NoAuthFlowConfig{}}, handlerFn: func(_ testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintln(w, `Internal Server Error`) @@ -88,7 +85,9 @@ func TestNoAuthFlow_Do(t *testing.T) { tb.Helper() return func(w http.ResponseWriter, r *http.Request) { - assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + if r.Header.Get("User-Agent") != "custom_transport" { + tb.Errorf("expected User-Agent header to be 'custom_transport', but got %s", r.Header.Get("User-Agent")) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -109,7 +108,7 @@ func TestNoAuthFlow_Do(t *testing.T) { &NoAuthFlowConfig{}, }, handlerFn: func(testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprintln(w, `{"status":"ok"}`) @@ -125,10 +124,14 @@ func TestNoAuthFlow_Do(t *testing.T) { t.Cleanup(server.Close) u, err := url.Parse(server.URL) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } httpClient := &http.Client{ Transport: tt.noAuthFlow, @@ -137,17 +140,27 @@ func TestNoAuthFlow_Do(t *testing.T) { res, err := httpClient.Do(req) if tt.wantErr { - require.Error(t, err) + if err == nil { + t.Errorf("error is expected, but got %v", err) + } } else { - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } - assert.Equal(t, tt.want, res.StatusCode) + if res.StatusCode != tt.want { + t.Errorf("expected status code %d, but got %d", tt.want, res.StatusCode) + } // Defer discard and close the body t.Cleanup(func() { - _, err := io.Copy(io.Discard, res.Body) - require.NoError(t, err) - require.NoError(t, res.Body.Close()) + if _, err := io.Copy(io.Discard, res.Body); err != nil { + t.Errorf("no error is expected, but got %v", err) + } + + if err := res.Body.Close(); err != nil { + t.Errorf("no error is expected, but got %v", err) + } }) } }) diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index 9643866eb..12a7b5ab0 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -8,9 +8,6 @@ import ( "net/url" "os" "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestTokenFlow_Init(t *testing.T) { @@ -75,7 +72,9 @@ func TestTokenFlow_Do(t *testing.T) { tb.Helper() return func(w http.ResponseWriter, r *http.Request) { - assert.Equal(tb, "Bearer efg", r.Header.Get("Authorization")) + if r.Header.Get("Authorization") != "Bearer efg" { + tb.Errorf("expected Authorization header to be 'Bearer efg', but got %s", r.Header.Get("Authorization")) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -118,8 +117,13 @@ func TestTokenFlow_Do(t *testing.T) { tb.Helper() return func(w http.ResponseWriter, r *http.Request) { - assert.Equal(tb, "Bearer efg", r.Header.Get("Authorization")) - assert.Equal(tb, "custom_transport", r.Header.Get("User-Agent")) + if r.Header.Get("Authorization") != "Bearer efg" { + tb.Errorf("expected Authorization header to be 'Bearer efg', but got %s", r.Header.Get("Authorization")) + } + + if r.Header.Get("User-Agent") != "custom_transport" { + tb.Errorf("expected User-Agent header to be 'custom_transport', but got %s", r.Header.Get("User-Agent")) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -158,10 +162,14 @@ func TestTokenFlow_Do(t *testing.T) { t.Cleanup(server.Close) u, err := url.Parse(server.URL) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } req, err := http.NewRequest(http.MethodGet, u.String(), http.NoBody) - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } httpClient := &http.Client{ Transport: tt.tokenFlow, @@ -170,17 +178,27 @@ func TestTokenFlow_Do(t *testing.T) { res, err := httpClient.Do(req) if tt.wantErr { - require.Error(t, err) + if err == nil { + t.Errorf("error is expected, but got %v", err) + } } else { - require.NoError(t, err) + if err != nil { + t.Errorf("no error is expected, but got %v", err) + } - assert.Equal(t, tt.want, res.StatusCode) + if res.StatusCode != tt.want { + t.Errorf("expected status code %d, but got %d", tt.want, res.StatusCode) + } // Defer discard and close the body t.Cleanup(func() { - _, err := io.Copy(io.Discard, res.Body) - require.NoError(t, err) - require.NoError(t, res.Body.Close()) + if _, err := io.Copy(io.Discard, res.Body); err != nil { + t.Errorf("no error is expected, but got %v", err) + } + + if err := res.Body.Close(); err != nil { + t.Errorf("no error is expected, but got %v", err) + } }) } }) diff --git a/core/go.mod b/core/go.mod index 508a866d8..0d209142b 100644 --- a/core/go.mod +++ b/core/go.mod @@ -6,11 +6,4 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/stretchr/testify v1.10.0 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/core/go.sum b/core/go.sum index 4d619ef11..d6abe9afa 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,16 +1,6 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 6f75b4431364faf4a7293540a1ab63b915a92fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 24 Mar 2025 19:01:09 +0100 Subject: [PATCH 6/7] fix lint issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- core/clients/key_flow_test.go | 5 ++--- core/clients/token_flow_test.go | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index f642dd966..41606d1f0 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -26,7 +26,7 @@ var ( testSigningKey = []byte(`Test`) ) -const testBearerToken = "eyJhbGciOiJub25lIn0.eyJleHAiOjIxNDc0ODM2NDd9." +const testBearerToken = "eyJhbGciOiJub25lIn0.eyJleHAiOjIxNDc0ODM2NDd9." //nolint:gosec // linter false positive func fixtureServiceAccountKey(mods ...func(*ServiceAccountKeyResponse)) *ServiceAccountKeyResponse { validUntil := time.Now().Add(time.Hour) @@ -345,7 +345,7 @@ func TestKeyFlow_Do(t *testing.T) { name: "success with code 500", keyFlow: &KeyFlow{rt: http.DefaultTransport, config: &KeyFlowConfig{}}, handlerFn: func(_ testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintln(w, `Internal Server Error`) @@ -405,7 +405,6 @@ func TestKeyFlow_Do(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() ctx, cancel := context.WithCancel(ctx) t.Cleanup(cancel) // This cancels the refresher goroutine diff --git a/core/clients/token_flow_test.go b/core/clients/token_flow_test.go index 12a7b5ab0..9e389c91f 100644 --- a/core/clients/token_flow_test.go +++ b/core/clients/token_flow_test.go @@ -90,7 +90,7 @@ func TestTokenFlow_Do(t *testing.T) { ServiceAccountToken: "efg", }}, handlerFn: func(testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintln(w, `Internal Server Error`) @@ -146,7 +146,7 @@ func TestTokenFlow_Do(t *testing.T) { }, }, handlerFn: func(testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprintln(w, `{"status":"ok"}`) From 893d7c2163fac52d4a28e0f5990fbfe6d2421caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 25 Mar 2025 00:00:59 +0100 Subject: [PATCH 7/7] fix lint issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- core/clients/key_flow_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/clients/key_flow_test.go b/core/clients/key_flow_test.go index 41606d1f0..b37b9593f 100644 --- a/core/clients/key_flow_test.go +++ b/core/clients/key_flow_test.go @@ -393,7 +393,7 @@ func TestKeyFlow_Do(t *testing.T) { config: &KeyFlowConfig{}, }, handlerFn: func(testing.TB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprintln(w, `{"status":"ok"}`)