Spock-core-0.12.0.0: test/Web/Spock/SafeSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE DeriveGeneric #-}
module Web.Spock.SafeSpec (spec) where
import Web.Spock.Core
import Web.Spock.FrameworkSpecHelper
import Control.Exception.Base
import Control.Monad
import Data.Aeson
import Data.Monoid
import GHC.Generics
import Network.HTTP.Types.Status
import Test.Hspec
import qualified Data.ByteString.Lazy.Char8 as BSLC
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Network.Wai as Wai
import qualified Test.Hspec.Wai as Test
data SampleJson
= SampleJson
{ sampleJson :: T.Text
} deriving (Show, Eq, Generic)
instance ToJSON SampleJson
instance FromJSON SampleJson
app :: SpockT IO ()
app =
do get root $ text "root"
get "verb-test" $ text "GET"
post "verb-test" $ text "POST"
getpost "verb-test-gp" $ text "GETPOST"
put "verb-test" $ text "PUT"
delete "verb-test" $ text "DELETE"
patch "verb-test" $ text "PATCH"
get "test-slash" $ text "ok"
get "/test-noslash" $ text "ok"
get ("param-test" <//> var) $ \(i :: Int) ->
text $ "int" <> T.pack (show i)
get ("param-test" <//> "static") $
text "static"
get ("cookie" <//> "single") $
do setCookie "single" "test" defaultCookieSettings { cs_EOL = CookieValidFor 3600 }
text "set"
get ("cookie" <//> "multiple") $
do setCookie "multiple1" "test1" defaultCookieSettings { cs_EOL = CookieValidFor 3600 }
setCookie "multiple2" "test2" defaultCookieSettings { cs_EOL = CookieValidFor 3600 }
text "set"
get "set-header" $
do setHeader "X-FooBar" "Baz"
text "ok"
get "set-multi-header" $
do setHeader "Content-Language" "de"
setHeader "Content-Language" "en"
text "ok"
get "get-params" $
do gp <- paramsGet
text (T.pack $ show gp)
post "post-params" $
do gp <- paramsPost
text (T.pack $ show gp)
post "json" $
do jbody <- jsonBody'
text (sampleJson jbody)
post "raw-body" $
do b <- body
text (T.decodeUtf8 b)
let subcomp x =
"subcomponent" <//> x
subsubcomp x =
subcomp ("subcomponent2" <//> x)
get (subcomp "foo") $ text "foo"
get (subsubcomp "bar") $ text "bar"
get "preferred-format" $
do fmt <- preferredFormat
case fmt of
PrefHTML -> text "html"
x -> text (T.pack (show x))
get ("auth" <//> var <//> var) $ \user pass ->
let checker user' pass' =
unless (user == user' && pass == pass') $
do setStatus status401
text "err"
in requireBasicAuth "Foo" checker $ \() -> text "ok"
hookRouteCustom "NOTIFY" ("notify" <//> var) $ \notification -> text notification
hookAny GET $ text . T.intercalate "/"
hookAnyCustom "MYVERB" $ text . T.intercalate "/"
get ("wai" <//> wildcard) $ \_ ->
respondApp dummyWai
dummyWai :: Wai.Application
dummyWai req respond =
respond $ Wai.responseLBS status200 [] (BSLC.pack $ show $ Wai.pathInfo req)
routeRenderingSpec :: Spec
routeRenderingSpec =
describe "Route Rendering" $
do it "should work with argument-less routes" $
do renderRoute "foo" `shouldBe` "/foo"
renderRoute "/foo" `shouldBe` "/foo"
renderRoute "/foo/" `shouldBe` "/foo"
renderRoute ("foo" <//> "bar") `shouldBe` "/foo/bar"
it "should work with routes with args" $
do let r1 = var :: Var Int
renderRoute r1 1 `shouldBe` "/1"
let r2 = "blog" <//> (var :: Var Int)
renderRoute r2 2 `shouldBe` "/blog/2"
let r3 = "blog" <//> (var :: Var Int) <//> (var :: Var T.Text)
renderRoute r3 2 "BIIM" `shouldBe` "/blog/2/BIIM"
ctxApp :: SpockT IO ()
ctxApp =
prehook hook $
do get "test" $ getContext >>= text
post "test" $ getContext >>= text
where
hook =
do sid <- header "X-ApiKey"
case sid of
Just s -> return s
Nothing -> text "Missing ApiKey"
ctxSpec :: Spec
ctxSpec =
describe "Contexts" $
Test.with (spockAsApp $ spockT id ctxApp) $
it "should work" $
do Test.request "GET" "/test" [] "" `Test.shouldRespondWith` "Missing ApiKey"
Test.request "GET" "/test" [("X-ApiKey", "foo")] "" `Test.shouldRespondWith` "foo"
Test.request "POST" "/test" [("X-ApiKey", "foo")] "" `Test.shouldRespondWith` "foo"
spec :: Spec
spec =
describe "SafeRouting" $
do frameworkSpec (spockAsApp $ spockT id app)
ctxSpec
routeRenderingSpec
sizeLimitSpec $ \lim -> spockAsApp $ spockConfigT (defaultSpockConfig { sc_maxRequestSize = Just lim }) id $
post "size" $ body >>= bytes
errorHandlerSpec $ spockAsApp $ spockConfigT specConfig id $ do
get ("failing" <//> "route") $
throw Overflow
get ("user" <//> "error") $
do setStatus status403
text "UNAUTHORIZED"
where
specConfig = defaultSpockConfig { sc_errorHandler = errorHandler }
errorHandler status = case statusCode status of
500 -> text "SERVER ERROR"
404 -> text "NOT FOUND"
_ -> text "OTHER ERROR"