-- | Yesod.Test.Json provides convenience functions for working -- with Test.Hspec and Network.Wai.Test on JSON data. module Yesod.Test.Json ( testApp, APIFunction, assertBool, assertString, assertOK, assertJSON, Session(..), H.Assertion, module Test.Hspec, module Data.Aeson, SResponse(..) ) where import qualified Test.HUnit as H import qualified Data.ByteString.Lazy.Char8 as L8 import qualified Data.ByteString.Lazy as L import Data.ByteString (ByteString) import Data.Text (Text) import Data.Aeson import Network.HTTP.Types import Test.Hspec import Network.Wai import Network.Wai.Test import Control.Monad.IO.Class import Yesod.Default.Config import Data.Conduit.List -- | A request to your server. type APIFunction = ByteString -- ^ method -> [Text] -- ^ path -> Maybe Value -- JSON data -> Session SResponse -- Assert a boolean value assertBool :: String -> Bool -> Session () assertBool s b = liftIO $ H.assertBool s b -- Fail a test with an error string assertString :: String -> Session () assertString = liftIO . H.assertString -- Assert a 200 response code assertOK :: SResponse -> Session () assertOK SResponse{simpleStatus = s, simpleBody = b} = assertBool (concat [ "Expected status code 200, but received " , show sc , ". Response body: " , show (L8.unpack b) ]) $ sc == 200 where sc = statusCode s -- Assert a JSON body meeting some requirement assertJSON :: (ToJSON a, FromJSON a) => (a -> (String, Bool)) -> SResponse -> Session () assertJSON f SResponse{simpleBody = lbs} = do case decode lbs of Nothing -> assertString $ "Invalid JSON: " ++ show (L8.unpack lbs) Just a -> case fromJSON a of Error s -> assertString (concat [s, "\nInput JSON: ", show a]) Success x -> uncurry assertBool (f x) -- | Make a request to your server apiRequest :: AppConfig env extra -> APIFunction apiRequest conf m p x = srequest $ SRequest r (maybe L.empty encode x) where r = defaultRequest { serverPort = appPort conf, requestBody = sourceList . L.toChunks $ encode x, requestMethod = m, pathInfo = p } -- | Run a test suite for your 'Application' testApp :: Application -> AppConfig env extra -> (((APIFunction -> Session ()) -> H.Assertion) -> Spec) -> IO () testApp app conf specfun = do let apiTest f = runSession (f (apiRequest conf)) app hspec $ (specfun apiTest)