packages feed

linnet-0.1.0.0: test/BodySpec.hs

{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}

module BodySpec
  ( spec
  ) where

import           Control.Concurrent         (MVar, newMVar, putMVar, readMVar,
                                             tryTakeMVar)
import           Control.Exception          (SomeException, toException)
import           Control.Monad.IO.Class     (liftIO)
import qualified Data.ByteString            as BS
import           Data.ByteString.Conversion (ToByteString, toByteString')
import qualified Data.CaseInsensitive       as CI
import qualified Data.Text                  as T
import           Debug.Trace                (trace)
import           EntityEndpointLaws
import           Instances
import           Linnet
import           Linnet.Endpoints.Entity
import           Linnet.Errors
import           Linnet.Input
import           Network.Wai                (Request (..),
                                             RequestBodyLength (..))
import           Test.Hspec
import           Test.QuickCheck            (property)
import           Test.QuickCheck.Monadic    (assert, monadicIO)
import           Util

withBody :: (ToByteString a) => MVar a -> Input
withBody mvar =
  let i = inputGet "/" []
      req = request i
      mBody = do
        result <- tryTakeMVar mvar
        return
          (case result of
             Just value -> toByteString' value
             Nothing    -> mempty)
   in i {request = req {requestBody = mBody, requestBodyLength = KnownLength 123}}

withEmptyBody :: Input
withEmptyBody =
  let i = inputGet "/" []
      req = request i
   in i {request = req {requestBody = pure mempty, requestBodyLength = KnownLength 0}}

spec :: Spec
spec = do
  it "decodes body correctly" $
    property $ \(foo :: Foo) ->
      monadicIO $ do
        let e = textBody @Foo @IO
        mvar <- liftIO $ newMVar (show foo)
        result <- liftIO $ resultOutputEither (runEndpoint e (withBody mvar))
        assert $ result == (Right $ Just (ok foo))
  it "decodes optional body correctly" $
    property $ \(foo :: Foo) ->
      monadicIO $ do
        let e = textBodyMaybe @Foo @IO
        mvar <- liftIO $ newMVar (show foo)
        result <- liftIO $ resultOutputEither (runEndpoint e (withBody mvar))
        assert $ result == (Right $ Just (ok (Just foo)))
  it "throws an error if body is missing" $ do
    let e = textBody @Foo @IO
    result <- resultOutputEither (runEndpoint e (inputGet "/" []))
    result `shouldBe` (Left $ toException (MissingEntity Body))
  it "throws an error if body is malformed" $ do
    let e = textBody @Foo @IO
    mvar <- newMVar ("foo" :: T.Text)
    result <- resultOutputEither (runEndpoint e (withBody mvar))
    result `shouldBe` (Left $ toException (EntityNotParsed Body (DecodeError "Couldn't parse Foo")))
  it "returns nothing if body is not required" $ do
    let e = textBodyMaybe @Foo @IO
    result <- resultOutputEither (runEndpoint e withEmptyBody)
    result `shouldBe` (Right $ Just $ ok Nothing)