{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Web.SLSSpec(main, spec) where import Control.Concurrent (forkIO, killThread) import Control.Exception import Control.Exception import Control.Monad import Control.Monad.IO.Class import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack) import Data.CaseInsensitive (CI) import qualified Data.CaseInsensitive as CI import Data.List (isPrefixOf) import Data.Maybe import Data.Monoid import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy as T import Network.HTTP import Network.HTTP.Types.Header import Network.Wai.Test import System.Directory import System.Exit (exitFailure) import Test.Hspec import Test.Hspec.Wai as W import Test.Hspec.Wai.Internal import Web.Scotty as S import Web.Scotty.Cookie as C import Web.Scotty.Login.Session testPort = 4040 baseURL = "localhost" ++ ":" ++ show testPort main = do catch (void $ removeFile dbName) (\(e :: SomeException) -> return ()) hspec spec catch (void $ removeFile dbName) (\(e :: SomeException) -> return ()) spec :: Spec spec = do describe "baseline" $ do withApp routes $ do it "GET to root returns 200 code" $ do W.get "/" `shouldRespondWith` 200 describe "denial" $ do withApp routes $ do it "denies if not authed" $ do W.get "/authcheck" `shouldRespondWith` "denied" {matchStatus = 403} describe "login" $ do withApp routes $ do it "logs in successfully" $ W.postHtmlForm "/login" [("username", "guest"), ("password", "password")] `shouldRespondWith` "authed" describe "auth usage" $ do withApp routes $ do it "gives sessionID cookie when i log in" $ do resp <- W.postHtmlForm "/login" [("username", "guest"), ("password", "password")] let hs = simpleHeaders resp c = lookup "Set-Cookie" hs -- honestly i am shocked that this works liftIO $ c `shouldSatisfy` \case Nothing -> False Just s -> "SessionId=" `isPrefixOf` unpack s it "allows access to page" $ do resp <- W.postHtmlForm "/login" [("username", "guest"), ("password", "password")] let hs = simpleHeaders resp c = fromMaybe "" $ lookup "Set-Cookie" hs headers = [ ("Cookie", c) ] W.request "GET" "/authcheck" headers "" `shouldRespondWith` "authorized" it "forbids access to page" $ do resp <- W.postHtmlForm "/login" [("username", "guest"), ("password", "password")] W.get "/authcheck" `shouldRespondWith` "denied" {matchStatus = 403} describe "logout" $ do withApp routes $ do it "logs out correctly" $ do resp <- W.postHtmlForm "/login" [("username", "guest"), ("password", "password")] let hs = simpleHeaders resp c = fromMaybe "" $ lookup "Set-Cookie" hs headers = [ ("Cookie", c) ] W.request "GET" "/authcheck" headers "" `shouldRespondWith` "authorized" W.request "GET" "/logout" headers "" W.request "GET" "/authcheck" headers "" `shouldRespondWith` "denied" {matchStatus = 403} -- withApp :: ScottyM () -> SpecWith Application -> Spec -- like before_each withApp r = with (initializeCookieDb conf >> scottyApp r) ----- basic library usage dbName = "__test__.sqlite3" conf :: SessionConfig conf = defaultSessionConfig { dbPath = dbName } routes :: ScottyM () routes = do S.get "/" $ S.text "howdy" S.get "/login" $ do S.html $ T.pack $ unlines $ [ "
" ] S.post "/login" $ do (usn :: String) <- param "username" (pass :: String) <- param "password" if usn == "guest" && pass == "password" then do addSession conf S.html "authed" else do S.html "denied" S.get "/authcheck" $ authCheck (S.html "denied") $ S.html "authorized" S.get "/logout" $ removeSession conf