freckle-app-1.2.0.0: tests/Freckle/App/MemcachedSpec.hs
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module Freckle.App.MemcachedSpec
( spec
) where
import Freckle.App.Prelude
import Control.Lens ((^?!))
import Control.Monad.Reader
import Data.Aeson
import Data.Aeson.Lens
import qualified Data.List.NonEmpty as NE
import qualified Freckle.App.Env as Env
import Freckle.App.Memcached
import Freckle.App.Memcached.Client (MemcachedClient, newMemcachedClient)
import qualified Freckle.App.Memcached.Client as Memcached
import Freckle.App.Memcached.Servers
import Freckle.App.Test
import Freckle.App.Test.Logging
data ExampleValue
= A
| B
| C
deriving stock (Eq, Show)
instance Cachable ExampleValue where
toCachable = \case
A -> "A"
B -> "Broken"
C -> "C"
fromCachable = \case
"A" -> Right A
"B" -> Right B
"C" -> Right C
x -> Left $ "invalid: " <> show x
runTestAppT
:: MonadUnliftIO m => AppExample MemcachedClient a -> m (a, [Maybe Value])
runTestAppT f = liftIO $ do
mc <- loadClient
-- NB. we could use `withApp` and not need to call this runner ourselves
-- within a plain-`IO` example -- except that we want to use
-- `runCapturedLoggingT` and assert on the logged messages. We should add
-- Blammo.Logging.Test to support this use-case.
fmap (second $ map logLineToJSON) $ runCapturedLoggingT $ runReaderT
(unAppExample f)
mc
loadClient :: IO MemcachedClient
loadClient = do
servers <- Env.parse id $ Env.var
(Env.eitherReader readMemcachedServers)
"MEMCACHED_SERVERS"
(Env.def defaultMemcachedServers)
newMemcachedClient servers
spec :: Spec
spec = do
describe "caching" $ do
it "caches the given action by key using Cachable" $ example $ do
void $ runTestAppT $ do
k <- cacheKeyThrow "A"
val <- caching k (cacheTTL 5) $ pure A
mbs <- Memcached.get k
liftIO $ val `shouldBe` A
liftIO $ mbs `shouldBe` Just "A"
it "logs, but doesn't fail, on deserialization errors" $ example $ do
(_, msgs) <- runTestAppT $ do
k <- cacheKeyThrow "B"
val0 <- caching k (cacheTTL 5) $ pure B -- set
val1 <- caching k (cacheTTL 5) $ pure B -- get will fail
mbs <- Memcached.get k
liftIO $ val0 `shouldBe` B
liftIO $ val1 `shouldBe` B
liftIO $ mbs `shouldBe` Just "Broken"
let Just val = NE.last =<< NE.nonEmpty msgs
val ^?! key "text" . _String `shouldBe` "Error deserializing"
val ^?! key "meta" . key "action" . _String `shouldBe` "deserializing"
val
^?! key "meta"
. key "message"
. _String
`shouldBe` "invalid: \"Broken\""