package outbox import ( "context" "encoding/json" "errors" "fmt" "net" "strings" "testing" "time" "github.com/jackc/pgx/v5/pgconn" ) func TestWorkerDefaultsAndRetryCap(t *testing.T) { worker := NewWorker(nil, discardLogger(), WorkerOptions{}) if worker.options.PollInterval != 2*time.Second || worker.options.BatchSize != 20 || worker.options.MaxAttempts != 8 { t.Fatalf("unexpected worker defaults: %#v", worker.options) } worker.options.BaseRetry = time.Second worker.options.MaxRetry = 3 * time.Second delay := time.Until(worker.nextAttempt(10)) if delay < 1400*time.Millisecond || delay > 3100*time.Millisecond { t.Fatalf("capped retry delay = %s", delay) } if !strings.HasPrefix(worker.workerID, "worker-") { t.Fatalf("unexpected worker id: %s", worker.workerID) } } func TestStoreRejectsInvalidEventBeforeDatabaseAccess(t *testing.T) { store := NewStore(nil) if _, err := store.Enqueue(context.Background(), nil, NewEvent{}); err == nil { t.Fatal("empty event should be rejected") } if _, err := store.Enqueue(context.Background(), nil, NewEvent{ AggregateType: "test", AggregateID: "id", EventType: "type", Payload: json.RawMessage(`not-json`), }); err == nil { t.Fatal("invalid JSON payload should be rejected") } } func TestHashPayloadIsStable(t *testing.T) { first := hashPayload([]byte(`{"ok":true}`)) if first != hashPayload([]byte(`{"ok":true}`)) || first == hashPayload([]byte(`{"ok":false}`)) { t.Fatal("payload hash is not stable") } } func TestRetryableErrorAllowList(t *testing.T) { permanent := errors.New("malformed payload") if IsRetryable(permanent) { t.Fatal("ordinary errors must be permanent by default") } if !IsRetryable(Retryable(permanent)) || !errors.Is(Retryable(permanent), permanent) { t.Fatal("explicit retryable marker was not preserved") } if !IsRetryable(context.DeadlineExceeded) || !IsRetryable(&net.DNSError{IsTimeout: true}) { t.Fatal("timeouts must remain retryable") } if !IsRetryable(&pgconn.PgError{Code: "40001"}) || IsRetryable(&pgconn.PgError{Code: "23505"}) { t.Fatal("PostgreSQL transient and constraint errors were misclassified") } if Retryable(nil) != nil { t.Fatal("wrapping nil must return nil") } if !IsRetryable(fmt.Errorf("dependency: %w", Retryable(permanent))) { t.Fatal("wrapped marker must remain discoverable") } }