servant/servant-server/test/Servant/Server/Internal/EnterSpec.hs

123 lines
4.2 KiB
Haskell
Raw Normal View History

2015-05-02 16:46:43 +02:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module Servant.Server.Internal.EnterSpec where
import qualified Control.Category as C
2015-05-02 16:46:43 +02:00
import Control.Monad.Reader
2015-09-12 14:11:24 +02:00
import Control.Monad.Trans.Except
2015-07-30 18:44:13 +02:00
import Control.Monad.Writer
import Data.IORef
2015-05-02 16:46:43 +02:00
import Data.Proxy
import Servant.API
import Servant.Server
2015-07-30 18:44:13 +02:00
import Control.Exception (bracket)
import Network.Wai (Application)
import Network.HTTP.Types (methodPost)
import Servant.Utils.StaticFiles (serveDirectory)
import System.Directory (createDirectory,
getCurrentDirectory,
setCurrentDirectory)
import System.IO.Temp (withSystemTempDirectory)
import System.IO.Unsafe (unsafePerformIO)
import Test.Hspec (Spec, around_, context, describe,
it, shouldReturn)
import Test.Hspec.Wai (get, matchStatus, post, request,
shouldRespondWith, with)
2015-05-02 16:46:43 +02:00
spec :: Spec
spec = describe "module Servant.Server.Enter" $ do
enterSpec
type ReaderAPI = "int" :> Get '[JSON] Int
:<|> "string" :> Post '[JSON] String
2015-07-30 18:44:13 +02:00
:<|> "static" :> Raw (Reader String) Application
2015-05-02 16:46:43 +02:00
type IdentityAPI = "bool" :> Get '[JSON] Bool
2015-07-30 18:44:13 +02:00
type WriterAPI = "fn" :> ReqBody '[JSON] Int :> Post '[JSON] Int
2015-05-02 16:46:43 +02:00
type CombinedAPI = ReaderAPI :<|> IdentityAPI
2015-07-30 18:44:13 +02:00
type CombinedAPI2 = CombinedAPI :<|> WriterAPI
2015-05-02 16:46:43 +02:00
readerAPI :: Proxy ReaderAPI
readerAPI = Proxy
combinedAPI :: Proxy CombinedAPI
combinedAPI = Proxy
2015-07-30 18:44:13 +02:00
combinedAPI2 :: Proxy CombinedAPI2
combinedAPI2 = Proxy
2015-05-02 16:46:43 +02:00
readerServer' :: ServerT ReaderAPI (Reader String)
2015-07-30 18:44:13 +02:00
readerServer' = return 1797
:<|> ask
:<|> serveDirectory "static"
writerServer :: ServerT WriterAPI (WriterT String IO)
writerServer x = tell "hi" >> return x
2015-05-02 16:46:43 +02:00
2015-09-12 14:11:24 +02:00
fReader :: Reader String :~> ExceptT ServantErr IO
2015-07-30 18:44:13 +02:00
fReader = generalizeNat C.. runReaderTNat "hi"
2015-05-02 16:46:43 +02:00
readerServer :: Server ReaderAPI
readerServer = enter fReader readerServer'
combinedReaderServer' :: ServerT CombinedAPI (Reader String)
combinedReaderServer' = readerServer' :<|> enter generalizeNat (return True)
combinedReaderServer :: Server CombinedAPI
combinedReaderServer = enter fReader combinedReaderServer'
2015-07-30 18:44:13 +02:00
combinedServer2 :: IORef String -> Server CombinedAPI2
combinedServer2 ref'
= enter fReader combinedReaderServer'
:<|> enter (liftNat C.. logWriterTLNat (writeIORef ref')) writerServer
withStaticFiles :: IO () -> IO ()
withStaticFiles action = withSystemTempDirectory "servant-test" $ \ tmpDir ->
bracket (setup tmpDir) teardown (const action)
where
setup tmpDir = do
outer <- getCurrentDirectory
setCurrentDirectory tmpDir
createDirectory "static"
writeFile "static/foo.txt" "bar"
writeFile "static/index.html" "index"
return outer
teardown outer = do
setCurrentDirectory outer
2015-05-02 16:46:43 +02:00
enterSpec :: Spec
enterSpec = describe "Enter" $ do
2015-07-30 18:44:13 +02:00
around_ withStaticFiles $ with (return (serve readerAPI EmptyConfig readerServer)) $ do
2015-05-02 16:46:43 +02:00
it "allows running arbitrary monads" $ do
get "int" `shouldRespondWith` "1797"
post "string" "3" `shouldRespondWith` "\"hi\""{ matchStatus = 200 }
2015-05-02 16:46:43 +02:00
2016-01-14 23:43:48 +01:00
with (return (serve combinedAPI EmptyConfig combinedReaderServer)) $ do
2015-07-30 18:44:13 +02:00
it "allows combination of enters" $ do
2015-05-02 16:46:43 +02:00
get "bool" `shouldRespondWith` "true"
2015-07-30 18:44:13 +02:00
with (newIORef "h" >>= \r -> return (serve combinedAPI2 EmptyConfig $ combinedServer2 r)) $ do
it "allows nested combination of enters" $ do
get "bool" `shouldRespondWith` "true"
request methodPost "fn" [("Content-Type", "application/json")] "3"
`shouldRespondWith` "3"{ matchStatus = 200 }
context "logWriter" $ do
with (return (serve combinedAPI2 EmptyConfig $ combinedServer2 ref)) $ do
it "runs the function provided with the logs as argument" $ do
void $ request methodPost "fn" [("Content-Type", "application/json")] "3"
liftIO $ readIORef ref `shouldReturn` "hi"
{-# NOINLINE ref #-}
ref :: IORef String
ref = unsafePerformIO $ newIORef ""