-- | Helpers for testing code that sends messages to Kafka.
module Kafka.Test
  ( stub,
  )
where

import qualified Data.IORef
import qualified Expect
import qualified GHC.Stack as Stack
import qualified Kafka
import qualified Kafka.Internal as Internal
import qualified Platform

-- | Can be used to test your Kafka writer.
-- yields a mock Kafka handler, and returns an expectation wrapping a list of
-- messages that would have been written if the handler was real
stub ::
  Stack.HasCallStack =>
  (Internal.Handler -> Expect.Expectation) ->
  Expect.Expectation' (List Kafka.Msg)
stub :: (Handler -> Expectation) -> Expectation' (List Msg)
stub Handler -> Expectation
stubbed = do
  IORef (List Msg)
logRef <- IO (IORef (List Msg)) -> Expectation' (IORef (List Msg))
forall a. IO a -> Expectation' a
Expect.fromIO (List Msg -> IO (IORef (List Msg))
forall a. a -> IO (IORef a)
Data.IORef.newIORef [])
  Handler
doAnything <- IO Handler -> Expectation' Handler
forall a. IO a -> Expectation' a
Expect.fromIO IO Handler
Platform.doAnythingHandler
  let sendStub :: Msg -> Task e ()
sendStub = \Msg
msg' -> do
        IORef (List Msg) -> (List Msg -> List Msg) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
Data.IORef.modifyIORef' IORef (List Msg)
logRef (\List Msg
prev -> Msg
msg' Msg -> List Msg -> List Msg
forall a. a -> [a] -> [a]
: List Msg
prev)
          IO () -> (IO () -> IO (Result e ())) -> IO (Result e ())
forall a b. a -> (a -> b) -> b
|> (() -> Result e ()) -> IO () -> IO (Result e ())
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map () -> Result e ()
forall error value. value -> Result error value
Ok
          IO (Result e ()) -> (IO (Result e ()) -> Task e ()) -> Task e ()
forall a b. a -> (a -> b) -> b
|> Handler -> IO (Result e ()) -> Task e ()
forall e a. Handler -> IO (Result e a) -> Task e a
Platform.doAnything Handler
doAnything
  let mockHandler :: Handler
mockHandler =
        Handler :: (Task Never () -> Msg -> Task Text ())
-> (Msg -> Task Text ()) -> Handler
Internal.Handler
          { sendAsync :: Task Never () -> Msg -> Task Text ()
Internal.sendAsync = \Task Never ()
_ -> Msg -> Task Text ()
forall e. Msg -> Task e ()
sendStub,
            sendSync :: Msg -> Task Text ()
Internal.sendSync = Msg -> Task Text ()
forall e. Msg -> Task e ()
sendStub
          }
  (forall e a. (Handler -> Task e a) -> Task e a)
-> (Handler -> Expectation) -> Expectation
forall arg.
(forall e a. (arg -> Task e a) -> Task e a)
-> (arg -> Expectation) -> Expectation
Expect.around (\Handler -> Task e a
f -> Handler -> Task e a
f Handler
mockHandler) ((HasCallStack => Handler -> Expectation) -> Handler -> Expectation
forall a. HasCallStack => (HasCallStack => a) -> a
Stack.withFrozenCallStack HasCallStack => Handler -> Expectation
Handler -> Expectation
stubbed)
  IO (List Msg) -> Expectation' (List Msg)
forall a. IO a -> Expectation' a
Expect.fromIO (IORef (List Msg) -> IO (List Msg)
forall a. IORef a -> IO a
Data.IORef.readIORef IORef (List Msg)
logRef)
    Expectation' (List Msg)
-> (Expectation' (List Msg) -> Expectation' (List Msg))
-> Expectation' (List Msg)
forall a b. a -> (a -> b) -> b
|> (List Msg -> List Msg)
-> Expectation' (List Msg) -> Expectation' (List Msg)
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map List Msg -> List Msg
forall a. List a -> List a
List.reverse