packages feed

superevent-0.1.0.1: test/SuperEvent/Store/HasqlSpec.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module SuperEvent.Store.HasqlSpec (spec) where

import SuperEvent.Store.Hasql
import SuperEvent.Store.Types

import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.Trans
import Data.Aeson
import Data.Conduit
import Test.Hspec
import qualified Data.Text as T
import qualified Data.UUID.V4 as UUID
import qualified Data.Vector as V

spec :: Spec
spec =
    around withTempStore $
    do it "should work for simple reads" simpleWriteRead
       it "should work for simple reads to two streams" simpleWriteRead2
       it "should work for simple stream reads" simpleWriteReadStream
       it "should work for simple global reads" simpleWriteReadGlobal
       it "should work for simple subscriptions" simpleWriteSubStream

simpleWriteRead ::
    (EventStoreReader IO es, EventStoreWriter IO es)
    => es -> IO ()
simpleWriteRead store =
    do let payload :: T.Text
           payload = "Hello"
           entry guid =
               EventData guid (EventType "foo") (toJSON payload) (toJSON ())
           stream = StreamId "text"
       guid <- UUID.nextRandom
       writeRes <- writeToStream store stream EvAny (V.singleton $ entry guid)
       writeRes `shouldBe` WrSuccess
       evt <- readEvent store stream (nextEventNumber firstEventNumber)
       case evt of
         ErrFailed -> expectationFailure "Read failed"
         ErrValue re -> re_guid re `shouldBe` guid

simpleWriteRead2 ::
    (EventStoreReader IO es, EventStoreWriter IO es)
    => es -> IO ()
simpleWriteRead2 store =
    do let payload :: T.Text
           payload = "Hello"
           entry guid =
               EventData guid (EventType "foo") (toJSON payload) (toJSON ())
           stream = StreamId "text"
       guid <- UUID.nextRandom
       writeRes <- writeToStream store stream EvAny (V.singleton $ entry guid)
       writeRes `shouldBe` WrSuccess
       guid2 <- UUID.nextRandom
       writeRes2 <-
           writeToStream store (StreamId "bar") EvAny (V.singleton $ entry guid2)
       writeRes2 `shouldBe` WrSuccess
       evt <- readEvent store stream (nextEventNumber firstEventNumber)
       case evt of
         ErrFailed -> expectationFailure "Read failed"
         ErrValue re -> re_guid re `shouldBe` guid

simpleWriteReadStream ::
    (EventStoreReader IO es, EventStoreWriter IO es)
    => es -> IO ()
simpleWriteReadStream store =
    do let payload :: T.Text
           payload = "Hello"
           entry guid =
               EventData guid (EventType "foo") (toJSON payload) (toJSON ())
           stream = StreamId "text"
       events <-
           V.forM (V.fromList [0..99]) $ \(_ :: Int) ->
           do guid <- UUID.nextRandom
              pure (entry guid)
       writeRes <- writeToStream store stream EvAny events
       writeRes `shouldBe` WrSuccess
       let writtenGuids = V.map ed_guid events
           readHelper evtNum lim =
               V.map re_guid <$> readStreamEvents store stream evtNum lim RdForward
       evts <- readHelper firstEventNumber 10
       evts `shouldBe` V.take 10 writtenGuids

       evts2 <- readHelper (incrementTimes 11 firstEventNumber) 100
       evts2 `shouldBe` V.take 90 (V.drop 10 writtenGuids)

simpleWriteReadGlobal ::
    (EventStoreReader IO es, EventStoreWriter IO es)
    => es -> IO ()
simpleWriteReadGlobal store =
    do let payload :: T.Text
           payload = "Hello"
           entry guid =
               EventData guid (EventType "foo") (toJSON payload) (toJSON ())
           stream = StreamId "text"
       events <-
           V.forM (V.fromList [0..99]) $ \(_ :: Int) ->
           do guid <- UUID.nextRandom
              pure (entry guid)
       writeRes <- writeToStream store stream EvAny events
       writeRes `shouldBe` WrSuccess
       let writtenGuids = V.map ed_guid events
           readHelper pos lim =
               V.map (re_guid . snd) <$> readAllEvents store pos lim RdForward
       evts <- readHelper (GlobalPosition 0) 10
       evts `shouldBe` V.take 10 writtenGuids

       evts2 <- readHelper (GlobalPosition 11) 100
       evts2 `shouldBe` V.take 90 (V.drop 10 writtenGuids)

simpleWriteSubStream ::
    (EventStoreSubscriber IO es, EventStoreReader IO es, EventStoreWriter IO es)
    => es -> IO ()
simpleWriteSubStream store =
    do let payload :: T.Text
           payload = "Hello"
           entry guid =
               EventData guid (EventType "foo") (toJSON payload) (toJSON ())
           stream = StreamId "text"
       outVar <- newTVarIO []
       poller <-
           async $
           do let consumer =
                      do val <- await
                         case val of
                           Nothing ->
                               do liftIO $ putStrLn "Consumer was terminated"
                                  pure ()
                           Just v ->
                               do liftIO $ atomically $ modifyTVar' outVar ((:) v)
                                  consumer
              subscribeTo store (SubscriptionConfig SspBeginning stream) $$ consumer
       events <-
           V.forM (V.fromList [0..9999]) $ \(_ :: Int) ->
           do guid <- UUID.nextRandom
              pure (entry guid)
       writeRes <- writeToStream store stream EvAny events
       writeRes `shouldBe` WrSuccess
       let writtenGuids = V.map ed_guid events
       finalResult <-
           atomically $
           do vals <- readTVar outVar
              when (length vals < 9999) retry
              pure (V.map re_guid $ V.reverse $ V.fromList vals)
       uninterruptibleCancel poller
       finalResult `shouldBe` writtenGuids