packages feed

elm-websocket-1.0: test/BroadcastSpec.hs

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

module BroadcastSpec where

import           Elm.WebSocket
import           Test.Hspec

import           Control.Concurrent (forkIO)
import qualified Control.Concurrent.Broadcast as B
import qualified Control.Concurrent.Event     as E
import           Control.Monad                (forever)
import           Data.Aeson                   (ToJSON, FromJSON)
import           Data.Text (Text)
import           GHC.Generics                 (Generic)
import           Network.HTTP.Types
import           Network.Wai
import           Network.Wai.Handler.Warp     (testWithApplication)
import qualified Network.WebSockets           as WS

newtype Message = Message
  { hello :: String
  } deriving (Eq, Show, Generic)

instance ToJSON Message
instance FromJSON Message

type ReceivedMessage = B.Broadcast Text

type TestState = (Broadcaster, ReceivedMessage)

spec :: Spec
spec = broadcastSpec

broadcastSpec :: Spec
broadcastSpec = around runWithClientServer $
  describe "Broadcaster" $
    it "should broadcast a message to connected clients" $ \(broadcaster, messages) -> do
      _ <- broadcast broadcaster $ Message "broadcast"
      message <- waitForMessage messages
      message `shouldBe` Just "{\"hello\":\"broadcast\"}"


waitForMessage :: ReceivedMessage -> IO (Maybe Text)
waitForMessage messages = B.listenTimeout messages 100000


runWithClientServer :: (TestState -> IO a) -> IO a
runWithClientServer action = do
  broadcaster <- newBroadcaster
  messages <- B.new
  isConnected <- E.new

  testWithApplication (return $ withWebSocketBroadcaster broadcaster webSocketService httpApplication) $ \port -> do
      _ <- forkIO $ WS.runClient "localhost" port "" $ clientReceiver messages isConnected
      E.wait isConnected
      action (broadcaster, messages)

  where

    clientReceiver :: ReceivedMessage -> E.Event -> WS.Connection -> IO ()
    clientReceiver receivedMessageBroadcast isConnected connection = do
      E.set isConnected
      _ <- forever $ WS.receiveData connection >>= B.broadcast receivedMessageBroadcast
      return ()

    webSocketService :: WebSocketServer Message Message
    webSocketService _ = return Nothing

    httpApplication :: Application
    httpApplication _ respond = respond $ responseLBS Network.HTTP.Types.status400 [] "Not a WebSocket request"