Files
go-caatsm/internal/infra/nats/error_handler_test.go
T

103 lines
3.3 KiB
Go

package nats
import (
"context"
"errors"
"time"
"github.com/nats-io/nats.go"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
)
var _ = Describe("ErrorHandler", func() {
var (
handler *ErrorHandler
logger *zap.Logger
)
BeforeEach(func() {
logger = zaptest.NewLogger(GinkgoT())
handler = NewErrorHandler(logger)
})
Describe("HandleProcessingError", func() {
It("identifies permanent errors correctly", func() {
// Mock a permanent error (this would be defined in the app package)
permanentErr := errors.New("permanent error")
// For testing, we'll assume any error is transient unless specified
result := handler.HandleProcessingError(0, permanentErr, logger, "test.subject")
Expect(result.IsPermanent).To(BeFalse()) // Since we can't easily mock app.IsPermanent
Expect(result.ShouldApplyBackpressure).To(BeFalse())
})
It("applies backpressure for consecutive errors", func() {
transientErr := errors.New("transient error")
result := handler.HandleProcessingError(10, transientErr, logger, "test.subject")
Expect(result.IsPermanent).To(BeFalse())
Expect(result.ShouldApplyBackpressure).To(BeTrue())
Expect(result.BackpressureDelay).To(BeNumerically(">=", 100*time.Millisecond))
})
})
Describe("HandleFetchError", func() {
var (
ctx context.Context
sub *nats.Subscription
fetchErrorStreak int
streamName string
consumerName string
)
BeforeEach(func() {
ctx = context.Background()
sub = nil
fetchErrorStreak = 0
streamName = "TEST_STREAM"
consumerName = "test-consumer"
})
It("handles timeout errors", func() {
result := handler.HandleFetchError(ctx, nats.ErrTimeout, &sub, &fetchErrorStreak, streamName, consumerName, nil)
Expect(result.ShouldContinue).To(BeTrue())
Expect(result.Error).NotTo(HaveOccurred())
})
It("handles no responders with backoff", func() {
result := handler.HandleFetchError(ctx, nats.ErrNoResponders, &sub, &fetchErrorStreak, streamName, consumerName, nil)
Expect(result.ShouldContinue).To(BeTrue())
Expect(result.Error).NotTo(HaveOccurred())
Expect(fetchErrorStreak).To(Equal(1))
})
It("handles resource not found errors in dev environment", func() {
// Mock resource not found error
resourceErr := errors.New("stream not found")
// Provide a no-op recovery function to avoid panic
recoveryFunc := func() (*nats.Subscription, error) {
return nil, errors.New("recovery not implemented in test")
}
result := handler.HandleFetchError(ctx, resourceErr, &sub, &fetchErrorStreak, streamName, consumerName, recoveryFunc)
// In test environment, this should attempt recovery but fail since recovery func returns error
Expect(result.ShouldContinue).To(BeFalse())
Expect(result.Error).To(HaveOccurred())
})
It("handles successful recovery", func() {
resourceErr := errors.New("consumer not found")
mockSub := &nats.Subscription{}
recoveryFunc := func() (*nats.Subscription, error) {
return mockSub, nil
}
result := handler.HandleFetchError(ctx, resourceErr, &sub, &fetchErrorStreak, streamName, consumerName, recoveryFunc)
Expect(result.ShouldContinue).To(BeTrue())
Expect(result.RecoveredSub).To(Equal(mockSub))
Expect(fetchErrorStreak).To(Equal(0)) // Should reset on successful recovery
})
})
})