amqp-0.24.0: test/BasicPublishSpec.hs
{-# OPTIONS -XOverloadedStrings #-}
module BasicPublishSpec (main, spec) where
import Test.Hspec
import Network.AMQP
import Data.ByteString.Lazy.Char8 as BL
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Word
import Control.Concurrent (threadDelay)
import Control.Concurrent.STM
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
describe "publishMsg" $ do
context "with a routing key" $ do
it "publishes a message" $ do
let q = "haskell-amqp.queues.publish-over-default-exchange1"
conn <- openConnection "127.0.0.1" "/" "guest" "guest"
ch <- openChannel conn
(_, n1, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
n1 `shouldBe` 0
-- publishes using default exchange
publishMsg ch "" q
(newMsg {msgBody = (BL.pack "hello")})
threadDelay (1000 * 100)
(_, n2, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
n2 `shouldBe` 1
n3 <- deleteQueue ch q
n3 `shouldBe` 1
closeConnection conn
context "with a blank routing key" $ do
it "publishes a message" $ do
let q = "haskell-amqp.queues.publish-over-fanout1"
e = "haskell-amqp.fanout.d.na"
conn <- openConnection "127.0.0.1" "/" "guest" "guest"
ch <- openChannel conn
_ <- declareExchange ch (newExchange {exchangeName = e,
exchangeType = "fanout",
exchangeDurable = True})
(_, _, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
_ <- purgeQueue ch q
bindQueue ch q e ""
publishMsg ch e ""
(newMsg {msgBody = (BL.pack "hello")})
threadDelay (1000 * 100)
(_, n, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
n `shouldBe` 1
_ <- deleteQueue ch q
closeConnection conn
context "confirmSelect" $ do
it "receives a confirmation message" $ do
let q = "haskell-amqp.queues.publish-over-fanout1"
e = "haskell-amqp.fanout.d.na"
conn <- openConnection "127.0.0.1" "/" "guest" "guest"
ch <- openChannel conn
confirmSelect ch True
(confirmMap, counter) <- atomically $ (,) <$> newTVar Map.empty <*> newTVar 0
addConfirmationListener ch (handleConfirms counter confirmMap)
_ <- declareExchange ch (newExchange {exchangeName = e,
exchangeType = "fanout",
exchangeDurable = True})
(_, _, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
_ <- purgeQueue ch q
bindQueue ch q e ""
_ <- traverse (\n -> do
sn' <- publishMsg ch e "" (newMsg {msgBody = (BL.pack "hello")})
case sn' of
Just sn -> atomically $ addSequenceNumber confirmMap (fromIntegral sn) n
Nothing -> return ()
) [1..5]
threadDelay (1000 * 100)
(_, n, _) <- declareQueue ch (newQueue {queueName = q, queueDurable = False})
n `shouldBe` 5
cMap' <- atomically $ readTVar confirmMap
cMap' `shouldBe` Map.empty
counter' <- atomically $ readTVar counter
counter' `shouldBe` 5
_ <- deleteQueue ch q
closeConnection conn
addSequenceNumber :: TVar (Map Word64 Int) -> Word64 -> Int -> STM ()
addSequenceNumber cMap sn n = modifyTVar' cMap (Map.insert sn n)
removeSequenceNumber :: TVar (Map Word64 Int) -> Word64 -> STM ()
removeSequenceNumber cMap sn = modifyTVar' cMap (Map.delete sn)
increaseCounter :: TVar Int -> STM ()
increaseCounter n = modifyTVar' n (+1)
handleConfirms :: TVar Int -> TVar (Map Word64 Int) -> (Word64, Bool, AckType) -> IO ()
handleConfirms c _ (_, False, BasicNack) = atomically $ increaseCounter c
handleConfirms c _ (_, True, BasicNack) = atomically $ increaseCounter c
handleConfirms c cMap (n, False, BasicAck) = atomically $ removeSequenceNumber cMap n >> increaseCounter c
handleConfirms c cMap (n, True, BasicAck) = atomically $ do
cMap' <- readTVar cMap
let (lt, eq', _) = Map.splitLookup n cMap'
case eq' of
Just _ -> removeSequenceNumber cMap n >> increaseCounter c
Nothing -> return ()
_ <- traverse (\i -> removeSequenceNumber cMap i >> increaseCounter c) (Map.keys lt)
return ()