packages feed

teardown-0.3.0.0: test/tasty/ComponentTest.hs

{-# LANGUAGE NoImplicitPrelude   #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
module ComponentTest where

import Protolude

import Test.Tasty       (TestTree, testGroup)
import Test.Tasty.HUnit

import Control.Monad.Component
import Data.IORef              (modifyIORef, newIORef, readIORef)


tests :: TestTree
tests =
  testGroup "component monad"
  [
    testCase "execute aggregation of components' teardown action works" $ do
      callCountRef <- newIORef (0 :: Int)

      let
        componentOne =
          buildComponentWithCleanup $ do
            return ((), ("one", modifyIORef callCountRef succ))

        componentTwo =
          buildComponentWithTeardown $ do
            t <- newTeardown "two" (modifyIORef callCountRef succ)
            return ((), t)

        componentThree =
          buildComponent $ return ()

        componentAction = do
          componentOne
          componentTwo
          componentThree

      result <- runComponentM "test application" componentAction
      void $ teardown result
      callCount <- readIORef callCountRef
      assertEqual "teardown action got called more than once"
                  2 callCount

  , testCase "component construction failure tears down previous components" $ do
      callCountRef <- newIORef (0 :: Int)

      let
        componentOne =
          buildComponentWithCleanup $ do
            return ((), ("one", modifyIORef callCountRef succ))

        componentTwo =
          buildComponent $ throwIO (ErrorCall "failing hard")

        componentThree =
          buildComponentWithTeardown $ do
            t <- newTeardown "three" (modifyIORef callCountRef succ)
            return ((), t)

        componentAction = do
          componentOne
          void componentTwo
          componentThree

      result <- try $ runComponentM "test application" componentAction
      case result of
        Left (ComponentStartupFailure [err]) -> do
          callCount <- readIORef callCountRef
          assertEqual "expected introduced error, got different one"
                      (Just $ ErrorCall "failing hard")
                      (fromException err)
          assertEqual "teardown action got called more than once" 1 callCount

        Left (err :: ComponentError) -> do
          assertFailure $ "expected ComponentStartupFailure exception, got instead: " <> show err

        Right _ ->
          assertFailure "expected error, but did not happen"

  , testCase "component construction multiple failure reported when using Applicative" $ do
      callCountRef <- newIORef (0 :: Int)

      let
        componentOne =
          buildComponentWithCleanup $ do
            return ((), ("one", modifyIORef callCountRef succ))

        componentTwo =
          buildComponent $ throwIO (ErrorCall "failing two")

        componentThree =
          buildComponent $ throwIO (ErrorCall "failing three")

        componentAction =
          componentOne
          *> componentTwo
          *> componentThree

      result <- try $ runComponentM "test application" componentAction
      case result of
        Left (ComponentStartupFailure [errTwo, errThree]) -> do
          callCount <- readIORef callCountRef
          assertEqual "expected introduced error, got different one"
                      (Just $ ErrorCall "failing two")
                      (fromException errTwo)
          assertEqual "expected introduced error, got different one"
                      (Just $ ErrorCall "failing three")
                      (fromException errThree)
          assertEqual "teardown action got called more than once" 1 callCount

        Left (err :: ComponentError) -> do
          assertFailure $ "expected ComponentStartupFailure exception, got instead: " <> show err

        Right _ ->
          assertFailure "expected error, but did not happen"

  , testCase "component construction allows MonadThrow calls" $ do
      callCountRef <- newIORef (0 :: Int)

      let
        componentOne =
          buildComponentWithCleanup $ do
            return ((), ("one", modifyIORef callCountRef succ))

        componentTwo =
          buildComponentWithCleanup $ do
            return ((), ("two", modifyIORef callCountRef succ))

        componentAction = do
          componentOne
          void $ throwM (ErrorCall "failing via MonadThrow")
          componentTwo

      result <- try $ runComponentM "test application" componentAction
      case result of
        Left (ComponentStartupFailure [err]) -> do
          callCount <- readIORef callCountRef
          assertEqual "expected introduced error, got different one"
                      (Just $ ErrorCall "failing via MonadThrow")
                      (fromException err)
          assertEqual "teardown action got called more than once" 1 callCount

        Left (err :: ComponentError) -> do
          assertFailure $ "expected ComponentStartupFailure exception, got instead: " <> show err

        Right _ ->
          assertFailure "expected error, but did not happen"

  , testCase "component construction allows fail calls" $ do
      callCountRef <- newIORef (0 :: Int)

      let
        componentOne =
          buildComponentWithCleanup $ do
            return ((), ("one", modifyIORef callCountRef succ))

        componentTwo =
          buildComponentWithCleanup $ do
            return ((), ("two", modifyIORef callCountRef succ))

        componentAction = do
          componentOne
          void $ fail "failing via fail"
          componentTwo

      result <- try $ runComponentM "test application" componentAction
      case result of
        Left (ComponentStartupFailure [err]) -> do
          callCount <- readIORef callCountRef
          assertEqual ("expected introduced error, got different one: " <> show err)
                      "failing via fail"
                      (maybe "" (\res ->
                                   case res of
                                     ComponentFailure errMsg ->
                                       errMsg
                                     _ ->
                                       "FAIL - Invalid ComponentException received")
                                (fromException err))
          assertEqual "teardown action got called more than once" 1 callCount

        Left (err :: ComponentError) -> do
          assertFailure $ "expected ComponentStartupFailure exception, got instead: " <> show err

        Right _ ->
          assertFailure "expected error, but did not happen"
  ]