packages feed

faktory-1.1.2.7: tests/Faktory/Test.hs

module Faktory.Test
  ( module X
  , workerTestCase
  , workerTestCaseWith

    -- * Lower-level
  , withProducer
  , withWorker
  , startWorker
  , haltWorker
  )
where

import Faktory.Prelude as X

import Control.Monad.IO.Class as X (MonadIO (..))
import Faktory.Job as X
import Faktory.Producer as X
import Test.Hspec as X

import Control.Concurrent (threadDelay)
import Control.Concurrent.Async
import Control.Concurrent.MVar
import Faktory.Settings
import Faktory.Worker

workerTestCase :: HasCallStack => (Producer -> IO ()) -> IO [Job Text]
workerTestCase = workerTestCaseWith id

workerTestCaseWith
  :: HasCallStack
  => (WorkerSettings -> WorkerSettings)
  -> (Producer -> IO ())
  -> IO [Job Text]
workerTestCaseWith editSettings =
  fmap (\(x, _, _) -> x) . withWorker editSettings . withProducer

withProducer :: (Producer -> IO a) -> IO a
withProducer f = bracket newProducerEnv closeProducer f

withWorker
  :: HasCallStack
  => (WorkerSettings -> WorkerSettings)
  -> IO a
  -> IO ([Job Text], [SomeException], a)
withWorker editSettings f = do
  a <- startWorker editSettings
  result <- f
  (processed, failed) <- haltWorker a
  pure (processed, failed, result)

startWorker
  :: HasCallStack
  => (WorkerSettings -> WorkerSettings)
  -> IO (Async ([Job Text], [SomeException]))
startWorker editSettings = do
  withProducer $ void . flush
  settings <- envSettings
  workerSettings <- editSettings <$> envWorkerSettings
  async $ do
    processedJobs <- newMVar []
    failedJobs <- newMVar []

    let workerSettings' =
          workerSettings
            { settingsOnFailed = \ex ->
                modifyMVar_ failedJobs $ pure . (ex :)
            }

    runWorker settings workerSettings' $ \faktoryJob -> do
      let job = jobArg faktoryJob
      when (job == "WAIT") $ threadDelay 3000000
      modifyMVar_ processedJobs $ pure . (faktoryJob :)
      when (job == "BOOM") $ throw $ userError "BOOM"
      when (job == "HALT") $ throw WorkerHalt

    (,)
      <$> readMVar processedJobs
      <*> readMVar failedJobs

haltWorker :: Async a -> IO a
haltWorker a = do
  withProducer $ \producer -> void $ perform @Text mempty producer "HALT"
  wait a