Fail fast with aggregated config errors after resolving secret files, before services initialize. Add Valkey session config, remove the unused session-secret, and fix the production CSP env key. Retry initial Temporal dials with bounded backoff and gate the test Temporal service on healthy DB and Keycloak dependencies.
97 lines
2.9 KiB
Go
97 lines
2.9 KiB
Go
package workflows
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.temporal.io/sdk/client"
|
|
)
|
|
|
|
// TestDialWithRetry_SucceedsAfterTransientFailures models the cold-start JWKS
|
|
// race: the first attempts fail with an "unauthorized" error, then the server's
|
|
// key cache refreshes and the dial succeeds.
|
|
func TestDialWithRetry_SucceedsAfterTransientFailures(t *testing.T) {
|
|
calls := 0
|
|
dial := func() (client.Client, error) {
|
|
calls++
|
|
if calls < 3 {
|
|
return nil, errors.New("failed reaching server: Request unauthorized.")
|
|
}
|
|
return nil, nil
|
|
}
|
|
c, err := dialWithRetry(context.Background(), dial, time.Second, time.Millisecond, 5*time.Millisecond, nil)
|
|
if err != nil {
|
|
t.Fatalf("expected success after retries, got: %v", err)
|
|
}
|
|
if c != nil {
|
|
t.Fatalf("expected the stub's nil client, got non-nil")
|
|
}
|
|
if calls != 3 {
|
|
t.Fatalf("expected 3 dial attempts, got %d", calls)
|
|
}
|
|
}
|
|
|
|
// TestDialWithRetry_ExhaustsBudget asserts a persistently failing dial returns the
|
|
// last error (wrapped) once the budget elapses.
|
|
func TestDialWithRetry_ExhaustsBudget(t *testing.T) {
|
|
calls := 0
|
|
wantErr := errors.New("failed reaching server: Request unauthorized.")
|
|
dial := func() (client.Client, error) {
|
|
calls++
|
|
return nil, wantErr
|
|
}
|
|
_, err := dialWithRetry(context.Background(), dial, 20*time.Millisecond, time.Millisecond, 5*time.Millisecond, nil)
|
|
if err == nil {
|
|
t.Fatal("expected an error after exhausting the budget")
|
|
}
|
|
if !errors.Is(err, wantErr) {
|
|
t.Fatalf("expected the wrapped last error, got: %v", err)
|
|
}
|
|
if calls < 2 {
|
|
t.Fatalf("expected multiple attempts within the budget, got %d", calls)
|
|
}
|
|
}
|
|
|
|
// TestDialWithRetry_HealthySucceedsFirstTry asserts a healthy server connects on
|
|
// the first attempt with no backoff delay.
|
|
func TestDialWithRetry_HealthySucceedsFirstTry(t *testing.T) {
|
|
calls := 0
|
|
dial := func() (client.Client, error) {
|
|
calls++
|
|
return nil, nil
|
|
}
|
|
start := time.Now()
|
|
if _, err := dialWithRetry(context.Background(), dial, time.Second, 50*time.Millisecond, time.Second, nil); err != nil {
|
|
t.Fatalf("expected immediate success, got: %v", err)
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("expected exactly 1 attempt, got %d", calls)
|
|
}
|
|
if elapsed := time.Since(start); elapsed > 20*time.Millisecond {
|
|
t.Fatalf("expected no backoff delay on first-try success, took %s", elapsed)
|
|
}
|
|
}
|
|
|
|
// TestDialWithRetry_RespectsContextCancellation asserts a canceled context aborts
|
|
// the retry loop promptly.
|
|
func TestDialWithRetry_RespectsContextCancellation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
calls := 0
|
|
dial := func() (client.Client, error) {
|
|
calls++
|
|
if calls == 1 {
|
|
cancel() // cancel before the first backoff sleep
|
|
}
|
|
return nil, errors.New("still starting")
|
|
}
|
|
_, err := dialWithRetry(ctx, dial, 5*time.Second, 50*time.Millisecond, time.Second, nil)
|
|
if err == nil {
|
|
t.Fatal("expected a cancellation error")
|
|
}
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("expected context.Canceled, got: %v", err)
|
|
}
|
|
}
|