packages feed

glue-common-0.7.0: test/Glue/TypesSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}

module Glue.TypesSpec (spec) where

import qualified Data.HashMap.Strict       as M
import qualified Data.HashSet              as S
import           Glue.Types
import           Test.Hspec
import           Test.QuickCheck
import           Test.QuickCheck.Instances ()
import           UnliftIO.Exception        (Exception, SomeException (..),
                                            fromException, throwIO)
import           UnliftIO.MVar             (newMVar)

data TypesTestException = TypesTestException
  deriving (Eq, Show)

instance Exception TypesTestException

spec :: Spec
spec = do
  describe "multiGetToBasic and basicToMultiGet" $ do
    it "basicToMultiGet lifts a BasicService over a HashSet of requests" $
      property $ \(requests :: S.HashSet Int) -> do
        let basic :: BasicService IO Int Int
            basic x = pure (x * 2)
            multi = basicToMultiGet basic
            expected = M.fromList [ (k, k * 2) | k <- S.toList requests ]
        multi requests `shouldReturn` expected

    it "multiGetToBasic looks up a single key from a MultiGetService" $
      property $ \(resp :: MultiGetResponse Int Int, key :: Int) -> do
        let multi :: MultiGetService IO Int Int
            multi _ = pure resp
            basic = multiGetToBasic multi
        basic key `shouldReturn` M.lookup key resp

    it "multiGetToBasic . basicToMultiGet round-trips as Just" $
      property $ \(key :: Int) -> do
        let basic :: BasicService IO Int Int
            basic x = pure (x + 10)
            roundTripped = multiGetToBasic (basicToMultiGet basic)
        roundTripped key `shouldReturn` Just (key + 10)

  describe "getResult" $ do
    it "returns the value when ResultVar holds Right" $
      property $ \(val :: Int) -> do
        var <- newMVar (Right val)
        getResult var `shouldReturn` val

    it "rethrows the exception when ResultVar holds Left" $ do
      var <- newMVar (Left (SomeException TypesTestException) :: Either SomeException Int)
      getResult var `shouldThrow` (== TypesTestException)

  describe "makeCall" $ do
    it "returns Right on a successful multi-get call" $
      property $ \(requests :: S.HashSet Int) -> do
        let expected = M.fromList [ (k, k * 3) | k <- S.toList requests ]
            service :: MultiGetService IO Int Int
            service _ = pure expected
        result <- makeCall service requests
        case result of
          Right actual -> actual `shouldBe` expected
          Left err     -> expectationFailure $ "Unexpected failure: " <> show err

    it "captures synchronous exceptions in Left" $ do
      let service :: MultiGetService IO Int Int
          service _ = throwIO TypesTestException
      result <- makeCall service (S.singleton 1)
      case result of
        Left err -> fromException err `shouldBe` Just TypesTestException
        Right v  -> expectationFailure $ "Expected Left exception, got Right: " <> show v