{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Network.MessagePack.RPC.Client.WebSocketSpec ( main , spec ) where import Control.Applicative (empty, (<|>)) import Control.Concurrent (threadDelay) import Control.Concurrent.Async (async, forConcurrently, wait) import Data.ByteString.Char8 () import qualified Data.MessagePack as MsgPack import qualified Data.Text () import qualified Network.WebSockets.Skews as Skews import System.Envy (FromEnv, decodeEnv, env, fromEnv) import Test.Hspec import Text.Read (readMaybe) import qualified Data.MessagePack.RPC as Rpc import qualified Network.MessagePack.RPC.Client.WebSocket as Rpc -- `main` is here so that this module can be run from GHCi on its own. It is -- not needed for automatic spec discovery. main :: IO () main = hspec spec newtype PortNumber = PortNumber Int deriving (Eq, Show) instance FromEnv PortNumber where #if MIN_VERSION_envy(2, 0, 0) fromEnv _ = do #else fromEnv = do #endif mpn <- (readMaybe <$> env "MESSAGE_PACK_RPC_TEST_SERVER_PORT") <|> pure (Just 8615) PortNumber <$> maybe empty pure mpn spec :: Spec spec = describe "call" $ do PortNumber pn <- runIO $ either fail return =<< decodeEnv let host = "localhost" server <- runIO $ Skews.start $ Skews.Args host pn let withClient = Rpc.withClient ("ws://" ++ host ++ ":" ++ show pn) Rpc.defaultConfig before_ (Skews.reinit server) $ do it "when several threads call, every thread should receive corresponding response" $ do Skews.setDefaultRequestHandler server $ \bin -> do rpcMsg <- MsgPack.unpack bin case rpcMsg of Rpc.RequestMessage mid _methodName args -> return $ Just $ MsgPack.pack $ Rpc.ResponseMessage mid $ Right $ head args _ -> return Nothing let requestIds = [1 .. 10] expectedResponses = map (Right . MsgPack.ObjectWord) requestIds actualResponses <- withClient $ \client -> forConcurrently requestIds $ Rpc.call client "someMethod" . (: []) . MsgPack.ObjectWord actualResponses `shouldBe` expectedResponses it "when a client is waiting for a response, any unrelated frames sent to the connection is ignored." $ do let midSentByClient = 0 midNotSentByClient = 1 expectedResponse = MsgPack.ObjectStr "The right response" actualResponse <- async $ withClient $ \client -> Rpc.call client "someMethod" [MsgPack.ObjectStr "request"] threadDelay $ 1000 * 1000 -- Wait until connection is established Skews.sendToClients server $ MsgPack.pack $ Rpc.ResponseMessage midNotSentByClient $ Right $ MsgPack.ObjectStr "Wrong response" Skews.sendToClients server $ MsgPack.pack $ Rpc.NotificationMessage "methodName" [MsgPack.ObjectStr "notification"] Skews.sendToClients server $ MsgPack.pack $ Rpc.ResponseMessage midSentByClient $ Right expectedResponse wait actualResponse `shouldReturn` Right expectedResponse