glue-core-0.7.0: test/Glue/CachingSpec.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Glue.CachingSpec (spec) where
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import Data.IORef
import Glue.Caching
import Glue.Types
import Test.Hspec
import Test.QuickCheck
import Test.QuickCheck.Instances ()
import UnliftIO.Exception (Exception, throwIO)
newtype CachingTestException = CachingTestException Int
deriving (Eq, Show)
instance Exception CachingTestException
type TestResp = MultiGetResponse Int Int
spec :: Spec
spec = do
describe "cacheWithBasic" $ do
it "For a second request, the value comes from the cache" $
property $ \(request :: Int, result :: Int) -> do
ref <- newIORef (0 :: Int)
let service req = do
if req == request then pure () else throwIO (CachingTestException (-1))
callCount <- atomicModifyIORef' ref (\c -> (c + 1, c + 1))
if callCount > 1 then throwIO (CachingTestException callCount) else pure result
cache <- newIORef M.empty
let lookupWith r = M.lookup r <$> readIORef cache
let insertWith req resp = atomicModifyIORef' cache (\c -> (M.insert req resp c, ()))
let cachedService = cacheWithBasic lookupWith insertWith service
cachedService request `shouldReturn` result
cachedService request `shouldReturn` result
describe "cacheWithMulti" $ do
it "For a second request, the value comes from the cache" $
property $ \(result :: TestResp) -> do
let request = S.fromList $ M.keys result
ref <- newIORef (0 :: Int)
let service req = do
if req == request then pure () else throwIO (CachingTestException (-1))
callCount <- atomicModifyIORef' ref (\c -> (c + 1, c + 1))
if callCount > 1 then throwIO (CachingTestException callCount) else pure result
cache <- newIORef M.empty
let lookupWith rs = M.filterWithKey (\k _ -> S.member k rs) <$> readIORef cache
let insertWith resp = atomicModifyIORef' cache (\c -> (M.union resp c, ()))
let cachedService = cacheWithMulti lookupWith insertWith service
cachedService request `shouldReturn` result
cachedService request `shouldReturn` result
it "Merges the cached values as appropriate from subsequent requests" $
property $ \(first :: TestResp, second :: TestResp, both :: TestResp) -> do
let uniqueFirst = M.difference first (M.union second both)
let uniqueSecond = M.difference second (M.union first both)
let uniqueBoth = M.difference both (M.union first second)
let uniqueAll = M.union uniqueFirst $ M.union uniqueSecond uniqueBoth
let firstResult = M.union uniqueFirst uniqueBoth
let secondResult = M.union uniqueSecond uniqueBoth
let firstRequest = S.fromList $ M.keys firstResult
let secondRequest = S.fromList $ M.keys secondResult
ref <- newIORef (0 :: Int)
let service req = do
callCount <- atomicModifyIORef' ref (\c -> (c + 1, c + 1))
if callCount > 2
then throwIO (CachingTestException callCount)
else pure $ M.filterWithKey (\k _ -> S.member k req) uniqueAll
cache <- newIORef M.empty
let lookupWith rs = M.filterWithKey (\k _ -> S.member k rs) <$> readIORef cache
let insertWith resp = atomicModifyIORef' cache (\c -> (M.union resp c, ()))
let cachedService = cacheWithMulti lookupWith insertWith service
cachedService firstRequest `shouldReturn` firstResult
cachedService secondRequest `shouldReturn` secondResult
cachedService firstRequest `shouldReturn` firstResult
cachedService secondRequest `shouldReturn` secondResult