packages feed

planb-token-introspection-0.1.3.0: tests/Network/PlanB/Introspection/Test.hs

{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE TypeSynonymInstances       #-}

module Network.PlanB.Introspection.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 qualified Data.ByteString.Lazy          as ByteString.Lazy
import           Data.IORef
import           Data.Map                       ( Map )
import qualified Data.Map                      as Map
import           Data.Text                      ( Text )
import           Network.HTTP.Client
import           Network.PlanB.Introspection.Internal

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 { _testStateHttpRequests :: [Request]
            , _testStateHttpResponse :: Maybe (Response ByteString.Lazy.ByteString)
            , _testStateEnvironment  :: Map Text Text
            }

makeFieldsNoPrefix ''TestState

instance MonadState TestState TestStack where
  get = do
    envRef <- ask
    liftIO $ readIORef envRef
  put s = do
    envRef <- ask
    liftIO $ writeIORef envRef s

mockHttpRequestExecute
  :: Request -> TestStack (Response ByteString.Lazy.ByteString)
mockHttpRequestExecute request = do
  testStateHttpRequests %= (request :)
  maybeResponse <- gets (view testStateHttpResponse)
  case maybeResponse of
    Just response -> pure response
    Nothing       -> error "FIXME"

mockHttpBackend :: BackendHttp TestStack
mockHttpBackend = BackendHttp {httpRequestExecute = mockHttpRequestExecute}

mockEnvBackend :: BackendEnv TestStack
mockEnvBackend = BackendEnv {envLookup = mockEnvLookup}

mockEnvLookup :: Text -> TestStack (Maybe Text)
mockEnvLookup name = do
  environment <- gets (view testStateEnvironment)
  pure $ Map.lookup name environment

mockBackend :: Backend TestStack
mockBackend =
  Backend {backendHttp = mockHttpBackend, backendEnv = mockEnvBackend}

makeTestIntrospector :: TestStack (TokenIntrospector TestStack)
makeTestIntrospector = newWithBackend mockBackend "https://localhost"