packages feed

caps-0.1: test/TestExamples.hs

{-# LANGUAGE DataKinds, TypeFamilies, RankNTypes, UndecidableInstances,
             MultiParamTypeClasses, FlexibleInstances, TypeApplications,
             AllowAmbiguousTypes, ScopedTypeVariables, TemplateHaskell #-}

{-# OPTIONS -ddump-splices #-}

module Main where

import Test.Tasty
import Test.Tasty.HUnit

import Control.Monad.Reader
import Monad.Capabilities

-------- Effect declarations ----------

data Logging msg m = Logging
  { _logError :: msg -> m (),
    _logWarning :: msg -> m ()
  }

makeCap ''Logging

data DB k v m = DB
  { _dbGet :: k -> m v,
    _dbPut :: k -> v -> m (),
    _dbWithLock :: forall a. (String -> m a) -> m a
  }

makeCap ''DB

-------- Effect implementations ----------

loggingDummy :: forall msg m. Monad m => CapImpl (Logging msg) '[] m
loggingDummy = CapImpl $ Logging
  { _logError = \_ -> return (),
    _logWarning = \_ -> return ()
  }

loggingIO :: MonadIO m => CapImpl (Logging String) '[Logging String] m
loggingIO = CapImpl $ Logging
  { _logError = liftIO . putStrLn,
    _logWarning = logError -- recursive use of capabilities!
  }

dbDummy :: Monad m => CapImpl (DB String Integer) '[Logging String] m
dbDummy = CapImpl $ DB
  { _dbGet = \key -> do logWarning ("get " ++ key); return 0,
    _dbPut = \key value -> do logWarning ("put " ++ key ++ " " ++ show value); return (),
    _dbWithLock = \m -> m "lock"
  }

-------- Test implementations ----------

testLoggingOverride :: TestTree
testLoggingOverride = testCase "logging override" $ do
  let
    caps = buildCaps $
      AddCap loggingIO $ -- try commenting out this line,
                         -- you get a nice error message
      -- AddCap loggingDummy $ -- try uncommenting this line,
                               -- you get a nice error message
      AddCap dbDummy $
      BaseCaps emptyCaps
  flip runReaderT caps $ do
    v :: Integer <- dbGet "k" -- will have log output
    withReaderT (overrideCap @(Logging String) loggingDummy) $ do
      dbPut "k2" v -- will not have log output
  -- I KNOW THIS IS NOT A PROPER UNIT TEST :)
  -- Check the output in the console manually for now.

testAddingDb :: TestTree
testAddingDb = testCase "adding db" $ do
  let
    caps = buildCaps $
      AddCap loggingIO $
      BaseCaps emptyCaps
  flip runReaderT caps $ do
    -- can't have DB access here
    withReaderT (insertCap dbDummy) $ do
      -- have DB access here
      dbPut "k" (42 :: Integer)
  -- I KNOW THIS IS NOT A PROPER UNIT TEST :)
  -- Check the output in the console manually for now.


-------- Test tree and Main ----------

main :: IO ()
main = do
  defaultMain suite

suite :: TestTree
suite = testGroup "Capabilities"
  [ testLoggingOverride,
    testAddingDb
  ]