packages feed

nakadi-client-0.5.0.0: tests/Network/Nakadi/Subscriptions/Processing/Test.hs

{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}


-- This test tests the following the high-level subscription consumption.

module Network.Nakadi.Subscriptions.Processing.Test where

import           ClassyPrelude

import           Control.Lens
import           Data.Aeson
import           Data.Conduit
import           Data.Maybe                  (fromJust)
import           Network.Nakadi
import qualified Network.Nakadi.Lenses       as L
import           Network.Nakadi.Tests.Common
import           Test.Tasty
import           Test.Tasty.HUnit

testSubscriptionsProcessing :: Config App -> TestTree
testSubscriptionsProcessing conf = testGroup "Processing"
  [ testCase "SubscriptionProcessing" (testSubscriptionHighLevelProcessing conf)
  ]

data ConsumptionDone = ConsumptionDone deriving (Show, Typeable)


instance Exception ConsumptionDone

testSubscriptionHighLevelProcessing :: Config App -> Assertion
testSubscriptionHighLevelProcessing conf = runApp . runNakadiT conf $ do
  counter <- newIORef 0
  events <- sequence $
    replicate nEvents genMyDataChangeEvent :: NakadiT App App [DataChangeEvent Foo]
  publishAndConsume events counter `catch` \ (_exn :: ConsumptionDone) -> pure ()
  eventsRead <- readIORef counter
  liftIO $ nEvents @=? eventsRead

  where before :: MonadNakadi App m => m SubscriptionId
        before = do
          recreateEvent myEventTypeName myEventType
          subscription <- subscriptionCreate Subscription
            { _id = Nothing
            , _owningApplication = "test-suite"
            , _eventTypes = [myEventTypeName]
            , _consumerGroup = Nothing -- ??
            , _createdAt = Nothing
            , _readFrom = Just SubscriptionPositionEnd
            , _initialCursors = Nothing
            }
          pure . fromJust $ subscription^.L.id

        after :: MonadNakadi App m => SubscriptionId -> m ()
        after subscriptionId = do
          subscriptionDelete subscriptionId
          eventTypeDelete myEventTypeName `catch` (ignoreExnNotFound ())

        nEvents :: Int
        nEvents = 100

        publishAndConsume :: (ToJSON a, FromJSON a)
                          => [DataChangeEvent a]
                          -> IORef Int
                          -> NakadiT App App ()
        publishAndConsume events counter =
          bracket before after $ \ subscriptionId -> do
          let n = length events
          void . async $ delayedPublish Nothing events
          subscriptionProcess (Just consumeParameters) subscriptionId $
            \ (batch :: SubscriptionEventStreamBatch (DataChangeEvent Foo)) -> do
              -- Make sure that we can use Nakadi from within the high
              -- level processing callback:
              void $ registryPartitionStrategies
              let eventsReceived = fromMaybe mempty (batch^.L.events)
              modifyIORef counter (+ (length eventsReceived))
              eventsRead <- readIORef counter
              when (n == eventsRead) $ throwM ConsumptionDone

        consumeParameters = defaultConsumeParameters
                            & setBatchFlushTimeout 1
                            & setBatchLimit 1

testSubscriptionConduitProcessing :: Config App -> Assertion
testSubscriptionConduitProcessing conf = runApp . runNakadiT conf $ do
  counter <- newIORef 0
  events <- sequence $
    replicate nEvents genMyDataChangeEvent :: NakadiT App App [DataChangeEvent Foo]
  publishAndConsume events counter `catch` \ (_exn :: ConsumptionDone) -> pure ()
  eventsRead <- readIORef counter
  liftIO $ nEvents @=? eventsRead

  where before :: MonadNakadi App m => m SubscriptionId
        before = do
          recreateEvent myEventTypeName myEventType
          subscription <- subscriptionCreate Subscription
            { _id = Nothing
            , _owningApplication = "test-suite"
            , _eventTypes = [myEventTypeName]
            , _consumerGroup = Nothing -- ??
            , _createdAt = Nothing
            , _readFrom = Just SubscriptionPositionEnd
            , _initialCursors = Nothing
            }
          pure . fromJust $ subscription^.L.id

        after :: MonadNakadi App m => SubscriptionId -> m ()
        after subscriptionId = do
          subscriptionDelete subscriptionId
          eventTypeDelete myEventTypeName `catch` (ignoreExnNotFound ())

        nEvents :: Int
        nEvents = 100

        publishAndConsume :: (ToJSON a, FromJSON a)
                          => [DataChangeEvent a]
                          -> IORef Int
                          -> NakadiT App App ()
        publishAndConsume events counter =
          bracket before after $ \ subscriptionId -> do
          let n = length events
          void . async $ delayedPublish Nothing events
          subscriptionProcessConduit (Just consumeParameters) subscriptionId $ do
            awaitForever $ \ (batch :: SubscriptionEventStreamBatch (DataChangeEvent Foo)) -> do
              -- Make sure that we can use Nakadi via lifting directly
              -- from within the Conduit.
              void $ lift registryPartitionStrategies
              let eventsReceived = fromMaybe mempty (batch^.L.events)
              modifyIORef counter (+ (length eventsReceived))
              eventsRead <- readIORef counter
              when (n == eventsRead) $ throwM ConsumptionDone
              yield batch

        consumeParameters = defaultConsumeParameters
                            & setBatchFlushTimeout 1
                            & setBatchLimit 1