From c81ecb40ece5ff82c7f05c072cd7451f7fb542aa Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 8 Oct 2025 18:20:44 +0200 Subject: [PATCH 1/4] Remove external service dependency in hcaptcha TestCaptcha The test calls out to a web service which may not be reachable or down as seen in the linked issue. It's better for tests to not have such external dependencies. Fixes: https://github.com/go-gitea/gitea/issues/35571 --- modules/hcaptcha/hcaptcha.go | 4 ++-- modules/hcaptcha/hcaptcha_test.go | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/hcaptcha/hcaptcha.go b/modules/hcaptcha/hcaptcha.go index b970d491c578f..f4386f78a2e47 100644 --- a/modules/hcaptcha/hcaptcha.go +++ b/modules/hcaptcha/hcaptcha.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" ) -const verifyURL = "https://hcaptcha.com/siteverify" +const VerifyURL = "https://hcaptcha.com/siteverify" // Client is an hCaptcha client type Client struct { @@ -93,7 +93,7 @@ func (c *Client) Verify(token string, opts PostOptions) (*Response, error) { } // Basically a copy of http.PostForm, but with a context - req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, verifyURL, strings.NewReader(post.Encode())) + req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, VerifyURL, strings.NewReader(post.Encode())) if err != nil { return nil, err } diff --git a/modules/hcaptcha/hcaptcha_test.go b/modules/hcaptcha/hcaptcha_test.go index 55e01ec5355ba..d4f9b6dda3dc0 100644 --- a/modules/hcaptcha/hcaptcha_test.go +++ b/modules/hcaptcha/hcaptcha_test.go @@ -4,7 +4,10 @@ package hcaptcha import ( + "errors" + "io" "net/http" + "net/url" "os" "strings" "testing" @@ -21,6 +24,27 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +type mockTransport struct{} + +func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if req.URL.String() != VerifyURL { + return nil, errors.New("unsupported host") + } + + body, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + + values, err := url.ParseQuery(string(body)) + token := values.Get("response") + if token == dummyToken { + return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(`{"success":true,"credit":false,"hostname":"dummy-key-pass","challenge_ts":"2025-10-08T16:02:56.136Z"}`))}, nil + } else { + return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(`{"success":false,"error-codes":["invalid-input-response"]}`))}, nil + } +} + func TestCaptcha(t *testing.T) { tt := []struct { Name string @@ -54,7 +78,8 @@ func TestCaptcha(t *testing.T) { for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { client, err := New(tc.Secret, WithHTTP(&http.Client{ - Timeout: time.Second * 5, + Timeout: time.Second * 5, + Transport: mockTransport{}, })) if err != nil { // The only error that can be returned from creating a client From 64d78de85c56f9afc53b69fd4f816f8fdfd59ddb Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 8 Oct 2025 18:25:53 +0200 Subject: [PATCH 2/4] refactor --- modules/hcaptcha/hcaptcha_test.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/hcaptcha/hcaptcha_test.go b/modules/hcaptcha/hcaptcha_test.go index d4f9b6dda3dc0..7aba57dfb7038 100644 --- a/modules/hcaptcha/hcaptcha_test.go +++ b/modules/hcaptcha/hcaptcha_test.go @@ -36,13 +36,19 @@ func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { return nil, err } - values, err := url.ParseQuery(string(body)) - token := values.Get("response") - if token == dummyToken { - return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(`{"success":true,"credit":false,"hostname":"dummy-key-pass","challenge_ts":"2025-10-08T16:02:56.136Z"}`))}, nil + bodyValues, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + + var responseText string + if bodyValues.Get("response") == dummyToken { + responseText = `{"success":true,"credit":false,"hostname":"dummy-key-pass","challenge_ts":"2025-10-08T16:02:56.136Z"}` } else { - return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(`{"success":false,"error-codes":["invalid-input-response"]}`))}, nil + responseText = `{"success":false,"error-codes":["invalid-input-response"]}` } + + return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(responseText))}, nil } func TestCaptcha(t *testing.T) { From c5ec5c3e7d692eea037e2a51a044570df2b0db6c Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 8 Oct 2025 18:26:48 +0200 Subject: [PATCH 3/4] fix error message --- modules/hcaptcha/hcaptcha_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hcaptcha/hcaptcha_test.go b/modules/hcaptcha/hcaptcha_test.go index 7aba57dfb7038..5d5ed3061b5a6 100644 --- a/modules/hcaptcha/hcaptcha_test.go +++ b/modules/hcaptcha/hcaptcha_test.go @@ -28,7 +28,7 @@ type mockTransport struct{} func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { if req.URL.String() != VerifyURL { - return nil, errors.New("unsupported host") + return nil, errors.New("unsupported url") } body, err := io.ReadAll(req.Body) From d554c81616feee58d79a9d66dae425c17a80e599 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 8 Oct 2025 18:27:53 +0200 Subject: [PATCH 4/4] unexport --- modules/hcaptcha/hcaptcha.go | 4 ++-- modules/hcaptcha/hcaptcha_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/hcaptcha/hcaptcha.go b/modules/hcaptcha/hcaptcha.go index f4386f78a2e47..b970d491c578f 100644 --- a/modules/hcaptcha/hcaptcha.go +++ b/modules/hcaptcha/hcaptcha.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" ) -const VerifyURL = "https://hcaptcha.com/siteverify" +const verifyURL = "https://hcaptcha.com/siteverify" // Client is an hCaptcha client type Client struct { @@ -93,7 +93,7 @@ func (c *Client) Verify(token string, opts PostOptions) (*Response, error) { } // Basically a copy of http.PostForm, but with a context - req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, VerifyURL, strings.NewReader(post.Encode())) + req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, verifyURL, strings.NewReader(post.Encode())) if err != nil { return nil, err } diff --git a/modules/hcaptcha/hcaptcha_test.go b/modules/hcaptcha/hcaptcha_test.go index 5d5ed3061b5a6..5906faf17ce0b 100644 --- a/modules/hcaptcha/hcaptcha_test.go +++ b/modules/hcaptcha/hcaptcha_test.go @@ -27,7 +27,7 @@ func TestMain(m *testing.M) { type mockTransport struct{} func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) { - if req.URL.String() != VerifyURL { + if req.URL.String() != verifyURL { return nil, errors.New("unsupported url") }