Files
cgalo5758 7bff4ac603 Validate startup config and retry Temporal
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.
2026-07-01 19:04:26 -05:00

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)
}
}