packages feed

rdkafka-hs-1.1.0.0: test-integration/Kafka/Integration/RuntimeContractTest.hs

{-# OPTIONS_GHC -Wno-deprecations #-}

module Kafka.Integration.RuntimeContractTest (tests) where

import Control.Concurrent (rtsSupportsBoundThreads)
import Control.Exception (bracket, finally, throwIO)
import Data.IORef (modifyIORef', newIORef, readIORef, writeIORef)
import Kafka.Consumer
import Kafka.TestEnv
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))

tests :: TestEnv -> TestTree
tests env =
  testGroup
    "runtime-contract"
    [ testCase "RTS capability is observed by async construction" $ do
        result <- newAssignedConsumer (base "async")
        if rtsSupportsBoundThreads
          then either throwIO destroyConsumer result
          else case result of
            Left (KafkaBadSpecification message) -> assertBool (show message) (message == "Async callback polling requires -threaded; select CallbackPollModeSync for a nonthreaded runtime.")
            Left err -> assertFailure (show err)
            Right consumer -> destroyConsumer consumer >> assertFailure "nonthreaded async construction silently succeeded",
      testCase "explicit async construction checks the runtime" $ do
        result <- newAssignedConsumer (base "explicit-async" <> callbackPollMode CallbackPollModeAsync)
        if rtsSupportsBoundThreads
          then either throwIO destroyConsumer result
          else case result of
            Left (KafkaBadSpecification _) -> pure ()
            Left err -> assertFailure (show err)
            Right consumer -> destroyConsumer consumer >> assertFailure "explicit nonthreaded async construction silently succeeded",
      testCase "sync subscription delivers an exact record" $
        withTopic env "runtime-sync" 1 $ \topic producer -> do
          produceTo producer topic 0 "runtime-record"
          events <- newIORef []
          let props = base "sync" <> callbackPollMode CallbackPollModeSync <> setCallback (rebalanceCallback (\_ event -> modifyIORef' events (<> [event])))
          bracket (newConsumer props (topics [topic] <> offsetReset Earliest) >>= either throwIO pure) releaseConsumer $ \consumer -> do
            pollConsumerEvents consumer (Just (Timeout 10000))
            readIORef events >>= (@?= [RebalanceBeforeAssign [(topic, PartitionId 0)], RebalanceAssign [(topic, PartitionId 0)]])
            message <- pollMessage consumer (Timeout 0) >>= either throwIO pure
            (crTopic message, crPartition message, crOffset message, crValue message) @?= (topic, PartitionId 0, Offset 0, Just "runtime-record"),
      testCase "deprecated sync runner closes after an exact record" $
        withTopic env "runtime-runner" 1 $ \topic producer -> do
          produceTo producer topic 0 "runner-record"
          retained <- newIORef Nothing
          let cleanup = readIORef retained >>= mapM_ destroyConsumer
          flip finally cleanup $ do
            result <- runConsumer (base "runner" <> callbackPollMode CallbackPollModeSync) (topics [topic] <> offsetReset Earliest) $ \consumer -> do
              writeIORef retained (Just consumer)
              fmap (fmap (\message -> (crOffset message, crValue message))) (pollMessage consumer (Timeout 10000))
            result @?= Right (Offset 0, Just "runner-record")
            readIORef retained >>= maybe (assertFailure "runner did not invoke its handler") (\consumer -> consumerClosed consumer >>= (@?= True))
    ]
  where
    base name = brokersList (teBrokers env) <> groupId (makeGroupId env ("runtime-" <> name)) <> noAutoCommit