{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE QuasiQuotes #-} module Test.Transaq ( unitTests ) where import Control.Monad.State (execState) import Data.AEq ((~==)) import qualified Data.ByteString as BS import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (assertBool, testCase, (@=?)) import Text.RawString.QQ import Text.XML.Light.Input (parseXMLDoc) import Transaq import Xeno.SAX (process) unitTests :: TestTree unitTests = testGroup "Parsing" [ unitTestsXml ] unitTestsXml :: TestTree unitTestsXml = testGroup "Parsing via haskell-xml" [ testParseResult , testParseCandles , testParseClient , testParseClientWithoutForts , testParseServerStatus , testParseMarkets , testParseCandleKinds , testParseSecurities , testParseSecInfo , testParseQuotations , testParseAllTrades , testParseQuotes , testParseOrders , testParseTrades ] testParseResult :: TestTree testParseResult = testGroup "Parsing result" [ testParseResultSuccess , testParseResultSuccessWithTransactionId , testParseResultFailure ] where resultSuccess = [r||] :: T.Text testParseResultSuccess = testCase "Parse success result" $ do (Just (ResponseSuccess Nothing)) @=? (parseXMLDoc resultSuccess >>= fromXml :: Maybe ResponseResult) resultSuccessWithTransactionId = [r||] :: T.Text testParseResultSuccessWithTransactionId = testCase "Parse success result with transaction ID" $ do (Just (ResponseSuccess (Just 12))) @=? (parseXMLDoc resultSuccessWithTransactionId >>= fromXml :: Maybe ResponseResult) resultFailure = [r||] :: T.Text testParseResultFailure = testCase "Parse failure result" $ do (Just (ResponseFailure "")) @=? (parseXMLDoc resultFailure >>= fromXml :: Maybe ResponseResult) testParseCandles :: TestTree testParseCandles = testCase "Parse ResponseCandles - valid XML" $ do let (Just responseCandles) = (parseXMLDoc validCandles >>= fromXml :: Maybe ResponseCandles) SecurityId "foo" "bar" @=? cSecurity responseCandles 1 @=? cPeriodId responseCandles StatusEndOfHistory @=? cStatus responseCandles 1 @=? (length . cCandles) responseCandles let candle = head . cCandles $ responseCandles assertBool "Candle open mismatch" (41.3 ~== cOpen candle) assertBool "Candle high mismatch" (42 ~== cHigh candle) assertBool "Candle low mismatch" (40 ~== cLow candle) assertBool "Candle close mismatch" (41.6 ~== cClose candle) 1234 @=? cVolume candle 78 @=? cOpenInterest candle where validCandles = [r| |] :: T.Text testParseClient :: TestTree testParseClient = testCase "Parse ResponseClient - valid XML" $ do let (Just (ResponseClient clientData)) = (parseXMLDoc validClient >>= fromXml :: Maybe ResponseClient) "FOO" @=? cClientId clientData "TYPE" @=? cType clientData "CURRENCY" @=? cCurrency clientData "MARKET" @=? cMarket clientData "UNION" @=? cUnion clientData Just "FORTS_ACC" @=? cForts clientData where validClient = [r| TYPE CURRENCY MARKET UNION FORTS_ACC |] :: T.Text testParseClientWithoutForts :: TestTree testParseClientWithoutForts = testCase "Parse ResponseClient - valid XML (without FORTS account ID)" $ do let (Just (ResponseClient clientData)) = (parseXMLDoc validClient >>= fromXml :: Maybe ResponseClient) "FOO" @=? cClientId clientData "TYPE" @=? cType clientData "CURRENCY" @=? cCurrency clientData "MARKET" @=? cMarket clientData "UNION" @=? cUnion clientData Nothing @=? cForts clientData where validClient = [r| TYPE CURRENCY MARKET UNION |] :: T.Text testParseServerStatus :: TestTree testParseServerStatus = testCase "Parse ResponseServerStatus - valid XML (full)" $ do let (Just serverStatus) = (parseXMLDoc validServerStatus >>= fromXml :: Maybe ResponseServerStatus) Just 22 @=? serverId serverStatus Connected @=? state serverStatus Just True @=? recover serverStatus Just "TZ" @=? serverTimezone serverStatus Just 42 @=? systemVersion serverStatus Just 51 @=? build serverStatus where validServerStatus = [r| |] :: T.Text testParseMarkets :: TestTree testParseMarkets = testCase "Parse ResponseMarkets - valid XML" $ do let (Just (ResponseMarkets markets)) = (parseXMLDoc validMarkets >>= fromXml :: Maybe ResponseMarkets) 2 @=? length markets let [foo, bar] = markets 1 @=? marketId foo "FOO" @=? marketName foo 2 @=? marketId bar "BAR" @=? marketName bar where validMarkets = [r| FOO BAR |] :: T.Text testParseCandleKinds :: TestTree testParseCandleKinds = testCase "Parse ResponseCandleKinds - valid XML" $ do let (Just (ResponseCandleKinds candleKinds)) = (parseXMLDoc validCandleKinds >>= fromXml :: Maybe ResponseCandleKinds) 2 @=? length candleKinds let [m10, m15] = candleKinds 4 @=? kCandleKindId m10 600 @=? kPeriod m10 "M10" @=? kName m10 5 @=? kCandleKindId m15 900 @=? kPeriod m15 "M15" @=? kName m15 where validCandleKinds = [r| 4 600 M10 5 900 M15 |] :: T.Text testParseSecurities :: TestTree testParseSecurities = testCase "Parse ResponseSecurities - valid XML" $ do let (Just (ResponseSecurities securities)) = (parseXMLDoc validSecurities >>= fromXml :: Maybe ResponseSecurities) 1 @=? length securities let sec = head securities "SECCODE" @=? sSeccode sec "CLASS" @=? sInstrClass sec "BOARD" @=? sBoard sec "15" @=? sMarket sec "CURRENCY" @=? sCurrency sec "SHORTNAME" @=? sShortName sec 3 @=? sDecimals sec assertBool "Minstep is not valid" (0.1 ~== sMinStep sec) 10 @=? sLotSize sec 1 @=? sLotDivider sec assertBool "Point cost is not valid" (6.28 ~== sPointCost sec) "SECTYPE" @=? sSecType sec where validSecurities = [r| SECCODE CLASS BOARD 15 CURRENCY SHORTNAME 3 0.1 10 1 6.28 SECTYPE SECTZ 1 FOO CURRENCYID |] :: T.Text testParseSecInfo :: TestTree testParseSecInfo = testCase "Parse ResponseSecInfo - valid XML" $ do let (Just sec) = (parseXMLDoc validSecInfo >>= fromXml :: Maybe ResponseSecInfo) "SECNAME" @=? secName sec "SECCODE" @=? sec.secCode 44 @=? market sec "PNAME" @=? pname sec assertBool "Clearing price is not valid" (12.34 ~== clearingPrice sec) assertBool "Min price is not valid" (10 ~== minPrice sec) assertBool "Max price is not valid" (20 ~== maxPrice sec) assertBool "Point cost is not valid" (6.28 ~== pointCost sec) where validSecInfo = [r| SECNAME SECCODE 44 PNAME 12.34 10 20 6.28 |] :: T.Text testParseQuotations :: TestTree testParseQuotations = testCase "Parse ResponseQuotations - valid XML (full)" $ do let (Just (ResponseQuotations quotations)) = (parseXMLDoc validQuotations >>= fromXml :: Maybe ResponseQuotations) 1 @=? length quotations let q = head quotations 1 @=? qSecId q "BOARD" @=? qBoard q "SECCODE" @=? qSeccode q let (Just open') = qOpen q assertBool "Open is not valid" (12.34 ~== open') let (Just waprice') = qWaprice q assertBool "WA price is not valid" (13.8 ~== waprice') Just 40 @=? qBidDepth q Just 140 @=? qBidDepthT q Just 10 @=? qNumBids q let (Just bid') = qBid q assertBool "Bid is not valid" (11.01 ~== bid') Just 50 @=? qOfferDepth q Just 150 @=? qOfferDepthT q Just 15 @=? qNumOffers q let (Just offer') = qOffer q assertBool "Offer is not valid" (11.05 ~== offer') Just 1000 @=? qNumTrades q Just 50000 @=? qVolToday q Just 1000 @=? qOpenPositions q let (Just last') = qLastPrice q assertBool "Last price is not valid" (11.03 ~== last') Just 8 @=? qQuantity q -- TODO check time let (Just valtoday') = qValToday q assertBool "Val today is not valid" (12345678 ~== valtoday') where validQuotations = [r| BOARD SECCODE 12.34 13.8 40 140 10 11.01 50 150 15 11.05 1000 50000 1000 11.03 8 12345678 |] :: T.Text testParseAllTrades :: TestTree testParseAllTrades = testCase "Parse ResponseAllTrades - valid XML (full)" $ do let (Just (ResponseAllTrades trades)) = (parseXMLDoc validAllTrades >>= fromXml :: Maybe ResponseAllTrades) 2 @=? length trades let [t1, t2] = trades 1 @=? attSecId t1 "SEC1" @=? attSecCode t1 14 @=? attTradeNo t1 -- TODO check time "BOARD" @=? attBoard t1 assertBool "Price is not valid" (12.34 ~== attPrice t1) 10 @=? attQuantity t1 Buy @=? attBuysell t1 100 @=? attOpenInterest t1 PeriodNormal @=? attPeriod t1 2 @=? attSecId t2 "SEC2" @=? attSecCode t2 15 @=? attTradeNo t2 -- TODO check time "BOARD" @=? attBoard t2 assertBool "Price is not valid" (12.35 ~== attPrice t2) 11 @=? attQuantity t2 Sell @=? attBuysell t2 200 @=? attOpenInterest t2 PeriodNormal @=? attPeriod t2 where validAllTrades = [r| SEC1 14 BOARD 12.34 10 B 100 N SEC2 15 BOARD 12.35 11 S 200 N |] :: T.Text testParseQuotes :: TestTree testParseQuotes = testCase "Parse ResponseQuotes - valid XML (full)" $ do let (Just (ResponseQuotes quotes)) = (parseXMLDoc validQuotes >>= fromXml :: Maybe ResponseQuotes) 2 @=? length quotes let [q1, q2] = quotes 1 @=? q1.secId "BOARD" @=? q1.board assertBool "Price is not valid" (12.34 ~== q1.price) Just "SOURCE" @=? q1.source Just 10 @=? q1.buy Nothing @=? q1.sell 2 @=? q2.secId "BOARD" @=? q2.board assertBool "Price is not valid" (12.35 ~== q2.price) Just "SOURCE" @=? q2.source Nothing @=? q2.buy Just 11 @=? q2.sell where validQuotes = [r| BOARD SEC1 12.34 SOURCE 10 BOARD SEC2 12.35 SOURCE 11 |] :: T.Text testParseOrders :: TestTree testParseOrders = testCase "Parse ResponseOrders - valid XML (full)" $ do let (Just (ResponseOrders orders)) = (parseXMLDoc validOrders >>= fromXml :: Maybe ResponseOrders) 1 @=? length orders let order = head orders 12 @=? oTransactionId order 42 @=? oOrderNo order 1 @=? oSecId order "BOARD" @=? oBoard order "SEC1" @=? oSecCode order Just "CLIENT" @=? oClient order Just "UNION" @=? oUnion order Just OrderActive @=? oStatus order Just Buy @=? oBuysell order let Just price' = oPrice order assertBool "Price is not valid" (12.34 ~== price') Just 12 @=? oQuantity order where validOrders = [r| 42 1 BOARD SEC1 CLIENT UNION active B 12.34 12 |] :: T.Text testParseTrades :: TestTree testParseTrades = testCase "Parse ResponseTrades - valid XML (full)" $ do let (Just (ResponseTrades trades)) = (parseXMLDoc validTrades >>= fromXml :: Maybe ResponseTrades) 1 @=? length trades let trade = head trades 1 @=? tSecId trade 12 @=? tTradeNo trade 42 @=? tOrderNo trade "BOARD" @=? tBoard trade "SEC1" @=? tSecCode trade "CLIENT" @=? tClient trade "UNION" @=? tUnion trade Buy @=? tBuysell trade -- TODO check time True @=? (123.4 ~== tValue trade) True @=? (0.5 ~== tComission trade) 10 @=? tQuantity trade True @=? (12.34 ~== tPrice trade) where validTrades = [r| 1 12 42 BOARD SEC1 CLIENT UNION B 123.40 0.5 10 12.34 |] :: T.Text