apiary-1.4.3: tests/Application.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DataKinds #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module Application(test) where
import Test.Tasty(TestTree, testGroup)
import Test.Tasty.HUnit(testCase)
import Control.Monad(when)
import Control.Monad.Identity(Identity(..))
import Web.Apiary
import Network.Wai(Request)
import qualified Network.Wai.Test as WT
import qualified Network.Wai as Wai
import qualified Network.HTTP.Types as HTTP
import qualified Data.ByteString.Lazy.Char8 as L
import qualified Data.ByteString.Char8 as S
testReq :: String -> (Request -> IO ()) -> TestTree
testReq str f =
let (meth, other) = break (== ' ') str
(p, version) = break (== ' ') (tail other)
in testCase str $ f (WT.setPath (setVersion version $ (WT.defaultRequest { Wai.requestMethod = S.pack meth })) (S.pack p))
where
setVersion [] r = r
setVersion v r | v == " HTTP/1.0" = r { Wai.httpVersion = HTTP.http10 }
| v == " HTTP/0.9" = r { Wai.httpVersion = HTTP.http09 }
| otherwise = r { Wai.httpVersion = HTTP.http11 }
--------------------------------------------------------------------------------
assertRequest :: Int -> (Maybe S.ByteString) -> L.ByteString -> Application -> Request -> IO ()
assertRequest sc ct body app req = flip WT.runSession app $ do
res <- WT.request req
WT.assertBody body res
WT.assertStatus sc res
maybe (return ()) (flip WT.assertContentType res) ct
assertPlain200 :: L.ByteString -> Application -> Request -> IO ()
assertPlain200 = assertRequest 200 (Just "text/plain")
assertHtml200 :: L.ByteString -> Application -> Request -> IO ()
assertHtml200 = assertRequest 200 (Just "text/html")
assertJson200 :: L.ByteString -> Application -> Request -> IO ()
assertJson200 = assertRequest 200 (Just "application/json")
assert404 :: Application -> Request -> IO ()
assert404 = assertRequest 404 (Just "text/plain") "404 Page Notfound.\n"
--------------------------------------------------------------------------------
runApp :: ApiaryT '[] '[] IO Identity () -> Application
runApp = runIdentity . runApiary return def
--------------------------------------------------------------------------------
helloWorldApp :: Application
helloWorldApp = runApp $ action $ do
contentType "text/plain"
bytes "hello"
helloWorldAllTest :: TestTree
helloWorldAllTest = testGroup "helloWorld" $ map ($ helloWorldApp)
[ testReq "GET /" . assertPlain200 "hello"
, testReq "GET /foo" . assert404
, testReq "POST /" . assertPlain200 "hello"
]
--------------------------------------------------------------------------------
methodFilterApp :: Application
methodFilterApp = runApp $ do
method "GET" . action $ contentType "text/plain" >> bytes "GET"
method POST . action $ contentType "text/plain" >> bytes "POST"
methodFilterTest :: TestTree
methodFilterTest = testGroup "methodFilter" $ map ($methodFilterApp)
[ testReq "GET /" . assertPlain200 "GET"
, testReq "POST /" . assertPlain200 "POST"
, testReq "GET /foo" . assert404
, testReq "DELETE /" . assert404
]
--------------------------------------------------------------------------------
httpVersionApp :: Application
httpVersionApp = runApp $ do
http09 . action $ contentType "text/plain" >> bytes "09"
http10 . action $ contentType "text/plain" >> bytes "10"
http11 . action $ contentType "text/plain" >> bytes "11"
httpVersionTest :: TestTree
httpVersionTest = testGroup "httpVersionFilter" $ map ($ httpVersionApp)
[ testReq "GET / HTTP/0.9" . assertPlain200 "09"
, testReq "GET / HTTP/1.0" . assertPlain200 "10"
, testReq "GET / HTTP/1.1" . assertPlain200 "11"
]
--------------------------------------------------------------------------------
rootFilterApp :: Application
rootFilterApp = runApp . root . action $ do
contentType "text/html"
bytes "root"
rootFilterTest :: TestTree
rootFilterTest = testGroup "rootFilter" $ map ($ rootFilterApp)
[ testReq "GET /" . assertHtml200 "root"
, testReq "POST /" . assertHtml200 "root"
, testReq "GET /neko" . assert404
, testReq "GET /index.html" . assertHtml200 "root"
]
--------------------------------------------------------------------------------
restFilterApp :: Application
restFilterApp = runApp $ do
[capture|/test/**rest|] . action $ contentType "text/plain" >> param [key|rest|] >>= showing
[capture|/test/neko|] . action $ contentType "text/plain" >> bytes "nyan"
restFilterTest :: TestTree
restFilterTest = testGroup "rest capture" $ map ($ restFilterApp)
[ testReq "GET /" . assert404
, testReq "GET /test" . assertPlain200 "[]"
, testReq "GET /test/foo" . assertPlain200 "[\"foo\"]"
, testReq "GET /test/foo/bar" . assertPlain200 "[\"foo\",\"bar\"]"
, testReq "GET /test/neko" . assertPlain200 "nyan"
]
--------------------------------------------------------------------------------
captureApp :: Application
captureApp = runApp $ do
[capture|/foo|] . action $ contentType "text/plain" >> bytes "foo"
[capture|/int::Int|] . method GET . action $ contentType "text/plain" >> bytes "Int " >> param [key|int|] >>= appendShowing
[capture|/d::Double|] . action $ contentType "text/plain" >> bytes "Double " >> param [key|d|] >>= appendShowing
[capture|/bar/s::L.ByteString/i::Int|] . action $ contentType "text/plain" >> param [key|s|] >>= lazyBytes >> appendChar ' ' >> param [key|i|] >>= appendShowing
[capture|/s::L.ByteString|] . action $ contentType "text/plain" >> bytes "fall " >> param [key|s|] >>= appendLazyBytes
captureTest :: TestTree
captureTest = testGroup "capture" $ map ($ captureApp)
[ testReq "GET /foo" . assertPlain200 "foo"
, testReq "GET /12" . assertPlain200 "Int 12"
, testReq "GET /12.4" . assertPlain200 "Double 12.4"
, testReq "POST /12" . assertPlain200 "Double 12.0"
, testReq "GET /bar" . assertPlain200 "fall bar"
, testReq "GET /baz" . assertPlain200 "fall baz"
, testReq "GET /bar/nyan/12" . assertPlain200 "nyan 12"
, testReq "GET /bar/nyan/12/other" . assert404
]
--------------------------------------------------------------------------------
queryApp f g h = runApp $ do
_ <- (f [key|foo|] pInt) . action $ contentType "text/plain" >> bytes "foo Int " >> param [key|foo|] >>= appendShowing
_ <- (g [key|foo|] pString) . action $ contentType "text/plain" >> bytes "foo String " >> param [key|foo|] >>= appendShowing
(h [key|foo|] (pMaybe pString)) . action $ contentType "text/plain" >> bytes "foo Maybe String " >> param [key|foo|] >>= appendShowing
queryOptionalApp :: Application
queryOptionalApp = runApp $ do
([key|foo|] =?!: (5 :: Int)) . action $ contentType "text/plain" >> bytes "foo Int " >> param [key|foo|] >>= appendShowing
([key|foo|] =?!: ("bar" :: String)) . action $ contentType "text/plain" >> bytes "foo String " >> param [key|foo|] >>= appendShowing
([key|foo|] =?!: (Just "baz" :: Maybe String)) . action $ contentType "text/plain" >> bytes "foo Maybe String " >> param [key|foo|] >>= appendShowing
queryFirstTest :: TestTree
queryFirstTest = testGroup "First" $ map ($ queryApp (=:) (=:) (=:))
[ testReq "GET /" . assert404
, testReq "GET /?foo" . assertPlain200 "foo Maybe String Nothing"
, testReq "GET /?foo&foo=3" . assertPlain200 "foo Maybe String Nothing"
, testReq "GET /?foo=12" . assertPlain200 "foo Int 12"
, testReq "GET /?foo=a" . assertPlain200 "foo String \"a\""
, testReq "GET /?foo=12&foo=23" . assertPlain200 "foo Int 12"
, testReq "GET /?foo=12&foo=b" . assertPlain200 "foo Int 12"
]
queryOneTest :: TestTree
queryOneTest = testGroup "One" $ map ($ queryApp (=!:) (=!:) (=!:))
[ testReq "GET /" . assert404
, testReq "GET /?foo" . assertPlain200 "foo Maybe String Nothing"
, testReq "GET /?foo&foo=3" . assert404
, testReq "GET /?foo=12" . assertPlain200 "foo Int 12"
, testReq "GET /?foo=a" . assertPlain200 "foo String \"a\""
, testReq "GET /?foo=12&foo=23" . assert404
, testReq "GET /?foo=12&foo=b" . assert404
]
queryOptionTest :: TestTree
queryOptionTest = testGroup "Option" $ map ($ queryApp (=?:) (=?:) (=?:))
[ testReq "GET /" . assertPlain200 "foo Int Nothing"
, testReq "GET /?foo" . assertPlain200 "foo Maybe String Just Nothing"
, testReq "GET /?foo&foo=3" . assertPlain200 "foo Maybe String Just Nothing"
, testReq "GET /?foo=12" . assertPlain200 "foo Int Just 12"
, testReq "GET /?foo=a" . assertPlain200 "foo String Just \"a\""
, testReq "GET /?foo=12&foo=23" . assertPlain200 "foo Int Just 12"
, testReq "GET /?foo=12&foo=b" . assertPlain200 "foo Int Just 12"
]
queryOptionalTest :: TestTree
queryOptionalTest = testGroup "Optional" $ map ($ queryOptionalApp)
[ testReq "GET /" . assertPlain200 "foo Int 5"
, testReq "GET /?foo" . assertPlain200 "foo Maybe String Nothing"
, testReq "GET /?foo&foo=3" . assertPlain200 "foo Maybe String Nothing"
, testReq "GET /?foo=12" . assertPlain200 "foo Int 12"
, testReq "GET /?foo=a" . assertPlain200 "foo String \"a\""
, testReq "GET /?foo=12&foo=23" . assertPlain200 "foo Int 12"
, testReq "GET /?foo=12&foo=b" . assertPlain200 "foo Int 12"
]
queryManyTest :: TestTree
queryManyTest = testGroup "Many" $ map ($ queryApp (=*:) (=*:) (=*:))
[ testReq "GET /" . assertPlain200 "foo Int []"
, testReq "GET /?foo" . assertPlain200 "foo Maybe String [Nothing]"
, testReq "GET /?foo&foo=3" . assertPlain200 "foo Maybe String [Nothing,Just \"3\"]"
, testReq "GET /?foo=12" . assertPlain200 "foo Int [12]"
, testReq "GET /?foo=a" . assertPlain200 "foo String [\"a\"]"
, testReq "GET /?foo=12&foo=23" . assertPlain200 "foo Int [12,23]"
, testReq "GET /?foo=12&foo=b" . assertPlain200 "foo String [\"12\",\"b\"]"
]
querySomeTest :: TestTree
querySomeTest = testGroup "Some" $ map ($ queryApp (=+:) (=+:) (=+:))
[ testReq "GET /" . assert404
, testReq "GET /?foo" . assertPlain200 "foo Maybe String [Nothing]"
, testReq "GET /?foo&foo=3" . assertPlain200 "foo Maybe String [Nothing,Just \"3\"]"
, testReq "GET /?foo=12" . assertPlain200 "foo Int [12]"
, testReq "GET /?foo=a" . assertPlain200 "foo String [\"a\"]"
, testReq "GET /?foo=12&foo=23" . assertPlain200 "foo Int [12,23]"
, testReq "GET /?foo=12&foo=b" . assertPlain200 "foo String [\"12\",\"b\"]"
]
switchQueryApp :: Application
switchQueryApp = runApp $ do
switchQuery [key|foo|] . switchQuery [key|bar|] . action $ do
contentType "text/plain"
param [key|foo|] >>= showing
param [key|bar|] >>= appendShowing
switchQueryTest :: TestTree
switchQueryTest = testGroup "switch" $ map ($ switchQueryApp)
[ testReq "GET /" . assertPlain200 "FalseFalse"
, testReq "GET /?foo" . assertPlain200 "TrueFalse"
, testReq "GET /?foo&bar" . assertPlain200 "TrueTrue"
, testReq "GET /?foo=true" . assertPlain200 "TrueFalse"
, testReq "GET /?foo=false" . assertPlain200 "FalseFalse"
, testReq "GET /?foo=false&bar=true" . assertPlain200 "FalseTrue"
, testReq "GET /?foo&bar=true" . assertPlain200 "TrueTrue"
, testReq "GET /?foo=1&bar=0" . assertPlain200 "TrueFalse"
]
queryTest :: TestTree
queryTest = testGroup "query"
[ queryFirstTest
, queryOneTest
, queryOptionTest
, queryOptionalTest
, queryManyTest
, querySomeTest
, switchQueryTest
]
--------------------------------------------------------------------------------
stopApp :: Application
stopApp = runApp $ do
[capture|/a/i::Int|] . action $ do
i <- param [key|i|]
contentType "text/plain"
when (i == 1) $ bytes "one\n"
if i `mod` 2 == 0 then appendBytes "even\n" else appendBytes "odd\n"
when (i == 2) stop
appendBytes "after stop"
stopTest :: TestTree
stopTest = testGroup "stop" $ map ($ stopApp)
[ testReq "GET /a/0" . assertPlain200 "even\nafter stop"
, testReq "GET /a/1" . assertPlain200 "one\nodd\nafter stop"
, testReq "GET /a/2" . assertPlain200 "even\n"
, testReq "GET /a/3" . assertPlain200 "odd\nafter stop"
]
--------------------------------------------------------------------------------
acceptApp :: Application
acceptApp = runApp $ [capture|/|] $ do
accept "application/json" . action $ bytes "json"
accept "text/html;prm=t" . action $ bytes "html+prm"
accept "text/html" . action $ bytes "html"
action $ bytes "other"
acceptTest :: TestTree
acceptTest = testGroup "accept" $ map ($ acceptApp)
[ testReq "GET / application/json" . (\a r -> assertJson200 "json" a $ addA "application/json" r)
, testReq "GET / text/html" . (\a r -> assertHtml200 "html" a $ addA "text/html" r)
, testReq "GET / text/html;p=a" . (\a r -> assertHtml200 "html" a $ addA "text/html;p=a" r)
, testReq "GET / text/html;prm=t" . (\a r -> assertHtml200 "html+prm" a $ addA "text/html;prm=t" r)
, testReq "GET / text/plain" . (\a r -> assertRequest 200 Nothing "other" a $ addA "text/plain" r)
, testReq "GET / text/*" . (\a r -> assertHtml200 "html" a $ addA "text/*" r)
, testReq "GET / text/*;p=a" . (\a r -> assertHtml200 "html" a $ addA "text/*;p=a" r)
, testReq "GET / */*" . (\a r -> assertJson200 "json" a $ addA "*/*" r)
, testReq "GET /" . assertRequest 200 Nothing "other"
]
where
addA :: S.ByteString -> Request -> Request
addA ct r = r {Wai.requestHeaders = ("Accept", ct) : filter (("Accept" ==) . fst) (Wai.requestHeaders r)}
--------------------------------------------------------------------------------
multipleFilter1App :: Application
multipleFilter1App = runApp $ do
root $ do
method GET . action $ contentType "text/plain" >> bytes "GET /"
method POST . action $ contentType "text/html" >> bytes "POST /"
method DELETE . action $ contentType "text/plain" >> bytes "DELETE ANY"
multipleFilter1Test :: TestTree
multipleFilter1Test = testGroup "multiple test1: root, method"
[ testReq "GET /index.html" $ assertPlain200 "GET /" multipleFilter1App
, testReq "POST /" $ assertHtml200 "POST /" multipleFilter1App
, testReq "DELETE /" $ assertPlain200 "DELETE ANY" multipleFilter1App
, testReq "PUT /" $ assert404 multipleFilter1App
]
--------------------------------------------------------------------------------
test :: TestTree
test = testGroup "Application"
[ helloWorldAllTest
, methodFilterTest
, httpVersionTest
, rootFilterTest
, restFilterTest
, captureTest
, queryTest
, stopTest
, acceptTest
, multipleFilter1Test
]