shibuya-pgmq-adapter-0.11.0.0: test/Shibuya/Adapter/Pgmq/ConfigSpec.hs
module Shibuya.Adapter.Pgmq.ConfigSpec (spec) where
import Pgmq.Types (parseQueueName, parseRoutingKey)
import Shibuya.Adapter.Pgmq.Config
import Test.Hspec
spec :: Spec
spec = do
defaultConfigSpec
defaultPollingConfigSpec
defaultPollRetryConfigSpec
validateConfigSpec
deadLetterTargetSpec
smartConstructorSpec
-- | Tests for defaultConfig
defaultConfigSpec :: Spec
defaultConfigSpec = describe "defaultConfig" $ do
let queueName = case parseQueueName "test_queue" of
Right q -> q
Left e -> error $ "Unexpected: " <> show e
config = defaultConfig queueName
it "sets queueName from parameter" $ do
config.queueName `shouldBe` queueName
it "sets visibilityTimeout to 30" $ do
config.visibilityTimeout `shouldBe` 30
it "sets batchSize to 1" $ do
config.batchSize `shouldBe` 1
it "uses StandardPolling with 1 second interval" $ do
case config.polling of
StandardPolling interval -> interval `shouldBe` 1
_ -> expectationFailure "Expected StandardPolling"
it "uses the default poll retry policy" $ do
config.pollRetry `shouldBe` defaultPollRetryConfig
it "uses the default ack retry policy" $ do
config.ackRetry `shouldBe` defaultPollRetryConfig
it "sets deadLetterConfig to Nothing" $ do
config.deadLetterConfig `shouldBe` Nothing
it "sets haltVisibilityTimeout to Nothing" $ do
config.haltVisibilityTimeout `shouldBe` Nothing
it "sets maxRetries to 3" $ do
config.maxRetries `shouldBe` 3
it "sets fifoConfig to Nothing" $ do
config.fifoConfig `shouldBe` Nothing
-- | Tests for defaultPollingConfig
defaultPollingConfigSpec :: Spec
defaultPollingConfigSpec = describe "defaultPollingConfig" $ do
it "is StandardPolling" $ do
case defaultPollingConfig of
StandardPolling _ -> pure ()
_ -> expectationFailure "Expected StandardPolling"
it "has 1 second interval" $ do
case defaultPollingConfig of
StandardPolling interval -> interval `shouldBe` 1
_ -> expectationFailure "Expected StandardPolling"
defaultPollRetryConfigSpec :: Spec
defaultPollRetryConfigSpec = describe "defaultPollRetryConfig" $ do
let PollRetryConfig
{ maxAttempts = retryMaxAttempts,
initialBackoff = retryInitialBackoff,
maxBackoff = retryMaxBackoff
} = defaultPollRetryConfig
it "retries five total attempts by default" $ do
retryMaxAttempts `shouldBe` 5
it "starts with a 100ms backoff" $ do
retryInitialBackoff `shouldBe` 0.1
it "caps backoff at five seconds" $ do
retryMaxBackoff `shouldBe` 5
validateConfigSpec :: Spec
validateConfigSpec = describe "validateConfig" $ do
let queueName = case parseQueueName "validate_queue" of
Right q -> q
Left e -> error $ "Unexpected: " <> show e
base = defaultConfig queueName
it "accepts the default config" $ do
validateConfig base `shouldBe` Right base
it "rejects batchSize below one" $ do
validateConfig base {batchSize = 0} `shouldBe` Left (InvalidBatchSize 0)
it "rejects visibilityTimeout below one" $ do
validateConfig base {visibilityTimeout = 0} `shouldBe` Left (InvalidVisibilityTimeout 0)
it "rejects non-positive standard polling interval" $ do
validateConfig base {polling = StandardPolling 0} `shouldBe` Left (InvalidStandardPollInterval 0)
it "rejects invalid long polling fields" $ do
validateConfig base {polling = LongPolling 0 100} `shouldBe` Left (InvalidLongPollSeconds 0)
validateConfig base {polling = LongPolling 10 0} `shouldBe` Left (InvalidLongPollIntervalMs 0)
it "rejects negative maxRetries but accepts zero" $ do
validateConfig base {maxRetries = -1} `shouldBe` Left (InvalidMaxRetries (-1))
validateConfig base {maxRetries = 0} `shouldBe` Right base {maxRetries = 0}
it "rejects invalid halt visibility timeout" $ do
validateConfig base {haltVisibilityTimeout = Just 0} `shouldBe` Left (InvalidHaltVisibilityTimeout 0)
it "rejects invalid retry policies" $ do
let invalidAttempts = defaultPollRetryConfig {maxAttempts = 0}
invalidBackoff = defaultPollRetryConfig {initialBackoff = -1}
validateConfig base {pollRetry = invalidAttempts} `shouldBe` Left (InvalidPollRetryMaxAttempts 0)
validateConfig base {ackRetry = invalidAttempts} `shouldBe` Left (InvalidAckRetryMaxAttempts 0)
validateConfig base {pollRetry = invalidBackoff} `shouldBe` Left (InvalidPollRetryBackoff (-1) 5)
validateConfig base {ackRetry = invalidBackoff} `shouldBe` Left (InvalidAckRetryBackoff (-1) 5)
-- | Tests for DeadLetterTarget
deadLetterTargetSpec :: Spec
deadLetterTargetSpec = describe "DeadLetterTarget" $ do
let queueName = case parseQueueName "dlq_test" of
Right q -> q
Left e -> error $ "Unexpected: " <> show e
routingKey = case parseRoutingKey "dlq.errors" of
Right r -> r
Left e -> error $ "Unexpected: " <> show e
it "DirectQueue holds a queue name" $ do
let target = DirectQueue queueName
case target of
DirectQueue q -> q `shouldBe` queueName
TopicRoute _ -> expectationFailure "Expected DirectQueue"
it "TopicRoute holds a routing key" $ do
let target = TopicRoute routingKey
case target of
TopicRoute r -> r `shouldBe` routingKey
DirectQueue _ -> expectationFailure "Expected TopicRoute"
it "DirectQueue and TopicRoute are not equal" $ do
DirectQueue queueName `shouldNotBe` TopicRoute routingKey
-- | Tests for smart constructors
smartConstructorSpec :: Spec
smartConstructorSpec = describe "Smart constructors" $ do
let queueName = case parseQueueName "dlq_test" of
Right q -> q
Left e -> error $ "Unexpected: " <> show e
routingKey = case parseRoutingKey "dlq.errors" of
Right r -> r
Left e -> error $ "Unexpected: " <> show e
describe "directDeadLetter" $ do
it "creates a DeadLetterConfig with DirectQueue target" $ do
let config = directDeadLetter queueName True
config.dlqTarget `shouldBe` DirectQueue queueName
it "sets includeMetadata correctly" $ do
let config = directDeadLetter queueName False
config.includeMetadata `shouldBe` False
describe "topicDeadLetter" $ do
it "creates a DeadLetterConfig with TopicRoute target" $ do
let config = topicDeadLetter routingKey True
config.dlqTarget `shouldBe` TopicRoute routingKey
it "sets includeMetadata correctly" $ do
let config = topicDeadLetter routingKey False
config.includeMetadata `shouldBe` False