{-# LANGUAGE UndecidableInstances #-}

-- | @DerivingVia@ machinery for mocking AWS interactions in tests
--
-- This module assumes your specs run in a custom transformer that can provide a
-- reader environment. If you define 'HasMatchers' for that environment, you can
-- then derive 'MonadAWS' for this transformer via 'MockAWS'.
--
-- For a more explicit alternative, see "Control.Monad.AWS.MockT".
--
-- === Example:
--
-- Assuming you have some implementation you wanted to test:
--
-- @
-- getBucketsByPrefix :: (MonadIO m, MonadAWS) m => Text -> m [Bucket]
-- getBucketsByPrefix p = do
--   resp <- send newListBuckets
--   pure
--    $ maybe [] (filter matchesPrefix)
--    $ resp ^. listBucketsResponse_buckets
--  where
--   matchesPrefix b = p `T.isPrefixOf` toText (b ^. bucket_name)
-- @
--
-- And assuming you've set up your example monad with 'MonadAWS' via 'MockAWS',
-- you can now test it without talking to AWS:
--
-- @
-- describe "getBucketsByPrefix" $ do
--   it "works" $ do
--     now <- getCurrentTime
--
--     let
--       bucketA = newBucket now "a-bucket"
--       bucketB = newBucket now "b-bucket"
--       bucketC = newBucket now "c-bucket"
--       buckets = [bucketA, bucketB, bucketC]
--       matcher =
--         SendMatcher (== newListBuckets)
--          $ Right
--          $ newListBucketsResponse 200
--          & listBucketsResponse_buckets ?~ buckets
--
--     withMatcher matcher $ do
--       buckets <- getBucketsByPrefix "b-"
--       buckets `shouldBe` [bucketB]
-- @
module Control.Monad.AWS.ViaMock
  ( Matchers
  , HasMatchers (..)
  , Matcher (..)
  , withMatcher
  , withMatchers
  , MockAWS (..)
  ) where

import Prelude

import Amazonka (AuthEnv (..))
import Control.Monad.AWS.Class
import Control.Monad.AWS.Matchers
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Reader (MonadReader (..))

-- |
--
-- @since 0.1.0.0
newtype MockAWS m a = MockAWS
  { forall (m :: * -> *) a. MockAWS m a -> m a
unMockAWS :: m a
  }
  deriving newtype
    ( forall a b. a -> MockAWS m b -> MockAWS m a
forall a b. (a -> b) -> MockAWS m a -> MockAWS m b
forall (m :: * -> *) a b.
Functor m =>
a -> MockAWS m b -> MockAWS m a
forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> MockAWS m a -> MockAWS m b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> MockAWS m b -> MockAWS m a
$c<$ :: forall (m :: * -> *) a b.
Functor m =>
a -> MockAWS m b -> MockAWS m a
fmap :: forall a b. (a -> b) -> MockAWS m a -> MockAWS m b
$cfmap :: forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> MockAWS m a -> MockAWS m b
Functor
    , forall a. a -> MockAWS m a
forall a b. MockAWS m a -> MockAWS m b -> MockAWS m a
forall a b. MockAWS m a -> MockAWS m b -> MockAWS m b
forall a b. MockAWS m (a -> b) -> MockAWS m a -> MockAWS m b
forall a b c.
(a -> b -> c) -> MockAWS m a -> MockAWS m b -> MockAWS m 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
forall {m :: * -> *}. Applicative m => Functor (MockAWS m)
forall (m :: * -> *) a. Applicative m => a -> MockAWS m a
forall (m :: * -> *) a b.
Applicative m =>
MockAWS m a -> MockAWS m b -> MockAWS m a
forall (m :: * -> *) a b.
Applicative m =>
MockAWS m a -> MockAWS m b -> MockAWS m b
forall (m :: * -> *) a b.
Applicative m =>
MockAWS m (a -> b) -> MockAWS m a -> MockAWS m b
forall (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c) -> MockAWS m a -> MockAWS m b -> MockAWS m c
<* :: forall a b. MockAWS m a -> MockAWS m b -> MockAWS m a
$c<* :: forall (m :: * -> *) a b.
Applicative m =>
MockAWS m a -> MockAWS m b -> MockAWS m a
*> :: forall a b. MockAWS m a -> MockAWS m b -> MockAWS m b
$c*> :: forall (m :: * -> *) a b.
Applicative m =>
MockAWS m a -> MockAWS m b -> MockAWS m b
liftA2 :: forall a b c.
(a -> b -> c) -> MockAWS m a -> MockAWS m b -> MockAWS m c
$cliftA2 :: forall (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c) -> MockAWS m a -> MockAWS m b -> MockAWS m c
<*> :: forall a b. MockAWS m (a -> b) -> MockAWS m a -> MockAWS m b
$c<*> :: forall (m :: * -> *) a b.
Applicative m =>
MockAWS m (a -> b) -> MockAWS m a -> MockAWS m b
pure :: forall a. a -> MockAWS m a
$cpure :: forall (m :: * -> *) a. Applicative m => a -> MockAWS m a
Applicative
    , forall a. a -> MockAWS m a
forall a b. MockAWS m a -> MockAWS m b -> MockAWS m b
forall a b. MockAWS m a -> (a -> MockAWS m b) -> MockAWS m b
forall {m :: * -> *}. Monad m => Applicative (MockAWS m)
forall (m :: * -> *) a. Monad m => a -> MockAWS m a
forall (m :: * -> *) a b.
Monad m =>
MockAWS m a -> MockAWS m b -> MockAWS m b
forall (m :: * -> *) a b.
Monad m =>
MockAWS m a -> (a -> MockAWS m b) -> MockAWS m 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
return :: forall a. a -> MockAWS m a
$creturn :: forall (m :: * -> *) a. Monad m => a -> MockAWS m a
>> :: forall a b. MockAWS m a -> MockAWS m b -> MockAWS m b
$c>> :: forall (m :: * -> *) a b.
Monad m =>
MockAWS m a -> MockAWS m b -> MockAWS m b
>>= :: forall a b. MockAWS m a -> (a -> MockAWS m b) -> MockAWS m b
$c>>= :: forall (m :: * -> *) a b.
Monad m =>
MockAWS m a -> (a -> MockAWS m b) -> MockAWS m b
Monad
    , forall a. IO a -> MockAWS m a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
forall {m :: * -> *}. MonadIO m => Monad (MockAWS m)
forall (m :: * -> *) a. MonadIO m => IO a -> MockAWS m a
liftIO :: forall a. IO a -> MockAWS m a
$cliftIO :: forall (m :: * -> *) a. MonadIO m => IO a -> MockAWS m a
MonadIO
    , MonadReader env
    )

instance (MonadIO m, MonadReader env m, HasMatchers env) => MonadAWS (MockAWS m) where
  sendEither :: forall a.
(AWSRequest a, Typeable a, Typeable (AWSResponse a)) =>
a -> MockAWS m (Either Error (AWSResponse a))
sendEither = forall (m :: * -> *) env a.
(MonadIO m, MonadReader env m, HasMatchers env, Typeable a,
 Typeable (AWSResponse a)) =>
a -> m (Either Error (AWSResponse a))
matchSend
  awaitEither :: forall a.
(AWSRequest a, Typeable a) =>
Wait a -> a -> MockAWS m (Either Error Accept)
awaitEither = forall (m :: * -> *) env a.
(MonadIO m, MonadReader env m, HasMatchers env, Typeable a) =>
Wait a -> a -> m (Either Error Accept)
matchAwait
  withAuth :: forall a. (AuthEnv -> MockAWS m a) -> MockAWS m a
withAuth = (forall a b. (a -> b) -> a -> b
$ AuthEnv
fakeAuthEnv)
  localEnv :: forall a. (Env -> Env) -> MockAWS m a -> MockAWS m a
localEnv Env -> Env
_ = forall a. a -> a
id

fakeAuthEnv :: AuthEnv
fakeAuthEnv :: AuthEnv
fakeAuthEnv =
  AuthEnv
    { $sel:accessKeyId:AuthEnv :: AccessKey
accessKeyId = AccessKey
"mock-aws-access-key-id"
    , $sel:secretAccessKey:AuthEnv :: Sensitive SecretKey
secretAccessKey = Sensitive SecretKey
"mock-aws-secret-key"
    , $sel:sessionToken:AuthEnv :: Maybe (Sensitive SessionToken)
sessionToken = forall a. a -> Maybe a
Just Sensitive SessionToken
"mock-aws-sessin-token"
    , $sel:expiration:AuthEnv :: Maybe ISO8601
expiration = forall a. Maybe a
Nothing
    }