packages feed

mealstrom-0.0.1.0: test/Exception.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}

{-|
Module      : Exception
Description : Test that illegal actions throw an exception and that they are caught.
Copyright   : (c) Max Amanshauser, 2016
License     : MIT
Maintainer  : max@lambdalifting.org
-}

module Exception (runExceptionTests) where

import Test.Tasty
import Test.Tasty.HUnit

import Control.Concurrent
import Data.Aeson
import Data.Hashable
import Data.Maybe                  (isNothing)
import Data.Text                   as Text
import Data.Typeable
import Data.UUID
import Data.UUID.V4
import GHC.Generics

import Mealstrom
import Mealstrom.FSMStore
import Mealstrom.WALStore
import Mealstrom.PostgresJSONStore as PGJSON
import Mealstrom.MemoryStore       as MemStore


type MyKey    = UUID
data MyState  = MyState1  | MyState2  deriving (Eq,Show,Generic,Typeable,ToJSON,FromJSON)
data MyEvent  = MyEvent1  | MyEvent2  deriving (Eq,Show,Generic,Typeable,ToJSON,FromJSON)
data MyAction = MyAction1 | MyAction2 deriving (Eq,Show,Generic,Typeable,ToJSON,FromJSON)

instance MealyInstance MyKey MyState MyEvent MyAction
myTransition :: (MyState,MyEvent) -> (MyState,[MyAction])
myTransition (MyState1,MyEvent1) =
    (MyState2,[MyAction1])

myEffects :: Msg MyAction -> IO Bool
myEffects _a = do
    putStrLn "Action completed"
    return True


runExceptionTests :: String -> TestTree
runExceptionTests c = testGroup "ExceptionFSM" [
    testCase "ExceptionPG" (runTest (PGJSON.mkStore c)),
    testCase "ExceptionMem" (runTest (MemStore.mkStore :: Text -> IO (MemoryStore MyKey MyState MyEvent MyAction)))
    ]

runTest :: (FSMStore st MyKey MyState MyEvent MyAction,
            WALStore st MyKey) => (Text -> IO st) -> IO ()
runTest c = do
    st       <- c "ExceptionTest"
    let t     = FSMTable myTransition myEffects
    let myFSM = FSMHandle st st t 90 3
    firstId <- nextRandom

    res1 <- post myFSM firstId MyState1
    res2 <- post myFSM firstId MyState1

    assertBool "Insert Instance once"                 res1
    assertBool "Insert Instance again and fail" $ not res2

    -- Technically the following assertions are not concerned with exceptions, but fine, whatever
    secondId <- nextRandom

    res3 <- get myFSM secondId
    assertBool "Get non-existing instance" $ isNothing res3

    res4 <- mkMsgs [MyEvent1] >>= patch myFSM secondId
    assertBool "Patch non-existing instance" $ not res4