72 lines
2.3 KiB
Go
72 lines
2.3 KiB
Go
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")
|
|
}
|
|
}
|