fix(gitea): address MINOR review findings on retry logic
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 39s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 50s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m28s

1. Fix non-deterministic test TestDoGet_RetriesOnTemporaryNetError:
   - Replace timing-dependent listener approach with mockTransport
   - mockTransport allows controlled injection of net.OpError failures
   - Test now makes deterministic assertions: exactly 3 attempts (2 fail + 1 success)
   - Added SetHTTPClient() method for test transport injection

2. Sanitize error content in retry warning logs:
   - Added sanitizeErrorForLog() helper that omits response body content
   - For APIError: logs only 'HTTP <status>' instead of full body
   - For other errors: preserves error type information
   - Addresses security concern about logging server error content at WARN level
   - Full error with body still returned to caller for proper error handling

Both changes have corresponding test coverage.
This commit is contained in:
Rodin
2026-05-11 05:15:07 -07:00
parent ecfbfddc7c
commit 5498dccd60
2 changed files with 111 additions and 37 deletions
+21 -1
View File
@@ -72,6 +72,12 @@ func NewClient(baseURL, token string) *Client {
}
}
// SetHTTPClient sets the underlying HTTP client used for requests.
// This is intended for testing to inject mock transports.
func (c *Client) SetHTTPClient(hc *http.Client) {
c.http = hc
}
// PullRequest holds relevant PR metadata.
type PullRequest struct {
Title string `json:"title"`
@@ -302,6 +308,20 @@ func redactURL(rawURL string) string {
return parsed.String()
}
// sanitizeErrorForLog returns a loggable version of an error that omits
// potentially sensitive content like response bodies. For APIError, only
// the status code is included; for other errors, the type is preserved.
func sanitizeErrorForLog(err error) string {
if err == nil {
return "<nil>"
}
var apiErr *APIError
if errors.As(err, &apiErr) {
return fmt.Sprintf("HTTP %d", apiErr.StatusCode)
}
return err.Error()
}
// doGet performs an HTTP GET request with retry on 5xx errors and temporary
// network errors. Retries up to 3 times with exponential backoff (1s, 2s delays
// by default; configurable via Client.RetryBackoff for testing).
@@ -334,7 +354,7 @@ func (c *Client) doGet(ctx context.Context, reqURL string) ([]byte, error) {
"attempt", attempt+1,
"url", redactURL(reqURL),
"delay", delay.String(),
"lastError", lastErr)
"lastError", sanitizeErrorForLog(lastErr))
timer := time.NewTimer(delay)
select {