|
|
|
@ -25,12 +25,14 @@ import Data.Aeson |
|
|
|
import Data.Time.Clock |
|
|
|
import Data.Time.Clock |
|
|
|
import Data.Time.Calendar |
|
|
|
import Data.Time.Calendar |
|
|
|
import Data.Maybe |
|
|
|
import Data.Maybe |
|
|
|
|
|
|
|
import qualified Data.List as L |
|
|
|
import Data.IORef |
|
|
|
import Data.IORef |
|
|
|
import Data.UUID as U |
|
|
|
import Data.UUID as U |
|
|
|
import Data.UUID.V4 as UV4 |
|
|
|
import Data.UUID.V4 as UV4 |
|
|
|
|
|
|
|
|
|
|
|
data MockBrokerState = MockBrokerState { |
|
|
|
data MockBrokerState = MockBrokerState { |
|
|
|
orders :: [Order], |
|
|
|
orders :: [Order], |
|
|
|
|
|
|
|
cancelledOrders :: [Order], |
|
|
|
notificationCallback :: Maybe (Notification -> IO ()) |
|
|
|
notificationCallback :: Maybe (Notification -> IO ()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -45,7 +47,11 @@ mockSubmitOrder state order = do |
|
|
|
submittedOrder = order { orderState = Submitted } |
|
|
|
submittedOrder = order { orderState = Submitted } |
|
|
|
|
|
|
|
|
|
|
|
mockCancelOrder :: IORef MockBrokerState -> OrderId -> IO () |
|
|
|
mockCancelOrder :: IORef MockBrokerState -> OrderId -> IO () |
|
|
|
mockCancelOrder state = undefined |
|
|
|
mockCancelOrder state oid = do |
|
|
|
|
|
|
|
ors <- orders <$> readIORef state |
|
|
|
|
|
|
|
case L.find (\o -> orderId o == oid) ors of |
|
|
|
|
|
|
|
Just order -> atomicModifyIORef' state (\s -> (s { cancelledOrders = order : cancelledOrders s}, ())) |
|
|
|
|
|
|
|
Nothing -> return () |
|
|
|
|
|
|
|
|
|
|
|
mockStopBroker :: IORef MockBrokerState -> IO () |
|
|
|
mockStopBroker :: IORef MockBrokerState -> IO () |
|
|
|
mockStopBroker state = return () |
|
|
|
mockStopBroker state = return () |
|
|
|
@ -54,6 +60,7 @@ mockStopBroker state = return () |
|
|
|
mkMockBroker accs = do |
|
|
|
mkMockBroker accs = do |
|
|
|
state <- newIORef MockBrokerState { |
|
|
|
state <- newIORef MockBrokerState { |
|
|
|
orders = [], |
|
|
|
orders = [], |
|
|
|
|
|
|
|
cancelledOrders = [], |
|
|
|
notificationCallback = Nothing |
|
|
|
notificationCallback = Nothing |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -66,18 +73,32 @@ mkMockBroker accs = do |
|
|
|
}, state) |
|
|
|
}, state) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unitTests = testGroup "Broker.Server" [testBrokerServerStartStop, testBrokerServerSubmitOrder] |
|
|
|
unitTests = testGroup "Broker.Server" [testBrokerServerStartStop |
|
|
|
|
|
|
|
, testBrokerServerSubmitOrder |
|
|
|
|
|
|
|
, testBrokerServerSubmitOrderToUnknownAccount |
|
|
|
|
|
|
|
, testBrokerServerCancelOrder ] |
|
|
|
|
|
|
|
|
|
|
|
testBrokerServerStartStop = testCase "Broker Server starts and stops" $ withContext (\ctx -> do |
|
|
|
testBrokerServerStartStop = testCase "Broker Server starts and stops" $ withContext (\ctx -> do |
|
|
|
ep <- toText <$> UV4.nextRandom |
|
|
|
ep <- toText <$> UV4.nextRandom |
|
|
|
broS <- startBrokerServer [] ctx ("inproc://brokerserver" `T.append` ep) |
|
|
|
broS <- startBrokerServer [] ctx ("inproc://brokerserver" `T.append` ep) |
|
|
|
stopBrokerServer broS) |
|
|
|
stopBrokerServer broS) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
makeEndpoint = do |
|
|
|
|
|
|
|
uid <- toText <$> UV4.nextRandom |
|
|
|
|
|
|
|
return $ "inproc://brokerserver" `T.append` uid |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
connectAndSendOrder step sock order ep = do |
|
|
|
|
|
|
|
step "Connecting" |
|
|
|
|
|
|
|
connect sock (T.unpack ep) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Sending request" |
|
|
|
|
|
|
|
send sock [] (BL.toStrict . encode $ RequestSubmitOrder 1 order) |
|
|
|
|
|
|
|
threadDelay 10000 |
|
|
|
|
|
|
|
|
|
|
|
testBrokerServerSubmitOrder = testCaseSteps "Broker Server submits order" $ \step -> withContext (\ctx -> do |
|
|
|
testBrokerServerSubmitOrder = testCaseSteps "Broker Server submits order" $ \step -> withContext (\ctx -> do |
|
|
|
step "Setup" |
|
|
|
step "Setup" |
|
|
|
uid <- toText <$> UV4.nextRandom |
|
|
|
|
|
|
|
(mockBroker, broState) <- mkMockBroker ["demo"] |
|
|
|
(mockBroker, broState) <- mkMockBroker ["demo"] |
|
|
|
let ep = "inproc://brokerserver" `T.append` uid |
|
|
|
ep <- makeEndpoint |
|
|
|
let order = mkOrder { |
|
|
|
let order = mkOrder { |
|
|
|
orderAccountId = "demo", |
|
|
|
orderAccountId = "demo", |
|
|
|
orderSecurity = "FOO", |
|
|
|
orderSecurity = "FOO", |
|
|
|
@ -87,12 +108,7 @@ testBrokerServerSubmitOrder = testCaseSteps "Broker Server submits order" $ \ste |
|
|
|
} |
|
|
|
} |
|
|
|
bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS -> |
|
|
|
bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS -> |
|
|
|
withSocket ctx Req (\sock -> do |
|
|
|
withSocket ctx Req (\sock -> do |
|
|
|
step "Connecting" |
|
|
|
connectAndSendOrder step sock order ep |
|
|
|
connect sock (T.unpack ep) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Sending request" |
|
|
|
|
|
|
|
send sock [] (BL.toStrict . encode $ RequestSubmitOrder 1 order) |
|
|
|
|
|
|
|
threadDelay 10000 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Checking that order is submitted to BrokerInterface" |
|
|
|
step "Checking that order is submitted to BrokerInterface" |
|
|
|
s <- readIORef broState |
|
|
|
s <- readIORef broState |
|
|
|
@ -106,3 +122,59 @@ testBrokerServerSubmitOrder = testCaseSteps "Broker Server submits order" $ \ste |
|
|
|
|
|
|
|
|
|
|
|
))) |
|
|
|
))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
testBrokerServerSubmitOrderToUnknownAccount = testCaseSteps "Broker Server returns error if account is unknown" $ |
|
|
|
|
|
|
|
\step -> withContext (\ctx -> do |
|
|
|
|
|
|
|
step "Setup" |
|
|
|
|
|
|
|
ep <- makeEndpoint |
|
|
|
|
|
|
|
(mockBroker, broState) <- mkMockBroker ["demo"] |
|
|
|
|
|
|
|
let order = mkOrder { |
|
|
|
|
|
|
|
orderAccountId = "foobar", |
|
|
|
|
|
|
|
orderSecurity = "FOO", |
|
|
|
|
|
|
|
orderPrice = Market, |
|
|
|
|
|
|
|
orderQuantity = 10, |
|
|
|
|
|
|
|
orderOperation = Buy |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS -> |
|
|
|
|
|
|
|
withSocket ctx Req (\sock -> do |
|
|
|
|
|
|
|
connectAndSendOrder step sock order ep |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Reading response" |
|
|
|
|
|
|
|
resp <- decode . BL.fromStrict <$> receive sock |
|
|
|
|
|
|
|
case resp of |
|
|
|
|
|
|
|
Just (ResponseError _) -> return () |
|
|
|
|
|
|
|
Just _ -> assertFailure "Invalid response" |
|
|
|
|
|
|
|
Nothing -> assertFailure "Invalid response" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
testBrokerServerCancelOrder = testCaseSteps "Broker Server: submitted order cancellation" $ |
|
|
|
|
|
|
|
\step -> withContext (\ctx -> do |
|
|
|
|
|
|
|
step "Setup" |
|
|
|
|
|
|
|
ep <- makeEndpoint |
|
|
|
|
|
|
|
(mockBroker, broState) <- mkMockBroker ["demo"] |
|
|
|
|
|
|
|
let order = mkOrder { |
|
|
|
|
|
|
|
orderAccountId = "demo", |
|
|
|
|
|
|
|
orderSecurity = "FOO", |
|
|
|
|
|
|
|
orderPrice = Market, |
|
|
|
|
|
|
|
orderQuantity = 10, |
|
|
|
|
|
|
|
orderOperation = Buy |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bracket (startBrokerServer [mockBroker] ctx ep) stopBrokerServer (\broS -> |
|
|
|
|
|
|
|
withSocket ctx Req (\sock -> do |
|
|
|
|
|
|
|
connectAndSendOrder step sock order ep |
|
|
|
|
|
|
|
(Just (ResponseOrderSubmitted orderId)) <- decode . BL.fromStrict <$> receive sock |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Sending order cancellation request" |
|
|
|
|
|
|
|
send sock [] (BL.toStrict . encode $ RequestCancelOrder 2 orderId) |
|
|
|
|
|
|
|
threadDelay 10000 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Checking that order is cancelled in BrokerInterface" |
|
|
|
|
|
|
|
s <- readIORef broState |
|
|
|
|
|
|
|
(length . cancelledOrders) s @?= 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step "Reading response" |
|
|
|
|
|
|
|
resp <- decode . BL.fromStrict <$> receive sock |
|
|
|
|
|
|
|
case resp of |
|
|
|
|
|
|
|
Just (ResponseOrderCancelled _) -> return () |
|
|
|
|
|
|
|
Nothing -> assertFailure "Invalid response" |
|
|
|
|
|
|
|
))) |
|
|
|
|