Skip to content

Commit 45e7bca

Browse files
committed
Add tasty-wai based test suite
This test suite checks that all modes of decoding work as expected, without running full warp web server. Previous test-suite "upload" was converted to an executable, since it is useless as a test-suite.
1 parent 4fa31a8 commit 45e7bca

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

servant-multipart.cabal

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ library
4848
, wai >=3.2.1.2 && <3.3
4949
, wai-extra >=3.0.24.3 && <3.1
5050

51-
test-suite upload
52-
type: exitcode-stdio-1.0
51+
executable upload
5352
hs-source-dirs: exe
5453
main-is: Upload.hs
5554
default-language: Haskell2010
@@ -68,6 +67,22 @@ test-suite upload
6867
, wai
6968
, warp
7069

70+
test-suite servant-multipart-test
71+
type: exitcode-stdio-1.0
72+
hs-source-dirs: test
73+
main-is: Test.hs
74+
default-language: Haskell2010
75+
build-depends:
76+
base
77+
, bytestring
78+
, http-types
79+
, servant-multipart
80+
, servant-server
81+
, string-conversions
82+
, tasty
83+
, tasty-wai
84+
, text
85+
7186
source-repository head
7287
type: git
7388
location: https://github.com/haskell-servant/servant-multipart

test/Test.hs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE MultiParamTypeClasses #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
{-# LANGUAGE TypeApplications #-}
5+
{-# LANGUAGE TypeOperators #-}
6+
7+
import Data.ByteString as BS (ByteString)
8+
import Data.ByteString.Lazy as BSL (ByteString)
9+
import Data.List (intersperse)
10+
import Data.Monoid
11+
import Data.String.Conversions (cs)
12+
import Data.Text (Text)
13+
import Network.HTTP.Types.Header (HeaderName, hContentType)
14+
15+
import Test.Tasty
16+
import Test.Tasty.Wai
17+
18+
import Servant
19+
import Servant.Multipart
20+
21+
main :: IO ()
22+
main = defaultMain $ testGroup "servant-multipart"
23+
[ testGroup "strict handler with FromMultipart"
24+
[ testWai testApp "correct body" testBlogPostStrictHandler
25+
, testWai testApp "empty body" testBlogPostStrictHandlerEmptyBody
26+
, testWai testApp "partial body" testBlogPostStrictHandlerPartialBody
27+
]
28+
, testGroup "lenient handler with FromMultipart"
29+
[ testWai testApp "correct body" testBlogPostLenientHandler
30+
, testWai testApp "partial body" testBlogPostLenientHandlerPartialBody
31+
]
32+
, testGroup "strict handler with raw MultipartData"
33+
[ testWai testApp "correct body" testBlogPostRawHandler
34+
]
35+
]
36+
37+
data BlogPost
38+
= BlogPost
39+
{ title :: Text
40+
, body :: Text
41+
}
42+
43+
instance FromMultipart Mem BlogPost where
44+
fromMultipart md =
45+
BlogPost
46+
<$> lookupInput "title" md
47+
<*> fmap (cs . fdPayload) (lookupFile "body" md)
48+
49+
type TestAPI
50+
= "blogPostStrict" :> MultipartForm Mem BlogPost :> Post '[PlainText] Text
51+
:<|> "blogPostLenient" :> MultipartForm' '[Lenient] Mem BlogPost :> Post '[JSON] Bool
52+
:<|> "blogPostRaw" :> MultipartForm Mem (MultipartData Mem) :> Post '[PlainText] Text
53+
54+
blogPostStrictHandler :: BlogPost -> Handler Text
55+
blogPostStrictHandler bp = return $ title bp <> "\n" <> body bp
56+
57+
blogPostLenientHandler :: Either String BlogPost -> Handler Bool
58+
blogPostLenientHandler eitherBP =
59+
case eitherBP of
60+
Left _ -> return False
61+
Right _ -> return True
62+
63+
blogPostRawHandler :: MultipartData Mem -> Handler Text
64+
blogPostRawHandler md =
65+
return $ mconcat $ intersperse " "
66+
$ map iName (inputs md) <> map fdInputName (files md)
67+
68+
testApp :: Application
69+
testApp = serve @TestAPI Proxy $ blogPostStrictHandler :<|> blogPostLenientHandler :<|> blogPostRawHandler
70+
71+
multipartHeaders :: [(HeaderName, BS.ByteString)]
72+
multipartHeaders = [(hContentType, "multipart/form-data; boundary=XX")]
73+
74+
testBlogPostStrictHandler :: Session ()
75+
testBlogPostStrictHandler = do
76+
res <- srequest $ buildRequestWithHeaders POST "/blogPostStrict" correctBody multipartHeaders
77+
assertStatus 200 res
78+
assertBody "Foo post\nFoo body\n" res
79+
80+
testBlogPostStrictHandlerEmptyBody :: Session ()
81+
testBlogPostStrictHandlerEmptyBody = do
82+
res <- srequest $ buildRequestWithHeaders POST "/blogPostStrict" "" multipartHeaders
83+
assertStatus 400 res
84+
assertBody "Could not decode multipart mime body: Field title not found" res
85+
86+
testBlogPostStrictHandlerPartialBody :: Session ()
87+
testBlogPostStrictHandlerPartialBody = do
88+
res <- srequest $ buildRequestWithHeaders POST "/blogPostStrict" partialBody multipartHeaders
89+
assertStatus 400 res
90+
assertBody "Could not decode multipart mime body: File body not found" res
91+
92+
testBlogPostLenientHandler :: Session ()
93+
testBlogPostLenientHandler = do
94+
res <- srequest $ buildRequestWithHeaders POST "/blogPostLenient" correctBody multipartHeaders
95+
assertStatus 200 res
96+
assertBody "true" res
97+
98+
testBlogPostLenientHandlerPartialBody :: Session ()
99+
testBlogPostLenientHandlerPartialBody = do
100+
res <- srequest $ buildRequestWithHeaders POST "/blogPostLenient" partialBody multipartHeaders
101+
assertStatus 200 res
102+
assertBody "false" res
103+
104+
testBlogPostRawHandler :: Session ()
105+
testBlogPostRawHandler = do
106+
res <- srequest $ buildRequestWithHeaders POST "/blogPostRaw" correctBody multipartHeaders
107+
assertStatus 200 res
108+
assertBody "title body" res
109+
110+
correctBody :: BSL.ByteString
111+
correctBody = mconcat $ intersperse "\n"
112+
[ "--XX"
113+
, "Content-Disposition: form-data; name=\"title\""
114+
, ""
115+
, "Foo post"
116+
, "--XX"
117+
, "Content-Disposition: form-data; name=\"body\"; filename=\"body.md\""
118+
, ""
119+
, "Foo body"
120+
, ""
121+
, "--XX--"
122+
]
123+
124+
partialBody :: BSL.ByteString
125+
partialBody = mconcat $ intersperse "\n"
126+
[ "--XX"
127+
, "Content-Disposition: form-data; name=\"title\""
128+
, ""
129+
, "Foo post"
130+
, ""
131+
, "--XX--"
132+
]

0 commit comments

Comments
 (0)