gdax-0.5.0.0: test/Network/GDAX/Test/Feed.hs
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.GDAX.Test.Feed
( tests
) where
import Control.Lens
import Data.Aeson (FromJSON (..))
import Data.Aeson (Value (..))
import qualified Data.Aeson as Aeson
import Data.Aeson.Lens
import qualified Data.ByteString.Lazy as LBS
import Data.Proxy
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import Data.Vector (Vector)
import Network.GDAX.Test.Types
import Network.GDAX.Types.Feed
import Network.GDAX.Types.MarketData (ProductId)
import Network.WebSockets
import Test.Tasty
import Test.Tasty.HUnit
import Wuss
tests :: Env -> TestTree
tests e = testGroup "Feed Parse"
[ parseTestClient (mkBTCSub [ChannelHeartbeat]) "heartbeat" (Proxy :: Proxy Heartbeat)
, parseTestClient (mkBTCSub [ChannelTicker]) "ticker" (Proxy :: Proxy Ticker)
, parseTestClient (mkBTCSub [ChannelLevel2]) "snapshot" (Proxy :: Proxy Level2Snapshot)
, parseTestClient (mkBTCSub [ChannelLevel2]) "l2update" (Proxy :: Proxy Level2Update)
, parseTestClient (mkBTCSub [ChannelMatches]) "last_match" (Proxy :: Proxy Match)
, parseTestClient (mkBTCSub [ChannelMatches]) "match" (Proxy :: Proxy Match)
, parseTestClient (mkBTCSub [ChannelFull]) "received" (Proxy :: Proxy Received)
, parseTestClient (mkBTCSub [ChannelFull]) "open" (Proxy :: Proxy Open)
, parseTestClient (mkBTCSub [ChannelFull]) "done" (Proxy :: Proxy Done)
, parseTestClient (mkBTCSub [ChannelFull]) "match" (Proxy :: Proxy Match)
-- This one cannot run independently, since you only receive them if you
-- trade against yourself.
-- , parseTestClient (mkBTCSub [ChannelFull]) "change" (Proxy :: Proxy Change)
-- This one cannot run independently, since you have to be authenticated with
-- a margin profile.
-- , parseTestClient (mkBTCSub [ChannelFull]) "margin_profile_update" (Proxy :: Proxy MarginProfileUpdate)
-- This one cannot run independently, they occur very infrequently.
-- , parseTestClient (mkBTCSub [ChannelFull]) "activate" (Proxy :: Proxy Activate)
, case_sum e
]
mkBTCSub :: Vector Channel -> Subscriptions
mkBTCSub = mkSubscriptions "BTC-USD"
mkSubscriptions :: ProductId -> Vector Channel -> Subscriptions
mkSubscriptions pid cs = Subscriptions [] $ fmap fn cs
where
fn c = ChannelSubscription c [pid]
parseTestClient :: (FromJSON a) => Subscriptions -> Text -> Proxy a -> TestTree
parseTestClient subs t pt = testCase (T.unpack t) $ runSecureClient "ws-feed.gdax.com" 443 "/" $ \conn -> do
sendTextData conn (Aeson.encode testSub)
m1 <- receiveOfType conn t
let res = Aeson.eitherDecode m1
case res of
Left er -> fail (show er)
Right v ->
let final = asProxyTypeOf v pt
in return ()
sendTextData conn (Aeson.encode testUnSub)
where
testSub = Subscribe subs
testUnSub = UnSubscribe subs
case_sum :: Env -> TestTree
case_sum _ = testCase "sum" $
runSecureClient "ws-feed.gdax.com" 443 "/" client
where
client :: ClientApp ()
client conn = do
sendTextData conn (Aeson.encode testSub)
ms <- sequence $ take 100 $ repeat (receiveNotSubs conn)
sendTextData conn (Aeson.encode testUnSub)
let res = fmap Aeson.eitherDecode ms
mapM_ assertRight (res :: [Either String GdaxMessage])
testSub = Subscribe $ Subscriptions [] subs
testUnSub = UnSubscribe $ Subscriptions [] subs
subs =
[ ChannelSubscription ChannelHeartbeat ["BTC-USD"]
, ChannelSubscription ChannelTicker ["BTC-USD"]
, ChannelSubscription ChannelLevel2 ["BTC-USD"]
, ChannelSubscription ChannelMatches ["BTC-USD"]
]
receiveOfType :: Connection -> Text -> IO LBS.ByteString
receiveOfType conn t = receiveOfTypes conn [t]
receiveOfTypes :: Connection -> [Text] -> IO LBS.ByteString
receiveOfTypes conn ts = loop
where
tset = Set.fromList ts
loop = do
res <- receiveData conn
let asValue = Aeson.eitherDecode res :: Either String Value
case asValue of
Left er -> fail (show er)
Right v ->
case v ^? key "type" . _String of
Nothing -> loop
Just t ->
if Set.member t tset
then return res
else loop
receiveNotSubs :: Connection -> IO LBS.ByteString
receiveNotSubs conn = loop
where
loop = do
res <- receiveData conn
let asValue = Aeson.eitherDecode res :: Either String Value
case asValue of
Left er -> fail (show er)
Right v ->
if (v ^? key "type") == (Just (String "subscriptions"))
then loop
else return res
assertRight :: (Show e) => Either e a -> IO ()
assertRight (Right _) = return ()
assertRight (Left er) = fail (show er)