@@ -1,9 +1,9 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2import Network.Wai.Application.Static
2import Network.Wai.Application.Static
4−import Test.Hspec
4+import Test.Hspec.Monadic
5+import Test.Hspec.QuickCheck
5import Test.Hspec.HUnit ()
6import Test.Hspec.HUnit ()
6−import Test.Hspec.QuickCheck (prop)
7import Test.HUnit ((@?=))
7import Test.HUnit ((@?=))
8import Distribution.Simple.Utils (isInfixOf)
8import Distribution.Simple.Utils (isInfixOf)
9import qualified Data.ByteString.Char8 as S8
9import qualified Data.ByteString.Char8 as S8
@@ -17,9 +17,6 @@ import Network.Wai.Test
17import Network.Socket.Internal as Sock
17import Network.Socket.Internal as Sock
18import qualified Network.HTTP.Types as H
18import qualified Network.HTTP.Types as H
23defRequest :: Request
20defRequest :: Request
24defRequest = Request {
21defRequest = Request {
25 rawQueryString = ""
22 rawQueryString = ""
@@ -40,38 +37,36 @@ setRawPathInfo r rawPinfo =
40 let pInfo = T.split (== '/') $ TE.decodeUtf8 rawPinfo
37 let pInfo = T.split (== '/') $ TE.decodeUtf8 rawPinfo
41 in r { rawPathInfo = rawPinfo, pathInfo = pInfo }
38 in r { rawPathInfo = rawPinfo, pathInfo = pInfo }
43−specs :: IO [Spec]
40+main :: IO a
44−specs = runSpecM $ do
41+main = hspecX $ do
46− context "Pieces: pathFromPieces" $ do
42+ describe "Pieces: pathFromPieces" $ do
47− ti "converts to a file path" $
43+ it "converts to a file path" $
48 (pathFromPieces "prefix" ["a", "bc"]) @?= "prefix/a/bc"
44 (pathFromPieces "prefix" ["a", "bc"]) @?= "prefix/a/bc"
50 prop "each piece is in file path" $ \pieces ->
46 prop "each piece is in file path" $ \pieces ->
51 let piecesT = (map T.pack pieces) in
47 let piecesT = (map T.pack pieces) in
52 all (\p -> ("/" ++ (unfixPathName $ T.unpack p)) `isInfixOf` (pathFromPieces "root" $ piecesT)) piecesT
48 all (\p -> ("/" ++ (unfixPathName $ T.unpack p)) `isInfixOf` (pathFromPieces "root" $ piecesT)) piecesT
57 let cacheLookup = Forever (\fp h -> fp == "tests/runtests.hs" && h == "cached")
52 let cacheLookup = Forever (\fp h -> fp == "tests/runtests.hs" && h == "cached")
58 let statApp = flip runSession $ staticApp $ (defaultStaticSettings cacheLookup) {ssFolder = "tests"}
53 let statApp = flip runSession $ staticApp $ (defaultStaticSettings cacheLookup) {ssFolder = "tests"}
59− context "staticApp" $ do
54+ describe "staticApp" $ do
60− ti "403 for unsafe paths" $ statApp $
55+ it "403 for unsafe paths" $ statApp $
61 flip mapM_ ["..", "."] $ \path ->
56 flip mapM_ ["..", "."] $ \path ->
62 assertStatus 403 =<<
57 assertStatus 403 =<<
63 request (setRawPathInfo defRequest path)
58 request (setRawPathInfo defRequest path)
65− ti "200 for hidden paths" $ statApp $
60+ it "200 for hidden paths" $ statApp $
66 flip mapM_ [".hidden/folder.png", ".hidden/haskell.png"] $ \path ->
61 flip mapM_ [".hidden/folder.png", ".hidden/haskell.png"] $ \path ->
67 assertStatus 200 =<<
62 assertStatus 200 =<<
68 request (setRawPathInfo defRequest path)
63 request (setRawPathInfo defRequest path)
70− ti "404 for non-existant files" $ statApp $
65+ it "404 for non-existant files" $ statApp $
71 assertStatus 404 =<<
66 assertStatus 404 =<<
72 request (setRawPathInfo defRequest "doesNotExist")
67 request (setRawPathInfo defRequest "doesNotExist")
74− ti "301 redirect when multiple slashes" $ statApp $ do
69+ it "301 redirect when multiple slashes" $ statApp $ do
75 req <- request (setRawPathInfo defRequest "a//b/c")
70 req <- request (setRawPathInfo defRequest "a//b/c")
76 assertStatus 301 req
71 assertStatus 301 req
77 assertHeader "Location" "../../a/b/c" req
72 assertHeader "Location" "../../a/b/c" req
@@ -79,34 +74,34 @@ specs = runSpecM $ do
79 let absoluteApp = flip runSession $ staticApp $ (defaultStaticSettings cacheLookup) {
74 let absoluteApp = flip runSession $ staticApp $ (defaultStaticSettings cacheLookup) {
80 ssFolder = "tests", ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
75 ssFolder = "tests", ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
82− ti "301 redirect when multiple slashes" $ absoluteApp $
77+ it "301 redirect when multiple slashes" $ absoluteApp $
83 flip mapM_ ["/a//b/c", "a//b/c"] $ \path -> do
78 flip mapM_ ["/a//b/c", "a//b/c"] $ \path -> do
84 req <- request (setRawPathInfo defRequest path)
79 req <- request (setRawPathInfo defRequest path)
85 assertStatus 301 req
80 assertStatus 301 req
86 assertHeader "Location" "http://www.example.com/a/b/c" req
81 assertHeader "Location" "http://www.example.com/a/b/c" req
88− context "when requesting a static asset" $ do
83+ describe "when requesting a static asset" $ do
89 let runtests = setRawPathInfo defRequest "runtests.hs"
84 let runtests = setRawPathInfo defRequest "runtests.hs"
90− ti "200 when no query parameters" $ statApp $ do
85+ it "200 when no query parameters" $ statApp $ do
91 req <- request runtests {
86 req <- request runtests {
92 requestHeaders = [("If-Modified-Since", "")]
87 requestHeaders = [("If-Modified-Since", "")]
94 assertStatus 200 req
89 assertStatus 200 req
95 assertNoHeader "Cache-Control" req
90 assertNoHeader "Cache-Control" req
97− ti "200 when no cache headers sent" $ statApp $ do
92+ it "200 when no cache headers sent" $ statApp $ do
98 req <- request runtests { rawQueryString ="?cached" }
93 req <- request runtests { rawQueryString ="?cached" }
99 assertStatus 200 req
94 assertStatus 200 req
100 assertHeader "Cache-Control" "max-age=31536000" req
95 assertHeader "Cache-Control" "max-age=31536000" req
102− ti "200 when If-Unmodified-Since" $ statApp $ do
97+ it "200 when If-Unmodified-Since" $ statApp $ do
103 req <- request runtests { rawQueryString ="?cached",
98 req <- request runtests { rawQueryString ="?cached",
104 requestHeaders = [("If-Unmodified-Since", "")]
99 requestHeaders = [("If-Unmodified-Since", "")]
106 assertStatus 200 req
101 assertStatus 200 req
107 assertHeader "Cache-Control" "max-age=31536000" req
102 assertHeader "Cache-Control" "max-age=31536000" req
109− ti "304 when cache header and query parameter sent" $ statApp $ do
104+ it "304 when cache header and query parameter sent" $ statApp $ do
110 req <- request runtests { rawQueryString ="?cached",
105 req <- request runtests { rawQueryString ="?cached",
111 requestHeaders = [("If-Modified-Since", "")]
106 requestHeaders = [("If-Modified-Since", "")]
@@ -114,15 +109,13 @@ specs = runSpecM $ do
114 assertNoHeader "Cache-Control" req
109 assertNoHeader "Cache-Control" req
116 let pubApp = flip runSession $ staticApp (defaultPublicSettings $ ETag (\_ -> return $ Just "hash")) {ssFolder = "tests"}
111 let pubApp = flip runSession $ staticApp (defaultPublicSettings $ ETag (\_ -> return $ Just "hash")) {ssFolder = "tests"}
117− context "staticApp when requesting a public asset - etags" $ do
112+ describe "staticApp when requesting a public asset - etags" $ do
118− ti "200 when no etag parameters" $ pubApp $ do
113+ it "200 when no etag parameters" $ pubApp $ do
119 req <- request (setRawPathInfo defRequest "runtests.hs")
114 req <- request (setRawPathInfo defRequest "runtests.hs")
120 assertStatus 200 req
115 assertStatus 200 req
121 assertHeader "Etag" "hash" req
116 assertHeader "Etag" "hash" req
123− ti "304 when etag parameter sent" $ pubApp $ do
118+ it "304 when etag parameter sent" $ pubApp $ do
124 req <- request (setRawPathInfo defRequest "runtests.hs") { requestHeaders = [("If-None-Match", "hash")] }
119 req <- request (setRawPathInfo defRequest "runtests.hs") { requestHeaders = [("If-None-Match", "hash")] }
125 assertStatus 304 req
120 assertStatus 304 req
126 assertNoHeader "Etag" req
121 assertNoHeader "Etag" req