2017-01-18 11:17:38 +01:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
|
|
{-# LANGUAGE KindSignatures #-}
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2017-01-12 02:58:29 +01:00
|
|
|
module Servant.Server.Internal.RoutingApplicationSpec (spec) where
|
|
|
|
|
2017-01-17 21:36:28 +01:00
|
|
|
import Prelude ()
|
|
|
|
import Prelude.Compat
|
|
|
|
|
2017-01-12 02:58:29 +01:00
|
|
|
import Control.Exception hiding (Handler)
|
2017-01-18 10:37:18 +01:00
|
|
|
import Control.Monad.Trans.Resource (register)
|
2017-01-12 02:58:29 +01:00
|
|
|
import Control.Monad.IO.Class
|
2017-01-17 21:36:28 +01:00
|
|
|
import Data.IORef
|
2017-01-18 11:17:38 +01:00
|
|
|
import Data.Maybe (isJust)
|
|
|
|
import Data.Proxy
|
|
|
|
import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)
|
|
|
|
import Servant
|
2017-01-12 02:58:29 +01:00
|
|
|
import Servant.Server.Internal.RoutingApplication
|
|
|
|
import Test.Hspec
|
2017-01-18 11:17:38 +01:00
|
|
|
import Test.Hspec.Wai (request, shouldRespondWith, with)
|
|
|
|
|
|
|
|
import qualified Data.Text as T
|
2017-01-12 02:58:29 +01:00
|
|
|
|
2017-01-17 21:36:28 +01:00
|
|
|
import System.IO.Unsafe (unsafePerformIO)
|
|
|
|
|
|
|
|
-- Let's not write to the filesystem
|
|
|
|
delayedTestRef :: IORef (Maybe String)
|
|
|
|
delayedTestRef = unsafePerformIO $ newIORef Nothing
|
|
|
|
|
2017-01-12 02:58:29 +01:00
|
|
|
delayed :: DelayedIO () -> RouteResult (Handler ()) -> Delayed () (Handler ())
|
|
|
|
delayed body srv = Delayed
|
2017-01-18 10:37:18 +01:00
|
|
|
{ capturesD = \_ -> return ()
|
|
|
|
, methodD = return ()
|
|
|
|
, authD = return ()
|
|
|
|
, bodyD = do
|
2017-01-17 21:36:28 +01:00
|
|
|
liftIO (writeIORef delayedTestRef (Just "hia") >> putStrLn "garbage created")
|
2017-01-18 10:37:18 +01:00
|
|
|
_ <- register (writeIORef delayedTestRef Nothing >> putStrLn "garbage collected")
|
2017-01-12 02:58:29 +01:00
|
|
|
body
|
2017-01-18 10:37:18 +01:00
|
|
|
, serverD = \() () _body _req -> srv
|
2017-01-12 02:58:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
simpleRun :: Delayed () (Handler ())
|
|
|
|
-> IO ()
|
|
|
|
simpleRun d = fmap (either ignoreE id) . try $
|
|
|
|
runAction d () undefined (\_ -> return ()) (\_ -> FailFatal err500)
|
|
|
|
|
|
|
|
where ignoreE :: SomeException -> ()
|
|
|
|
ignoreE = const ()
|
|
|
|
|
2017-01-18 11:17:38 +01:00
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Combinator example
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- | This data types writes 'sym' to 'delayedTestRef'.
|
|
|
|
data Res (sym :: Symbol)
|
|
|
|
|
|
|
|
instance (KnownSymbol sym, HasServer api ctx) => HasServer (Res sym :> api) ctx where
|
|
|
|
type ServerT (Res sym :> api) m = IORef (Maybe String) -> ServerT api m
|
|
|
|
route Proxy ctx server = route (Proxy :: Proxy api) ctx $
|
|
|
|
server `addBodyCheck` check
|
|
|
|
where
|
|
|
|
sym = symbolVal (Proxy :: Proxy sym)
|
|
|
|
check = do
|
|
|
|
liftIO $ writeIORef delayedTestRef (Just sym)
|
|
|
|
_ <- register (writeIORef delayedTestRef Nothing)
|
|
|
|
return delayedTestRef
|
|
|
|
|
|
|
|
type ResApi = "foobar" :> Res "foobar" :> Get '[PlainText] T.Text
|
|
|
|
|
|
|
|
resApi :: Proxy ResApi
|
|
|
|
resApi = Proxy
|
|
|
|
|
|
|
|
resServer :: Server ResApi
|
|
|
|
resServer ref = liftIO $ fmap (maybe "<empty>" T.pack) $ readIORef ref
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Spec
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
2017-01-12 02:58:29 +01:00
|
|
|
spec :: Spec
|
|
|
|
spec = do
|
|
|
|
describe "Delayed" $ do
|
|
|
|
it "actually runs clean up actions" $ do
|
|
|
|
_ <- simpleRun $ delayed (return ()) (Route $ return ())
|
2017-01-17 21:36:28 +01:00
|
|
|
cleanUpDone <- isJust <$> readIORef delayedTestRef
|
|
|
|
cleanUpDone `shouldBe` False
|
2017-01-12 02:58:29 +01:00
|
|
|
it "even with exceptions in serverD" $ do
|
|
|
|
_ <- simpleRun $ delayed (return ()) (Route $ throw DivideByZero)
|
2017-01-17 21:36:28 +01:00
|
|
|
cleanUpDone <- isJust <$> readIORef delayedTestRef
|
|
|
|
cleanUpDone `shouldBe` False
|
2017-01-12 02:58:29 +01:00
|
|
|
it "even with routing failure in bodyD" $ do
|
|
|
|
_ <- simpleRun $ delayed (delayedFailFatal err500) (Route $ return ())
|
2017-01-17 21:36:28 +01:00
|
|
|
cleanUpDone <- isJust <$> readIORef delayedTestRef
|
|
|
|
cleanUpDone `shouldBe` False
|
2017-01-12 02:58:29 +01:00
|
|
|
it "even with exceptions in bodyD" $ do
|
|
|
|
_ <- simpleRun $ delayed (liftIO $ throwIO DivideByZero) (Route $ return ())
|
2017-01-17 21:36:28 +01:00
|
|
|
cleanUpDone <- isJust <$> readIORef delayedTestRef
|
|
|
|
cleanUpDone `shouldBe` False
|
2017-01-18 11:17:38 +01:00
|
|
|
describe "ResApi" $
|
|
|
|
with (return $ serve resApi resServer) $ do
|
|
|
|
it "writes and cleanups resources" $ do
|
|
|
|
request "GET" "foobar" [] "" `shouldRespondWith` "foobar"
|
|
|
|
liftIO $ do
|
|
|
|
cleanUpDone <- isJust <$> readIORef delayedTestRef
|
|
|
|
cleanUpDone `shouldBe` False
|