{- |
Module      : Test.Hspec.Benri
Copyright   : (c) 2022 Tim Emiola
Maintainer  : Tim Emiola <adetokunbo@emio.la>
SPDX-License-Identifier: BSD3

Provides \convenient\ functions for writing hspec tests where test values are
returned from a monad.
-}
module Test.Hspec.Benri
  ( -- * match a predicate
    endsThen

    -- * @Maybe@ values
  , endsJust
  , endsJust_
  , endsNothing

    -- * @Either@ values
  , endsLeft
  , endsLeft_
  , endsRight
  , endsRight_
  )
where

import Data.Maybe (isJust)
import Test.Hspec (Expectation, HasCallStack, shouldBe, shouldSatisfy)


{- $setup
 >>> import Text.Read (readEither, readMaybe)
 >>> import Data.Maybe (isNothing, isJust)
-}


{- | @action \`endsRight\` expected@ sets the expectation that @action@
 __returns__ @Right expected@.

==== __Example__

>>> pure (readEither "1" :: Either String Int) `endsRight` 1
-}
endsRight :: (HasCallStack, Show a, Eq a, Show b, Eq b) => IO (Either a b) -> b -> Expectation
IO (Either a b)
action endsRight :: forall a b.
(HasCallStack, Show a, Eq a, Show b, Eq b) =>
IO (Either a b) -> b -> Expectation
`endsRight` b
expected = IO (Either a b)
action IO (Either a b) -> (Either a b -> Expectation) -> Expectation
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Either a b -> Either a b -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` b -> Either a b
forall a b. b -> Either a b
Right b
expected)


{- | @action \`endsLeft\` expected@ sets the expectation that @action@ __returns__
 @Left expected@.

==== __Example__

>>> pure (readEither "not an int" :: Either String Int) `endsLeft` "Prelude.read: no parse"
-}
endsLeft
  :: (HasCallStack, Show a, Eq a, Show b, Eq b) => IO (Either a b) -> a -> Expectation
IO (Either a b)
action endsLeft :: forall a b.
(HasCallStack, Show a, Eq a, Show b, Eq b) =>
IO (Either a b) -> a -> Expectation
`endsLeft` a
expected = IO (Either a b)
action IO (Either a b) -> (Either a b -> Expectation) -> Expectation
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Either a b -> Either a b -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` a -> Either a b
forall a b. a -> Either a b
Left a
expected)


{- | @endsRight_ action@ sets the expectation that @action@ __returns__ @Right b@.

==== __Example__

>>> endsRight_ $ pure (readEither "1" :: Either String Int)
-}
endsRight_ :: (Show a, Show b) => IO (Either a b) -> IO ()
endsRight_ :: forall a b. (Show a, Show b) => IO (Either a b) -> Expectation
endsRight_ IO (Either a b)
action = IO (Either a b) -> (Either a b -> Bool) -> Expectation
forall a. Show a => IO a -> (a -> Bool) -> Expectation
endsThen IO (Either a b)
action ((Either a b -> Bool) -> Expectation)
-> (Either a b -> Bool) -> Expectation
forall a b. (a -> b) -> a -> b
$ (a -> Bool) -> (b -> Bool) -> Either a b -> Bool
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
False) (Bool -> b -> Bool
forall a b. a -> b -> a
const Bool
True)


{- | @endsLeft_ action@ sets the expectation that @action@ __returns__ @Left a@.

==== __Example__

>>> endsLeft_ $ pure (readEither "not an int" :: Either String Int)
-}
endsLeft_ :: (Show a, Show b) => IO (Either a b) -> IO ()
endsLeft_ :: forall a b. (Show a, Show b) => IO (Either a b) -> Expectation
endsLeft_ IO (Either a b)
action = IO (Either a b) -> (Either a b -> Bool) -> Expectation
forall a. Show a => IO a -> (a -> Bool) -> Expectation
endsThen IO (Either a b)
action ((Either a b -> Bool) -> Expectation)
-> (Either a b -> Bool) -> Expectation
forall a b. (a -> b) -> a -> b
$ (a -> Bool) -> (b -> Bool) -> Either a b -> Bool
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True) (Bool -> b -> Bool
forall a b. a -> b -> a
const Bool
False)


{- | @action \`endsJust\` expected@ sets the expectation that @action@ __returns__
 @Just expected@.

==== __Example__

>>> pure (readMaybe "1" :: Maybe Int) `endsJust` 1
-}
endsJust
  :: (HasCallStack, Show a, Eq a) => IO (Maybe a) -> a -> Expectation
IO (Maybe a)
action endsJust :: forall a.
(HasCallStack, Show a, Eq a) =>
IO (Maybe a) -> a -> Expectation
`endsJust` a
expected = IO (Maybe a)
action IO (Maybe a) -> (Maybe a -> Expectation) -> Expectation
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Maybe a -> Maybe a -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` a -> Maybe a
forall a. a -> Maybe a
Just a
expected)


{- | @endsNothing action@ sets the expectation that @action@ __returns__
 @Nothing@.

==== __Example__

>>> endsNothing $ pure (readMaybe "not an int" :: Maybe Int)
-}
endsNothing :: (Show a, Eq a) => IO (Maybe a) -> IO ()
endsNothing :: forall a. (Show a, Eq a) => IO (Maybe a) -> Expectation
endsNothing IO (Maybe a)
action = IO (Maybe a)
action IO (Maybe a) -> (Maybe a -> Expectation) -> Expectation
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Maybe a -> Maybe a -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`shouldBe` Maybe a
forall a. Maybe a
Nothing)


{- | @endsJust_ action@ sets the expectation that @action@ __returns__ @Just a@.

==== __Example__

>>> endsJust_ $ pure (readMaybe "1" :: Maybe Int)
-}
endsJust_ :: (Show a) => IO (Maybe a) -> IO ()
endsJust_ :: forall a. Show a => IO (Maybe a) -> Expectation
endsJust_ IO (Maybe a)
action = IO (Maybe a) -> (Maybe a -> Bool) -> Expectation
forall a. Show a => IO a -> (a -> Bool) -> Expectation
endsThen IO (Maybe a)
action Maybe a -> Bool
forall a. Maybe a -> Bool
isJust


{- | @action \`endsThen\` expected@ sets the expectation that the result of
 @action@ __satisfies__ the predicate @p@.

==== __Example__

>>> pure (readMaybe "1" :: Maybe Int) `endsThen` isJust
>>> pure (readMaybe "not a number" :: Maybe Int) `endsThen` isNothing
-}
endsThen :: (Show a) => IO a -> (a -> Bool) -> IO ()
endsThen :: forall a. Show a => IO a -> (a -> Bool) -> Expectation
endsThen IO a
action a -> Bool
p = IO a
action IO a -> (a -> Expectation) -> Expectation
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (a -> (a -> Bool) -> Expectation
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> Expectation
`shouldSatisfy` a -> Bool
p)


infix 1 `endsLeft`, `endsRight`, `endsThen`, `endsJust`