ultisuite-backend/internal/migration/http_retry_test.go
R3D347HR4Y 7143a36c19
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(mail): integrate Stalwart hosted mail and migration features
- Added configuration options for Stalwart hosted mail in .env.example.
- Updated Docker Compose to include Stalwart service with health checks.
- Introduced new API endpoints for managing mail domains and migration projects.
- Enhanced Authentik blueprints for user enrollment and post-migration security.
- Updated OAuth handling for Google and Microsoft migration processes.
- Improved error handling and response structures in the mail API.
- Added integration tests for email claiming and migration workflows.
2026-06-13 12:47:08 +02:00

104 lines
2.6 KiB
Go

package migration
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestParseRetryAfter(t *testing.T) {
if got := parseRetryAfter("30"); got != 30*time.Second {
t.Fatalf("seconds = %v", got)
}
if got := parseRetryAfter(""); got != 0 {
t.Fatalf("empty = %v", got)
}
future := time.Now().Add(45 * time.Second).UTC().Format(http.TimeFormat)
if got := parseRetryAfter(future); got < 40*time.Second || got > 50*time.Second {
t.Fatalf("http date = %v", got)
}
}
func TestRateLimitDelayUsesRetryAfter(t *testing.T) {
ConfigureRateLimit(RateLimitConfig{
MaxRetries: 3,
BaseDelay: 100 * time.Millisecond,
MaxDelay: time.Second,
})
delay := rateLimitDelay(1, 500*time.Millisecond)
if delay != 500*time.Millisecond {
t.Fatalf("delay = %v", delay)
}
delay = rateLimitDelay(3, 0)
if delay != 400*time.Millisecond {
t.Fatalf("exponential delay = %v", delay)
}
}
func TestAPIGETRetries429ThenSucceeds(t *testing.T) {
ConfigureRateLimit(RateLimitConfig{
MaxRetries: 5,
BaseDelay: 5 * time.Millisecond,
MaxDelay: 50 * time.Millisecond,
})
calls := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if calls < 3 {
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusTooManyRequests)
_, _ = w.Write([]byte(`{"error":"rateLimitExceeded"}`))
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}))
t.Cleanup(srv.Close)
body, err := apiGet(context.Background(), srv.Client(), srv.URL, "token")
if err != nil {
t.Fatalf("apiGet: %v", err)
}
if string(body) != `{"ok":true}` {
t.Fatalf("body = %q", body)
}
if calls != 3 {
t.Fatalf("calls = %d", calls)
}
}
func TestAPIGETReturnsRateLimitErrorAfterMaxRetries(t *testing.T) {
ConfigureRateLimit(RateLimitConfig{
MaxRetries: 2,
BaseDelay: time.Millisecond,
MaxDelay: 10 * time.Millisecond,
})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusTooManyRequests)
_, _ = w.Write([]byte(`{"error":"quota"}`))
}))
t.Cleanup(srv.Close)
_, err := apiGet(context.Background(), srv.Client(), srv.URL, "token")
if !IsRateLimitError(err) {
t.Fatalf("expected RateLimitError, got %v", err)
}
}
func TestWorkerRateLimitErrorIsPending(t *testing.T) {
if !IsRateLimitError(&RateLimitError{Cause: errTestRateLimit}) {
t.Fatal("expected typed rate limit error")
}
}
var errTestRateLimit = &RateLimitError{Cause: errTestRateLimitCause{}}
type errTestRateLimitCause struct{}
func (errTestRateLimitCause) Error() string { return "api rate limited (429): quota" }