{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeSynonymInstances #-} module Test where import Control.Arrow import Control.Lens import Control.Monad.Catch hiding (bracket) import Control.Monad.IO.Class import Control.Monad.IO.Unlift import Control.Monad.Reader import Control.Monad.State import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as ByteString.Lazy import Data.IORef import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Text (Text) import Network.HTTP.Client import Security.AccessTokenProvider.Internal.Types import Security.AccessTokenProvider.Internal.Types.Severity runTestStack :: TestState -> TestStack a -> IO (a, TestState) runTestStack testState m = do s <- newIORef testState a <- m & (_runTestStack >>> flip runReaderT s) (a,) <$> readIORef s evalTestStack :: TestState -> TestStack a -> IO a evalTestStack testState m = do s <- newIORef testState m & (_runTestStack >>> flip runReaderT s) newtype TestStack a = TestStack { _runTestStack :: ReaderT (IORef TestState) IO a } deriving ( Functor , Applicative , Monad , MonadThrow , MonadCatch , MonadMask , MonadReader (IORef TestState) , MonadIO ) instance MonadUnliftIO TestStack where askUnliftIO = do (UnliftIO u) <- TestStack askUnliftIO pure $ UnliftIO (\ (TestStack m) -> u m) data TestState = TestState { _testStateFilesystem :: Map FilePath ByteString , _testStateEnvironment :: Map Text Text , _testStateHttpRequests :: [Request] , _testStateHttpResponse :: Maybe (Response ByteString.Lazy.ByteString) , _testStateLog :: [(Severity, Text)] } makeFieldsNoPrefix ''TestState instance MonadState TestState TestStack where get = do envRef <- ask liftIO $ readIORef envRef put s = do envRef <- ask liftIO $ writeIORef envRef s mockBackend :: Backend TestStack mockBackend = Backend { backendHttp = mockBackendHttp , backendEnv = mockBackendEnv , backendFilesystem = mockBackendFilesystem , backendLog = mockBackendLog } mockBackendHttp :: BackendHttp TestStack mockBackendHttp = BackendHttp { httpRequestExecute = \ request -> do testStateHttpRequests %= (request :) maybeResponse <- gets (view testStateHttpResponse) case maybeResponse of Just response -> pure response Nothing -> error "FIXME" } mockBackendEnv :: BackendEnv TestStack mockBackendEnv = BackendEnv { envLookup = \ name -> Map.lookup name <$> gets (view testStateEnvironment) } mockBackendFilesystem :: BackendFilesystem TestStack mockBackendFilesystem = BackendFilesystem { fileRead = \ filename -> do fs <- gets (view testStateFilesystem) case Map.lookup filename fs of Just bytes -> pure bytes Nothing -> error "FIXME: file not found" } mockBackendLog :: BackendLog TestStack mockBackendLog = BackendLog { logAddNamespace = \ _namespace -> id , logMsg = \ severity msg -> modify (testStateLog %~ ((severity, msg) :)) }