Spock-0.14.0.0: test/Web/Spock/SafeSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE CPP #-}
module Web.Spock.SafeSpec (spec) where
import Web.Spock.TestUtils
import Web.Spock
import Web.Spock.Config
import Control.Monad
import Data.IORef
#if MIN_VERSION_base(4,11,0)
#else
import Data.Monoid
#endif
import Test.Hspec
import qualified Data.ByteString.Lazy.Char8 as BSLC
import qualified Data.HashSet as HS
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Network.Wai.Test as Wai
import qualified Test.Hspec.Wai as Test
import qualified Test.Hspec.Wai.Internal as Test
sessionSpec :: Spec
sessionSpec =
describe "SessionManager" $
do Test.with sessionApp $
it "should generate random session ids" $
do ids <- Test.liftIO $ newIORef HS.empty
-- of course this sample is too small to prove anything, but it's a good
-- smoke test...
replicateM_ 500 $
do Test.WaiSession (Wai.deleteClientCookie "spockcookie")
res <- Test.get "/test"
Test.liftIO $ checkCookie ids res `shouldReturn` True
Test.with sessionApp $
it "should remember a session" $
do res <- Test.get "/set/5"
case getSessCookie res of
Nothing ->
Test.liftIO $ expectationFailure "Missing spockcookie"
Just sessCookie ->
Test.request "GET" "/check" [("Cookie", T.encodeUtf8 $ "spockcookie=" <> sessCookie)] ""
`Test.shouldRespondWith` "5"
Test.with sessionApp $
it "should update internal session id correctly" $
do bdy <- fmap Wai.simpleBody (Test.get "/regenerate-different-sids")
case BSLC.split '|' bdy of
[a, b] | a /= b -> pure ()
xs -> Test.liftIO $ expectationFailure ("Bad result: " ++ show xs)
Test.with sessionApp $
it "should regenerate and preserve all content" $
do res <- Test.get "/set/5"
case getSessCookie res of
Nothing ->
Test.liftIO $ expectationFailure "Missing spockcookie"
Just sessCookie ->
do res2 <- Test.request "GET" "/regenerate" [("Cookie", T.encodeUtf8 $ "spockcookie=" <> sessCookie)] ""
case getSessCookie res2 of
Nothing ->
Test.liftIO $ expectationFailure "Missing new spockcookie"
Just sessCookie2 ->
do Test.request "GET" "/check" [("Cookie", T.encodeUtf8 $ "spockcookie=" <> sessCookie2)] ""
`Test.shouldRespondWith` "5"
Test.request "GET" "/check" [("Cookie", T.encodeUtf8 $ "spockcookie=" <> sessCookie)] ""
`Test.shouldRespondWith` "0"
where
sessionApp =
spockAsApp $
spockCfg >>= \cfg ->
spock cfg $
do get "test" $ getSessionId >>= text
get ("set" <//> var) $ \number -> writeSession number >> text "done"
get "regenerate" $ sessionRegenerateId >> text "done"
get "regenerate-different-sids" $
do s1 <- getSessionId
sessionRegenerateId
s2 <- getSessionId
text $ s1 <> "|" <> s2
get "check" $
do val <- readSession
text (T.pack $ show val)
spockCfg =
defaultSpockCfg (0 :: Int) PCNoDatabase True
checkCookie :: IORef (HS.HashSet T.Text) -> Wai.SResponse -> IO Bool
checkCookie setRef resp =
do let mSessCookie = getSessCookie resp
case mSessCookie of
Nothing ->
do expectationFailure "Missing spockcookie"
return False
Just sessionId ->
do set <- readIORef setRef
if HS.member sessionId set
then fail $ "Reused " ++ show sessionId ++ " (" ++ show set ++ ")"
else do writeIORef setRef $ HS.insert sessionId set
return True
spec :: Spec
spec =
describe "SafeRouting" $ sessionSpec