atelier-core-0.1.0.0: test/Unit/Atelier/Effects/ChanSpec.hs
module Unit.Atelier.Effects.ChanSpec (spec_Chan) where
import Effectful (IOE, runEff)
import Effectful.Timeout (Timeout, runTimeout)
import Hedgehog (forAll, (===))
import Test.Hspec (Spec, describe, it, shouldBe)
import Test.Hspec.Hedgehog (hedgehog)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Atelier.Effects.Chan (Chan, dupChan, newChan, readChan, readChanBatched, runChan, writeChan)
import Atelier.Time (Millisecond, Second)
spec_Chan :: Spec
spec_Chan = do
describe "Basic Operations" do
it "writeChan then readChan roundtrips a value" do
result <- runChanTest $ do
(inChan, outChan) <- newChan
writeChan inChan (42 :: Int)
readChan outChan
result `shouldBe` 42
it "preserves FIFO order" $ hedgehog do
xs <- forAll $ Gen.list (Range.linear 0 50) (Gen.int Range.linearBounded)
result <- liftIO $ runChanTest $ do
(inChan, outChan) <- newChan
traverse_ (writeChan inChan) xs
replicateM (length xs) (readChan outChan)
result === xs
it "dupChan creates an independent reader that receives the same messages" do
result <- runChanTest $ do
(inChan, outChan1) <- newChan
outChan2 <- dupChan inChan
writeChan inChan (42 :: Int)
v1 <- readChan outChan1
v2 <- readChan outChan2
pure (v1, v2)
result `shouldBe` (42, 42)
describe "readChanBatched" do
describe "when items fill the batch before timeout" do
it "returns a full batch" do
result <- runChanTest $ do
(inChan, outChan) <- newChan
traverse_ (writeChan inChan) [1, 2, 3 :: Int]
readChanBatched (1 :: Second) 3 outChan
result `shouldBe` (1 :| [2, 3])
it "caps at batchSize even when more items are available" $ hedgehog do
batchSize <- forAll $ Gen.int (Range.linear 1 20)
extra <- forAll $ Gen.int (Range.linear 1 10)
let n = batchSize + extra
result <- liftIO $ runChanTest $ do
(inChan, outChan) <- newChan
traverse_ (writeChan inChan) [1 .. n]
readChanBatched (1 :: Second) batchSize outChan
length result === batchSize
describe "when timeout fires before batch is full" do
it "returns a singleton when only one item is in the channel" do
result <- runChanTest $ do
(inChan, outChan) <- newChan
writeChan inChan (1 :: Int)
readChanBatched (1 :: Millisecond) 5 outChan
result `shouldBe` (1 :| [])
it "returns a partial batch" do
result <- runChanTest $ do
(inChan, outChan) <- newChan
writeChan inChan (1 :: Int)
writeChan inChan 2
readChanBatched (1 :: Millisecond) 5 outChan
result `shouldBe` (1 :| [2])
--------------------------------------------------------------------------------
-- Test Helpers
--------------------------------------------------------------------------------
runChanTest :: Eff '[Chan, Timeout, IOE] a -> IO a
runChanTest = runEff . runTimeout . runChan