couchdb-enumerator-0.3.5: test/Database/CouchDB/Enumerator/Test/Util.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Database.CouchDB.Enumerator.Test.Util (
CouchT
, testCouch
, testCouchCase
, testCouchProperty
, isSubmapOf
, assertStr
, assertObjMember
, checkError
, assertRecvError
, checkRevision
, checkLoad
, clearObject
, ArbitraryObject(..)
)where
import Control.Applicative
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Exception.Lifted as E
import Control.Monad
import Control.Monad.Trans.Reader
import qualified Data.Aeson as A
import qualified Data.HashMap.Lazy as M
import Data.Maybe (fromJust)
import Database.CouchDB.Enumerator
import qualified Data.Text as T
import qualified Data.Vector as V
import Test.Framework (Test)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.Framework.Providers.HUnit (testCase)
import Test.QuickCheck
import Test.QuickCheck.Monadic
import Test.HUnit hiding (Test, path)
type CouchT m a = ReaderT CouchConnection m a
testCouch :: CouchT IO a -> IO ()
testCouch c = withCouchConnection "localhost" 5984 "testcouchenum" (runReaderT c) >> return ()
testCouchCase :: String -> CouchT IO a -> Test
testCouchCase s c = testCase s $ testCouch c
testCouchProperty :: (Show a, Arbitrary a) => String -> (Int,Int) -> ([a] -> CouchT IO b) -> Test
testCouchProperty s i f = testProperty s $ monadicIO $ do
len <- pick $ choose i
lst <- pick $ vector len
run $ testCouch $ f lst
-- | Assert that the value is a string, and check that it matches the given string
assertStr :: T.Text -> A.Value -> Assertion
assertStr t (A.String t') = unless (t == t') $ assertFailure $ "strings are not equal. expecting "
++ T.unpack t ++ " received " ++ T.unpack t'
assertStr _ _ = assertFailure "expecting a JSON string"
member :: T.Text -> A.Object -> Bool
member k o = M.lookup k o /= Nothing
isSubmapOf :: A.Object -> A.Object -> Bool
isSubmapOf x y = 0 == M.size (M.difference x y)
-- | Assert that the given key exists, and the value matches the given assertion
assertObjMember :: T.Text -> (A.Value -> Assertion) -> A.Object -> Assertion
assertObjMember t f x = do
assertBool (T.unpack t ++ " is missing") $ member t x
f $ fromJust $ M.lookup t x
-- | Check an action for a couch error
checkError :: MonadBaseControl IO m => Maybe Int -> m () -> m ()
checkError code m = E.catch m handler
where handler e@(CouchError c _) = unless (c == code) $ E.throwIO e
-- | Expect a couch error with the given code
assertRecvError :: (MonadIO m, MonadBaseControl IO m) => Maybe Int -> m a -> m ()
assertRecvError code v = checkError code $ v >> liftIO (assertFailure "was expecting a couch error")
-- | Check that an object in the database matches the given value.
checkLoad :: String -> A.Object -> CouchT IO ()
checkLoad n obj = do
obj' <- couchGet n []
lift $ assertBool "returned object does not match" $ isSubmapOf obj obj'
checkRevision :: String -> Revision -> CouchT IO ()
checkRevision n r = do
r' <- couchRev n
lift $ assertBool "returned revision does not match" $ r == r'
-- | Delete the given object, useful for the start of a test
clearObject :: String -> CouchT IO ()
clearObject n = checkError (Just 404) go
where go = do rev <- couchRev n
couchDelete n rev
newtype ArbitraryObject = ArbitraryObject { unArbObject :: A.Object }
deriving (Show,Eq,A.FromJSON,A.ToJSON)
instance Arbitrary T.Text where
arbitrary = liftM T.pack $ listOf $ elements $ ['a'..'z'] ++ ['A'..'Z'] ++ " 1234567890!@#$%^&*()+|"
shrink "" = []
shrink x = [T.tail x]
arbBaseValue :: Gen A.Value
arbBaseValue = oneof [ A.String <$> arbitrary
, A.toJSON <$> (arbitrary :: Gen Integer)
, A.Bool <$> arbitrary
, return A.Null
]
arbObject :: Bool -> Gen A.Object
arbObject onlyBase = do nkeys <- choose (3,15)
keys <- vectorOf nkeys arbitrary
vals <- vectorOf nkeys $ if onlyBase
then arbBaseValue
else frequency [ (8, arbBaseValue)
, (1, A.Object <$> arbObject False)
, (1, A.Array <$> arbArrayOfObj)
]
return $ M.fromList $ zip keys vals
arbArrayOfObj :: Gen A.Array
arbArrayOfObj = do len <- choose (1,20)
vals <- vectorOf len (A.Object <$> arbObject False)
return $ V.fromList vals
instance Arbitrary ArbitraryObject where
arbitrary = ArbitraryObject <$> arbObject True