natskell-0.3.0.0: test/Unit/JetStream/ConsumerSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module JetStream.ConsumerSpec (spec) where
import Data.Aeson
( Value
, eitherDecode
, encode
, object
, (.=)
)
import qualified Data.ByteString.Lazy as LBS
import JetStream.Consumer.Types
import Test.Hspec
spec :: Spec
spec = do
describe "ConsumerConfig request JSON" $ do
it "encodes only populated fields" $ do
eitherDecode (encode durablePullConsumerConfigRequest) `shouldBe` Right durablePullConsumerConfigValue
it "encodes push and ordered consumer fields" $ do
eitherDecode (encode pushConsumerConfigRequest) `shouldBe` Right pushConsumerConfigValue
it "lets consumer target and kind own mutually exclusive fields" $ do
eitherDecode (encode targetKindConsumerConfigRequest)
`shouldBe` Right targetKindConsumerConfigValue
describe "Consumer reset request JSON" $ do
it "encodes an optional reset sequence" $ do
eitherDecode (encode (consumerResetRequest [withConsumerResetSequence 7]))
`shouldBe` Right (object ["seq" .= (7 :: Int)])
describe "ConsumerInfo JSON" .
it "decodes server responses into concrete fields" $ do
eitherDecode consumerInfoJSON `shouldBe` Right consumerInfo
describe "ConsumerResetResponse JSON" .
it "decodes reset metadata with the updated consumer info" $ do
fmap consumerResetResponseSequence (eitherDecode consumerResetJSON)
`shouldBe` Right 7
describe "Consumer list JSON" $ do
it "decodes detailed consumer list responses" $ do
eitherDecode consumerListJSON
`shouldBe` Right ConsumerListResponse
{ consumerListTotal = 1
, consumerListOffset = 0
, consumerListLimit = 1024
, consumerListConsumers = [consumerInfo]
}
it "decodes consumer name responses" $ do
eitherDecode consumerNamesJSON
`shouldBe` Right ConsumerNamesResponse
{ consumerNamesTotal = 2
, consumerNamesOffset = 0
, consumerNamesLimit = 1024
, consumerNamesConsumers = ["orders-puller", "orders-worker"]
}
durablePullConsumerConfigRequest :: ConsumerConfigRequest
durablePullConsumerConfigRequest =
consumerConfigRequest
[ withConsumerDurableName "orders-puller"
, withConsumerName "orders-puller"
, withConsumerDeliverPolicy DeliverAll
, withConsumerAckPolicy AckExplicit
, withConsumerReplayPolicy ReplayInstant
, withConsumerFilter (ConsumerFilterSubjects ["orders.created", "orders.updated"])
, withConsumerMaxDeliver 5
, withConsumerAckWait 30
, withConsumerMaxAckPending 256
, withConsumerInactiveThreshold 60
]
durablePullConsumerConfigValue :: Value
durablePullConsumerConfigValue = object
[ "durable_name" .= ("orders-puller" :: String)
, "name" .= ("orders-puller" :: String)
, "deliver_policy" .= ("all" :: String)
, "ack_policy" .= ("explicit" :: String)
, "replay_policy" .= ("instant" :: String)
, "filter_subjects" .= ["orders.created" :: String, "orders.updated"]
, "max_deliver" .= (5 :: Int)
, "ack_wait" .= (30000000000 :: Integer)
, "max_ack_pending" .= (256 :: Int)
, "inactive_threshold" .= (60000000000 :: Integer)
]
pushConsumerConfigRequest :: ConsumerConfigRequest
pushConsumerConfigRequest =
consumerConfigRequest
[ withConsumerName "push"
, withConsumerDeliverSubject "deliver"
, withConsumerDeliverGroup "workers"
, withConsumerAckPolicy AckExplicit
, withConsumerMaxWaiting 512
, withConsumerIdleHeartbeat 1
, withConsumerHeadersOnly True
, withConsumerReplicas 1
, withConsumerMemoryStorage True
]
pushConsumerConfigValue :: Value
pushConsumerConfigValue = object
[ "name" .= ("push" :: String)
, "deliver_subject" .= ("deliver" :: String)
, "deliver_group" .= ("workers" :: String)
, "ack_policy" .= ("explicit" :: String)
, "max_waiting" .= (512 :: Int)
, "idle_heartbeat" .= (1000000000 :: Integer)
, "headers_only" .= True
, "num_replicas" .= (1 :: Int)
, "mem_storage" .= True
]
targetKindConsumerConfigRequest :: ConsumerConfigRequest
targetKindConsumerConfigRequest =
applyConsumerTarget (NamedConsumer "right") .
applyConsumerKind (PushConsumer "deliver") $
consumerConfigRequest
[ withConsumerName "wrong"
, withConsumerDeliverSubject "wrong"
, withConsumerDeliverGroup "workers"
]
targetKindConsumerConfigValue :: Value
targetKindConsumerConfigValue = object
[ "name" .= ("right" :: String)
, "deliver_subject" .= ("deliver" :: String)
, "deliver_group" .= ("workers" :: String)
]
consumerInfoJSON :: LBS.ByteString
consumerInfoJSON =
"{\"type\":\"io.nats.jetstream.api.v1.consumer_info_response\",\"stream_name\":\"ORDERS\",\"name\":\"orders-puller\",\"created\":\"2024-01-01T00:00:00Z\",\"config\":{\"deliver_policy\":\"all\",\"ack_policy\":\"explicit\",\"replay_policy\":\"instant\"},\"delivered\":{\"consumer_seq\":2,\"stream_seq\":10},\"ack_floor\":{\"consumer_seq\":1,\"stream_seq\":9},\"num_ack_pending\":1,\"num_redelivered\":0,\"num_waiting\":0,\"num_pending\":3}"
consumerResetJSON :: LBS.ByteString
consumerResetJSON =
"{\"type\":\"io.nats.jetstream.api.v1.consumer_reset_response\",\"stream_name\":\"ORDERS\",\"name\":\"orders-puller\",\"created\":\"2024-01-01T00:00:00Z\",\"config\":{\"deliver_policy\":\"all\",\"ack_policy\":\"explicit\",\"replay_policy\":\"instant\"},\"delivered\":{\"consumer_seq\":1,\"stream_seq\":7},\"ack_floor\":{\"consumer_seq\":0,\"stream_seq\":0},\"num_ack_pending\":0,\"num_redelivered\":0,\"num_waiting\":0,\"num_pending\":3,\"reset_seq\":7}"
consumerListJSON :: LBS.ByteString
consumerListJSON =
"{\"type\":\"io.nats.jetstream.api.v1.consumer_list_response\",\"total\":1,\"offset\":0,\"limit\":1024,\"consumers\":[{\"type\":\"io.nats.jetstream.api.v1.consumer_info_response\",\"stream_name\":\"ORDERS\",\"name\":\"orders-puller\",\"created\":\"2024-01-01T00:00:00Z\",\"config\":{\"deliver_policy\":\"all\",\"ack_policy\":\"explicit\",\"replay_policy\":\"instant\"},\"delivered\":{\"consumer_seq\":2,\"stream_seq\":10},\"ack_floor\":{\"consumer_seq\":1,\"stream_seq\":9},\"num_ack_pending\":1,\"num_redelivered\":0,\"num_waiting\":0,\"num_pending\":3}]}"
consumerNamesJSON :: LBS.ByteString
consumerNamesJSON =
"{\"type\":\"io.nats.jetstream.api.v1.consumer_names_response\",\"total\":2,\"offset\":0,\"limit\":1024,\"consumers\":[\"orders-puller\",\"orders-worker\"]}"
consumerInfo :: ConsumerInfo
consumerInfo = ConsumerInfo
{ consumerInfoStreamName = "ORDERS"
, consumerInfoName = "orders-puller"
, consumerInfoCreated = timestamp
, consumerInfoConfig = ConsumerConfig
{ consumerConfigDurableName = Nothing
, consumerConfigName = Nothing
, consumerConfigDescription = Nothing
, consumerConfigDeliverSubject = Nothing
, consumerConfigDeliverGroup = Nothing
, consumerConfigDeliverPolicy = DeliverAll
, consumerConfigAckPolicy = AckExplicit
, consumerConfigReplayPolicy = ReplayInstant
, consumerConfigFilterSubject = Nothing
, consumerConfigFilterSubjects = Nothing
, consumerConfigAckWait = Nothing
, consumerConfigMaxDeliver = Nothing
, consumerConfigMaxWaiting = Nothing
, consumerConfigMaxAckPending = Nothing
, consumerConfigInactiveThreshold = Nothing
, consumerConfigIdleHeartbeat = Nothing
, consumerConfigHeadersOnly = Nothing
, consumerConfigReplicas = Nothing
, consumerConfigMemoryStorage = Nothing
}
, consumerInfoDelivered = ConsumerSequenceInfo
{ consumerSequenceConsumer = 2
, consumerSequenceStream = 10
, consumerSequenceLast = Nothing
}
, consumerInfoAckFloor = ConsumerSequenceInfo
{ consumerSequenceConsumer = 1
, consumerSequenceStream = 9
, consumerSequenceLast = Nothing
}
, consumerInfoNumAckPending = 1
, consumerInfoNumRedelivered = 0
, consumerInfoNumWaiting = 0
, consumerInfoNumPending = 3
}
timestamp = read "2024-01-01 00:00:00 UTC"