packages feed

mig-0.2.0.1: 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 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
        , path = ["counter", "put", Text.pack (show increment)]
        }

    getReq :: Request
    getReq = emptyReq{path = ["counter", "get"]}