fix(retry): retry transient edge answers centrally, fix stale task test (#57)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 21s
Tests / Test (Go stable) (pull_request) Successful in 21s

- Route HTTP helpers (getJSON/formRequest/deleteReq/postJSON/putJSON/
  multipart upload), Query and AuthenticateContext/ensureToken through the
  deterministic 429/502/503/504 retry (retryRaw), so the integration suite
  no longer fails on the shared openresty rate limit under parallel runs.
- Fix stale cmd/office/fetch integration test: loader.TaskFields was
  replaced by loader.DetailForm (broke go vet -tags=integration).
- Add unit tests for Transient/DoRetry.
This commit is contained in:
2026-09-16 21:32:14 +00:00
parent 8a17a32269
commit d6b8eb7777
7 changed files with 169 additions and 18 deletions
+88
View File
@@ -0,0 +1,88 @@
package onlyoffice
import (
"context"
"errors"
"testing"
"time"
)
func TestTransient(t *testing.T) {
cases := []struct {
err error
want bool
}{
{nil, false},
{errors.New("boom"), false},
{errors.New("GET /api/2.0/files/1.json: 429 Too Many Requests"), true},
{errors.New("auth: 502 bad gateway"), true},
{errors.New("POST /x: 503 service unavailable"), true},
{errors.New("PUT /x: 504 gateway timeout"), true},
{errors.New("GET /x: 404 not found"), false},
{errors.New("GET /x: 4290 not a code"), false},
}
for _, c := range cases {
if got := Transient(c.err); got != c.want {
t.Errorf("Transient(%v) = %v, want %v", c.err, got, c.want)
}
}
}
func TestDoRetryRetriesTransientThenSucceeds(t *testing.T) {
p := RetryPolicy{Attempts: 5, Base: time.Millisecond, Max: 5 * time.Millisecond}
attempts := 0
err := DoRetry(context.Background(), p, func() error {
attempts++
if attempts < 3 {
return errors.New("GET /x: 429 Too Many Requests")
}
return nil
})
if err != nil {
t.Fatalf("DoRetry: %v", err)
}
if attempts != 3 {
t.Fatalf("attempts = %d, want 3", attempts)
}
}
func TestDoRetryStopsOnPermanentError(t *testing.T) {
p := RetryPolicy{Attempts: 5, Base: time.Millisecond, Max: time.Millisecond}
attempts := 0
err := DoRetry(context.Background(), p, func() error {
attempts++
return errors.New("GET /x: 404 not found")
})
if err == nil {
t.Fatal("want error")
}
if attempts != 1 {
t.Fatalf("attempts = %d, want 1", attempts)
}
}
func TestDoRetryExhaustsAttemptsOnPersistentTransient(t *testing.T) {
p := RetryPolicy{Attempts: 3, Base: time.Millisecond, Max: time.Millisecond}
attempts := 0
err := DoRetry(context.Background(), p, func() error {
attempts++
return errors.New("GET /x: 429 Too Many Requests")
})
if !Transient(err) {
t.Fatalf("want transient error, got %v", err)
}
if attempts != 3 {
t.Fatalf("attempts = %d, want 3", attempts)
}
}
func TestDoRetryHonoursContextCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
err := DoRetry(ctx, RetryPolicy{Attempts: 3, Base: time.Millisecond, Max: time.Millisecond}, func() error {
return errors.New("GET /x: 429 Too Many Requests")
})
if !errors.Is(err, context.Canceled) {
t.Fatalf("want context.Canceled, got %v", err)
}
}