mig-0.2.1.0: test/Test/Server/Counter.hs
-- | Test case for ReaderT based server
module Test.Server.Counter (spec) where
import Control.Monad.Reader
import Data.IORef
import Data.Maybe
import Data.Text qualified as Text
import Mig.Core
import Mig.Core qualified as Request (Request (..))
import Network.HTTP.Types.Method (methodPost)
import Test.Hspec
import Test.Server.Common
-------------------------------------------------------------------------------------
-- server definition
newtype Env = Env (IORef Int)
initEnv :: IO Env
initEnv = Env <$> newIORef 0
newtype App a = App (ReaderT Env IO a)
deriving newtype (Functor, Applicative, Monad, MonadReader Env, MonadIO)
runApp :: Env -> App a -> IO a
runApp env (App act) = runReaderT act env
{-| Server has two routes:
* get - to querry current state
* put - to add some integer to the state
-}
server :: Server App
server =
"counter"
/. [ "get" /. handleGet
, "put" /. handlePut
]
-- | Get handler. It logs the call and returns current state
handleGet :: Get App (Resp Json Int)
handleGet = Send $ do
Env ref <- ask
liftIO $ ok <$> readIORef ref
-- | Put handler. It logs the call and updates the state with integer which is read from URL
handlePut :: Capture "arg" Int -> Post App (Resp Json ())
handlePut (Capture val) = Send $ do
Env ref <- ask
liftIO $ ok <$> atomicModifyIORef' ref (\cur -> (cur + val, ()))
-------------------------------------------------------------------------------------
-- test cases
spec :: Spec
spec = describe "counter server (ReaderT)" $ do
describe "plain route finder" $ specBy plainApiStrategy
describe "tree route finder" $ specBy treeApiStrategy
specBy :: FindRoute normalForm App -> Spec
specBy findRoute =
it "run accumulator script" $
script serverFun [1, 2, 3, 4] `shouldReturn` [1, 3, 6, 10]
where
serverFun = fromServer findRoute server
{-| Puts inputs to server and returns result of "counter/get" method call
on each increment
-}
script :: ServerFun App -> [Int] -> IO [Int]
script f inputs = do
env <- initEnv
runApp env $ catMaybes <$> mapM go inputs
where
go :: Int -> App (Maybe Int)
go n = fmap (parseResp =<<) $ do
mRes <- f (putReq n)
if (isJust mRes)
then f getReq
else pure Nothing
putReq :: Int -> Request
putReq increment =
emptyReq
{ method = methodPost
, Request.path = ["counter", "put", Text.pack (show increment)]
}
getReq :: Request
getReq = emptyReq{Request.path = ["counter", "get"]}