freckle-app-1.0.2.1: tests/Freckle/App/WaiSpec.hs
module Freckle.App.WaiSpec
( spec
) where
import Prelude
import Data.ByteString (ByteString)
import Data.Function (on)
import Data.List (deleteBy)
import Freckle.App.Wai
(corsMiddleware, denyFrameEmbeddingMiddleware, noCacheMiddleware)
import Network.HTTP.Types.Method (Method)
import Network.HTTP.Types.Status (status200)
import Network.Wai
import Network.Wai.Test
import Test.Hspec
spec :: Spec
spec = do
describe "noCacheMiddleware" $ do
let
runTestSession :: Session a -> IO a
runTestSession f = runSession f $ noCacheMiddleware app
it "adds an appropriate Cache-Control header" $ runTestSession $ do
response <- request defaultRequest
assertHeader
"Cache-Control"
"no-cache, no-store, max-age=0, private"
response
assertBody "Test" response
describe "corsMiddleware" $ do
let
runTestSession :: Session a -> IO a
runTestSession = runTestSessionWith (const False) []
runTestSessionWith
:: (ByteString -> Bool) -> [ByteString] -> Session a -> IO a
runTestSessionWith validateOrigin extraExposedHeaders f =
runSession f $ corsMiddleware validateOrigin extraExposedHeaders app
it "adds CORS headers to responses for non-OPTIONS" $ runTestSession $ do
response <- request $ setMethod "GET" $ setOriginHeader
"unimportant"
defaultRequest
assertAccessControlHeaders "BADORIGIN" response
assertBody "Test" response
it "responds itself, with CORS headers, for OPTIONS" $ runTestSession $ do
response <- request $ setMethod "OPTIONS" $ setOriginHeader
"unimportant"
defaultRequest
assertAccessControlHeaders "BADORIGIN" response
assertBody mempty response
it "doesn't operate on requests without Origin" $ runTestSession $ do
response1 <- request defaultRequest
response2 <- request $ setMethod "OPTIONS" defaultRequest
assertBody "Test" response1
assertNoAccessControlHeaders response1
assertBody "Test" response2
assertNoAccessControlHeaders response2
it "accepts only valid Origins" $ runTestSessionWith (== "A") [] $ do
responseA <- request $ setOriginHeader "A" defaultRequest
responseB <- request $ setOriginHeader "B" defaultRequest
assertAccessControlHeaders "A" responseA
assertAccessControlHeaders "BADORIGIN" responseB
assertBody "Test" responseA
assertBody "Test" responseB
it "adds extra Exposed-Headers"
$ runTestSessionWith (const False) ["X-Foo"]
$ do
response <- request $ setOriginHeader "unimportant" defaultRequest
assertHeader
"Access-Control-Expose-Headers"
"Set-Cookie, Content-Disposition, Link, X-Foo"
response
describe "denyFrameEmbeddingMiddleware" $ do
let
runTestSession :: Session a -> IO a
runTestSession f = runSession f $ denyFrameEmbeddingMiddleware app
it "adds an appropriate X-Frame-Options header" $ runTestSession $ do
response <- request defaultRequest
assertHeader "X-Frame-Options" "DENY" response
assertBody "Test" response
app :: Application
app _req respond = respond $ responseLBS status200 [] "Test"
setMethod :: Method -> Request -> Request
setMethod method req = req { requestMethod = method }
setOriginHeader :: ByteString -> Request -> Request
setOriginHeader origin req =
let
header = ("Origin", origin)
others = deleteBy ((==) `on` fst) header $ requestHeaders req
in req { requestHeaders = others <> [header] }
assertAccessControlHeaders :: ByteString -> SResponse -> Session ()
assertAccessControlHeaders origin response = do
assertHeader "Access-Control-Allow-Origin" origin response
assertHeader
"Access-Control-Allow-Methods"
"POST, GET, OPTIONS, PUT, DELETE, PATCH"
response
assertHeader "Access-Control-Allow-Credentials" "true" response
assertHeader "Access-Control-Allow-Headers" "Content-Type, *" response
assertHeader
"Access-Control-Expose-Headers"
"Set-Cookie, Content-Disposition, Link"
response
assertNoAccessControlHeaders :: SResponse -> Session ()
assertNoAccessControlHeaders response = do
assertNoHeader "Access-Control-Allow-Origin" response
assertNoHeader "Access-Control-Allow-Methods" response
assertNoHeader "Access-Control-Allow-Credentials" response
assertNoHeader "Access-Control-Allow-Headers" response
assertNoHeader "Access-Control-Expose-Headers" response