{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-deprecations #-}

module Freckle.App.Test
  ( AppExample (..)
  , appExample
  , withApp
  , beforeSql
  , expectationFailure
  , pending
  , pendingWith

    -- * Re-exports
  , module X
  ) where

import Freckle.App.Prelude as X

import Data.Pool as X
import Test.Hspec as X
  ( Expectation
  , Spec
  , beforeAll
  , beforeWith
  , context
  , describe
  , example
  , fit
  , it
  , xit
  )
import Test.Hspec.Expectations.Lifted as X hiding (expectationFailure)

import Blammo.Logging
import Control.Lens (view)
import Control.Monad.Base
import Control.Monad.Catch (ExitCase (..), MonadCatch, MonadThrow, mask)
import qualified Control.Monad.Catch
import qualified Control.Monad.Fail as Fail
import Control.Monad.Primitive
import Control.Monad.Random (MonadRandom (..))
import Control.Monad.Reader
import Control.Monad.Trans.Control
import Database.Persist.Sql (SqlPersistT, runSqlPool)
import Freckle.App.Database
  ( HasSqlPool (..)
  , HasStatsClient
  , MonadSqlTx (..)
  , runDB
  )
import qualified Freckle.App.Database.XRay as XRay
import qualified Freckle.App.Dotenv as Dotenv
import qualified Freckle.App.Exception.MonadThrow as MonadThrow
import Freckle.App.OpenTelemetry
import qualified Test.Hspec as Hspec hiding (expectationFailure)
import Test.Hspec.Core.Spec (Arg, Example, SpecWith, evaluateExample)
import qualified Test.Hspec.Expectations.Lifted as Hspec (expectationFailure)
import qualified UnliftIO.Exception as UnliftIO

-- | An Hspec example over some @App@ value
--
-- To disable logging in tests, you can either:
--
-- - Export @LOG_LEVEL=error@, if this would be quiet enough, or
-- - Export @LOG_DESTINATION=@/dev/null@ to fully silence
newtype AppExample app a = AppExample
  { forall app a. AppExample app a -> ReaderT app (LoggingT IO) a
unAppExample :: ReaderT app (LoggingT IO) a
  }
  deriving newtype
    ( Functor (AppExample app)
Functor (AppExample app) =>
(forall a. a -> AppExample app a)
-> (forall a b.
    AppExample app (a -> b) -> AppExample app a -> AppExample app b)
-> (forall a b c.
    (a -> b -> c)
    -> AppExample app a -> AppExample app b -> AppExample app c)
-> (forall a b.
    AppExample app a -> AppExample app b -> AppExample app b)
-> (forall a b.
    AppExample app a -> AppExample app b -> AppExample app a)
-> Applicative (AppExample app)
forall app. Functor (AppExample app)
forall a. a -> AppExample app a
forall app a. a -> AppExample app a
forall a b.
AppExample app a -> AppExample app b -> AppExample app a
forall a b.
AppExample app a -> AppExample app b -> AppExample app b
forall a b.
AppExample app (a -> b) -> AppExample app a -> AppExample app b
forall app a b.
AppExample app a -> AppExample app b -> AppExample app a
forall app a b.
AppExample app a -> AppExample app b -> AppExample app b
forall app a b.
AppExample app (a -> b) -> AppExample app a -> AppExample app b
forall a b c.
(a -> b -> c)
-> AppExample app a -> AppExample app b -> AppExample app c
forall app a b c.
(a -> b -> c)
-> AppExample app a -> AppExample app b -> AppExample app c
forall (f :: * -> *).
Functor f =>
(forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
$cpure :: forall app a. a -> AppExample app a
pure :: forall a. a -> AppExample app a
$c<*> :: forall app a b.
AppExample app (a -> b) -> AppExample app a -> AppExample app b
<*> :: forall a b.
AppExample app (a -> b) -> AppExample app a -> AppExample app b
$cliftA2 :: forall app a b c.
(a -> b -> c)
-> AppExample app a -> AppExample app b -> AppExample app c
liftA2 :: forall a b c.
(a -> b -> c)
-> AppExample app a -> AppExample app b -> AppExample app c
$c*> :: forall app a b.
AppExample app a -> AppExample app b -> AppExample app b
*> :: forall a b.
AppExample app a -> AppExample app b -> AppExample app b
$c<* :: forall app a b.
AppExample app a -> AppExample app b -> AppExample app a
<* :: forall a b.
AppExample app a -> AppExample app b -> AppExample app a
Applicative
    , (forall a b. (a -> b) -> AppExample app a -> AppExample app b)
-> (forall a b. a -> AppExample app b -> AppExample app a)
-> Functor (AppExample app)
forall a b. a -> AppExample app b -> AppExample app a
forall a b. (a -> b) -> AppExample app a -> AppExample app b
forall app a b. a -> AppExample app b -> AppExample app a
forall app a b. (a -> b) -> AppExample app a -> AppExample app b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall app a b. (a -> b) -> AppExample app a -> AppExample app b
fmap :: forall a b. (a -> b) -> AppExample app a -> AppExample app b
$c<$ :: forall app a b. a -> AppExample app b -> AppExample app a
<$ :: forall a b. a -> AppExample app b -> AppExample app a
Functor
    , Applicative (AppExample app)
Applicative (AppExample app) =>
(forall a b.
 AppExample app a -> (a -> AppExample app b) -> AppExample app b)
-> (forall a b.
    AppExample app a -> AppExample app b -> AppExample app b)
-> (forall a. a -> AppExample app a)
-> Monad (AppExample app)
forall app. Applicative (AppExample app)
forall a. a -> AppExample app a
forall app a. a -> AppExample app a
forall a b.
AppExample app a -> AppExample app b -> AppExample app b
forall a b.
AppExample app a -> (a -> AppExample app b) -> AppExample app b
forall app a b.
AppExample app a -> AppExample app b -> AppExample app b
forall app a b.
AppExample app a -> (a -> AppExample app b) -> AppExample app b
forall (m :: * -> *).
Applicative m =>
(forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
$c>>= :: forall app a b.
AppExample app a -> (a -> AppExample app b) -> AppExample app b
>>= :: forall a b.
AppExample app a -> (a -> AppExample app b) -> AppExample app b
$c>> :: forall app a b.
AppExample app a -> AppExample app b -> AppExample app b
>> :: forall a b.
AppExample app a -> AppExample app b -> AppExample app b
$creturn :: forall app a. a -> AppExample app a
return :: forall a. a -> AppExample app a
Monad
    , MonadBase IO
    , MonadBaseControl IO
    , MonadThrow (AppExample app)
MonadThrow (AppExample app) =>
(forall e a.
 (HasCallStack, Exception e) =>
 AppExample app a -> (e -> AppExample app a) -> AppExample app a)
-> MonadCatch (AppExample app)
forall app. MonadThrow (AppExample app)
forall e a.
(HasCallStack, Exception e) =>
AppExample app a -> (e -> AppExample app a) -> AppExample app a
forall app e a.
(HasCallStack, Exception e) =>
AppExample app a -> (e -> AppExample app a) -> AppExample app a
forall (m :: * -> *).
MonadThrow m =>
(forall e a.
 (HasCallStack, Exception e) =>
 m a -> (e -> m a) -> m a)
-> MonadCatch m
$ccatch :: forall app e a.
(HasCallStack, Exception e) =>
AppExample app a -> (e -> AppExample app a) -> AppExample app a
catch :: forall e a.
(HasCallStack, Exception e) =>
AppExample app a -> (e -> AppExample app a) -> AppExample app a
MonadCatch
    , Monad (AppExample app)
Monad (AppExample app) =>
(forall a. IO a -> AppExample app a) -> MonadIO (AppExample app)
forall app. Monad (AppExample app)
forall a. IO a -> AppExample app a
forall app α. IO α -> AppExample app α
forall (m :: * -> *).
Monad m =>
(forall a. IO a -> m a) -> MonadIO m
$cliftIO :: forall app α. IO α -> AppExample app α
liftIO :: forall a. IO a -> AppExample app a
MonadIO
    , MonadIO (AppExample app)
MonadIO (AppExample app) =>
(forall b.
 ((forall a. AppExample app a -> IO a) -> IO b) -> AppExample app b)
-> MonadUnliftIO (AppExample app)
forall app. MonadIO (AppExample app)
forall b.
((forall a. AppExample app a -> IO a) -> IO b) -> AppExample app b
forall app b.
((forall a. AppExample app a -> IO a) -> IO b) -> AppExample app b
forall (m :: * -> *).
MonadIO m =>
(forall b. ((forall a. m a -> IO a) -> IO b) -> m b)
-> MonadUnliftIO m
$cwithRunInIO :: forall app b.
((forall a. AppExample app a -> IO a) -> IO b) -> AppExample app b
withRunInIO :: forall b.
((forall a. AppExample app a -> IO a) -> IO b) -> AppExample app b
MonadUnliftIO
    , MonadReader app
    , Monad (AppExample app)
Monad (AppExample app) =>
(forall e a. (HasCallStack, Exception e) => e -> AppExample app a)
-> MonadThrow (AppExample app)
forall app. Monad (AppExample app)
forall e a. (HasCallStack, Exception e) => e -> AppExample app a
forall app e a.
(HasCallStack, Exception e) =>
e -> AppExample app a
forall (m :: * -> *).
Monad m =>
(forall e a. (HasCallStack, Exception e) => e -> m a)
-> MonadThrow m
$cthrowM :: forall app e a.
(HasCallStack, Exception e) =>
e -> AppExample app a
throwM :: forall e a. (HasCallStack, Exception e) => e -> AppExample app a
MonadThrow
    , Monad (AppExample app)
Monad (AppExample app) =>
(forall msg.
 ToLogStr msg =>
 Loc -> LogSource -> LogLevel -> msg -> AppExample app ())
-> MonadLogger (AppExample app)
forall app. Monad (AppExample app)
forall msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> AppExample app ()
forall app msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> AppExample app ()
forall (m :: * -> *).
Monad m =>
(forall msg.
 ToLogStr msg =>
 Loc -> LogSource -> LogLevel -> msg -> m ())
-> MonadLogger m
$cmonadLoggerLog :: forall app msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> AppExample app ()
monadLoggerLog :: forall msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> AppExample app ()
MonadLogger
    , Monad (AppExample app)
Monad (AppExample app) =>
(forall a. String -> AppExample app a)
-> MonadFail (AppExample app)
forall app. Monad (AppExample app)
forall a. String -> AppExample app a
forall app a. String -> AppExample app a
forall (m :: * -> *).
Monad m =>
(forall a. String -> m a) -> MonadFail m
$cfail :: forall app a. String -> AppExample app a
fail :: forall a. String -> AppExample app a
Fail.MonadFail
    )

instance MonadMask (AppExample app) where
  mask :: forall b.
HasCallStack =>
((forall a. AppExample app a -> AppExample app a)
 -> AppExample app b)
-> AppExample app b
mask = ((forall a. AppExample app a -> AppExample app a)
 -> AppExample app b)
-> AppExample app b
forall (m :: * -> *) b.
MonadUnliftIO m =>
((forall a. m a -> m a) -> m b) -> m b
UnliftIO.mask
  uninterruptibleMask :: forall b.
HasCallStack =>
((forall a. AppExample app a -> AppExample app a)
 -> AppExample app b)
-> AppExample app b
uninterruptibleMask = ((forall a. AppExample app a -> AppExample app a)
 -> AppExample app b)
-> AppExample app b
forall (m :: * -> *) b.
MonadUnliftIO m =>
((forall a. m a -> m a) -> m b) -> m b
UnliftIO.uninterruptibleMask
  generalBracket :: forall a b c.
HasCallStack =>
AppExample app a
-> (a -> ExitCase b -> AppExample app c)
-> (a -> AppExample app b)
-> AppExample app (b, c)
generalBracket AppExample app a
acquire a -> ExitCase b -> AppExample app c
release a -> AppExample app b
use = ((forall a. AppExample app a -> AppExample app a)
 -> AppExample app (b, c))
-> AppExample app (b, c)
forall b.
HasCallStack =>
((forall a. AppExample app a -> AppExample app a)
 -> AppExample app b)
-> AppExample app b
forall (m :: * -> *) b.
(MonadMask m, HasCallStack) =>
((forall a. m a -> m a) -> m b) -> m b
mask (((forall a. AppExample app a -> AppExample app a)
  -> AppExample app (b, c))
 -> AppExample app (b, c))
-> ((forall a. AppExample app a -> AppExample app a)
    -> AppExample app (b, c))
-> AppExample app (b, c)
forall a b. (a -> b) -> a -> b
$ \forall a. AppExample app a -> AppExample app a
unmasked -> do
    a
resource <- AppExample app a
acquire
    b
b <-
      AppExample app b -> AppExample app b
forall a. AppExample app a -> AppExample app a
unmasked (a -> AppExample app b
use a
resource) AppExample app b
-> (SomeException -> AppExample app b) -> AppExample app b
forall e (m :: * -> *) a.
(Exception e, MonadUnliftIO m, HasCallStack) =>
m a -> (e -> m a) -> m a
`catch` \SomeException
e -> do
        c
_ <- a -> ExitCase b -> AppExample app c
release a
resource (SomeException -> ExitCase b
forall a. SomeException -> ExitCase a
ExitCaseException SomeException
e)
        SomeException -> AppExample app b
forall e (m :: * -> *) a.
(Exception e, MonadThrow m, HasCallStack) =>
e -> m a
MonadThrow.throwM SomeException
e

    c
c <- a -> ExitCase b -> AppExample app c
release a
resource (b -> ExitCase b
forall a. a -> ExitCase a
ExitCaseSuccess b
b)
    (b, c) -> AppExample app (b, c)
forall a. a -> AppExample app a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (b
b, c
c)

instance MonadRandom (AppExample app) where
  getRandomR :: forall a. Random a => (a, a) -> AppExample app a
getRandomR = IO a -> AppExample app a
forall a. IO a -> AppExample app a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO a -> AppExample app a)
-> ((a, a) -> IO a) -> (a, a) -> AppExample app a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, a) -> IO a
forall a. Random a => (a, a) -> IO a
forall (m :: * -> *) a. (MonadRandom m, Random a) => (a, a) -> m a
getRandomR
  getRandom :: forall a. Random a => AppExample app a
getRandom = IO a -> AppExample app a
forall a. IO a -> AppExample app a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO a
forall a. Random a => IO a
forall (m :: * -> *) a. (MonadRandom m, Random a) => m a
getRandom
  getRandomRs :: forall a. Random a => (a, a) -> AppExample app [a]
getRandomRs = IO [a] -> AppExample app [a]
forall a. IO a -> AppExample app a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [a] -> AppExample app [a])
-> ((a, a) -> IO [a]) -> (a, a) -> AppExample app [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, a) -> IO [a]
forall a. Random a => (a, a) -> IO [a]
forall (m :: * -> *) a.
(MonadRandom m, Random a) =>
(a, a) -> m [a]
getRandomRs
  getRandoms :: forall a. Random a => AppExample app [a]
getRandoms = IO [a] -> AppExample app [a]
forall a. IO a -> AppExample app a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO [a]
forall a. Random a => IO [a]
forall (m :: * -> *) a. (MonadRandom m, Random a) => m [a]
getRandoms

instance PrimMonad (AppExample app) where
  type PrimState (AppExample app) = PrimState IO
  primitive :: forall a.
(State# (PrimState (AppExample app))
 -> (# State# (PrimState (AppExample app)), a #))
-> AppExample app a
primitive = IO a -> AppExample app a
forall a. IO a -> AppExample app a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO a -> AppExample app a)
-> ((State# RealWorld -> (# State# RealWorld, a #)) -> IO a)
-> (State# RealWorld -> (# State# RealWorld, a #))
-> AppExample app a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
(State# (PrimState IO) -> (# State# (PrimState IO), a #)) -> IO a
forall a.
(State# (PrimState IO) -> (# State# (PrimState IO), a #)) -> IO a
forall (m :: * -> *) a.
PrimMonad m =>
(State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
primitive

instance HasLogger app => Example (AppExample app a) where
  type Arg (AppExample app a) = app

  evaluateExample :: AppExample app a
-> Params
-> (ActionWith (Arg (AppExample app a)) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample (AppExample ReaderT app (LoggingT IO) a
ex) Params
params ActionWith (Arg (AppExample app a)) -> IO ()
action =
    IO ()
-> Params
-> (ActionWith (Arg (IO ())) -> IO ())
-> ProgressCallback
-> IO Result
forall e.
Example e =>
e
-> Params
-> (ActionWith (Arg e) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample
      (ActionWith (Arg (AppExample app a)) -> IO ()
action (ActionWith (Arg (AppExample app a)) -> IO ())
-> ActionWith (Arg (AppExample app a)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Arg (AppExample app a)
app -> IO a -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO a -> IO ()) -> IO a -> IO ()
forall a b. (a -> b) -> a -> b
$ app -> LoggingT IO a -> IO a
forall (m :: * -> *) env a.
(MonadUnliftIO m, HasLogger env) =>
env -> LoggingT m a -> m a
runLoggerLoggingT app
Arg (AppExample app a)
app (LoggingT IO a -> IO a) -> LoggingT IO a -> IO a
forall a b. (a -> b) -> a -> b
$ ReaderT app (LoggingT IO) a -> app -> LoggingT IO a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ReaderT app (LoggingT IO) a
ex app
Arg (AppExample app a)
app)
      Params
params
      ((() -> IO ()) -> () -> IO ()
forall a b. (a -> b) -> a -> b
$ ())

instance HasTracer app => MonadTracer (AppExample app) where
  getTracer :: AppExample app Tracer
getTracer = Getting Tracer app Tracer -> AppExample app Tracer
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Tracer app Tracer
forall s. HasTracer s => Lens' s Tracer
Lens' app Tracer
tracerL

instance XRay.MonadTracer (AppExample app) where
  getVaultData :: AppExample app (Maybe XRayVaultData)
getVaultData = Maybe XRayVaultData -> AppExample app (Maybe XRayVaultData)
forall a. a -> AppExample app a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe XRayVaultData
forall a. Maybe a
Nothing

instance
  (HasSqlPool app, HasStatsClient app, HasTracer app)
  => MonadSqlTx (SqlPersistT (AppExample app)) (AppExample app)
  where
  runSqlTx :: forall a.
HasCallStack =>
SqlPersistT (AppExample app) a -> AppExample app a
runSqlTx = SqlPersistT (AppExample app) a -> AppExample app a
forall (m :: * -> *) app a.
(MonadUnliftIO m, MonadTracer m, MonadReader app m, HasSqlPool app,
 HasStatsClient app, HasCallStack) =>
SqlPersistT m a -> m a
runDB

-- | A type restricted version of id
--
-- Like 'example', which forces the expectation to 'IO', this can be used to
-- force the expectation to 'AppExample'.
--
-- This can be used to avoid ambiguity errors when your expectation uses only
-- polymorphic functions like 'runDB' or lifted 'shouldBe' et-al.
appExample :: AppExample app a -> AppExample app a
appExample :: forall app a. AppExample app a -> AppExample app a
appExample = AppExample app a -> AppExample app a
forall a. a -> a
id

-- | Spec before helper
--
-- @
-- spec :: Spec
-- spec = 'withApp' loadApp $ do
-- @
--
-- Reads @.env.test@, then loads the application. Examples within this spec can
-- use any @'MonadReader' app@ (including 'runDB', if the app 'HasSqlPool').
withApp :: ((app -> IO ()) -> IO ()) -> SpecWith app -> Spec
withApp :: forall app. ((app -> IO ()) -> IO ()) -> SpecWith app -> Spec
withApp (app -> IO ()) -> IO ()
run = IO () -> Spec -> Spec
forall a. HasCallStack => IO a -> SpecWith a -> Spec
beforeAll IO ()
Dotenv.loadTest (Spec -> Spec) -> (SpecWith app -> Spec) -> SpecWith app -> Spec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((app -> IO ()) -> IO ()) -> SpecWith app -> Spec
forall a.
HasCallStack =>
(ActionWith a -> IO ()) -> SpecWith a -> Spec
Hspec.aroundAll (app -> IO ()) -> IO ()
run

-- | Run the given SQL action before every spec item
beforeSql :: HasSqlPool app => SqlPersistT IO a -> SpecWith app -> SpecWith app
beforeSql :: forall app a.
HasSqlPool app =>
SqlPersistT IO a -> SpecWith app -> SpecWith app
beforeSql SqlPersistT IO a
f = (app -> IO app) -> SpecWith app -> SpecWith app
forall b a. (b -> IO a) -> SpecWith a -> SpecWith b
beforeWith ((app -> IO app) -> SpecWith app -> SpecWith app)
-> (app -> IO app) -> SpecWith app -> SpecWith app
forall a b. (a -> b) -> a -> b
$ \app
app -> app
app app -> IO a -> IO app
forall a b. a -> IO b -> IO a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ SqlPersistT IO a -> Pool SqlBackend -> IO a
forall backend (m :: * -> *) a.
(MonadUnliftIO m, BackendCompatible SqlBackend backend) =>
ReaderT backend m a -> Pool backend -> m a
runSqlPool SqlPersistT IO a
f (app -> Pool SqlBackend
forall app. HasSqlPool app => app -> Pool SqlBackend
getSqlPool app
app)

expectationFailure :: (MonadIO m, HasCallStack) => String -> m a
expectationFailure :: forall (m :: * -> *) a. (MonadIO m, HasCallStack) => String -> m a
expectationFailure String
msg = String -> m ()
forall (m :: * -> *). (HasCallStack, MonadIO m) => String -> m ()
Hspec.expectationFailure String
msg m () -> m a -> m a
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> m a
forall a. HasCallStack => String -> a
error String
"unreachable"

pending :: MonadIO m => m ()
pending :: forall (m :: * -> *). MonadIO m => m ()
pending = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
HasCallStack => IO ()
Hspec.pending

pendingWith :: MonadIO m => String -> m ()
pendingWith :: forall (m :: * -> *). MonadIO m => String -> m ()
pendingWith String
msg = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> IO ()
String -> IO ()
Hspec.pendingWith String
msg