packages feed

delta-store-1.0.0.0: test/unit/Data/StoreSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-- |
-- Copyright: © 2023 IOHK
-- License: Apache-2.0
module Data.StoreSpec
    ( spec
    ) where

import Prelude

import Data.Delta
    ( Delta (..)
    )
import Data.Store
    ( Store (..)
    , UpdateStore
    , newCachedStore
    , newStore
    )
import Test.Data.Store
    ( prop_StoreUpdate
    )
import Test.Hspec
    ( Spec
    , describe
    , it
    )
import Test.QuickCheck
    ( elements
    , generate
    , (===)
    )
import Test.QuickCheck.Gen
    ( Gen
    , listOf
    )
import Test.QuickCheck.Monadic
    ( monadicIO
    , run
    )

spec :: Spec
spec = do
    describe "CachedStore" $ do
        it "respects store laws" $
            let setupStore = do
                    testStore <- newTestStore
                    resetTestStoreBase testStore
                    newCachedStore testStore
            in  prop_StoreUpdate
                    id
                    setupStore
                    (pure emptyTestStore)
                    $ const genTestStoreDeltas

        it "behaves like the cached one" $ monadicIO $ run $ do

            das <- generate $ listOf genTestStoreDeltas

            testStore <- newTestStore

            cachedStore <- newCachedStore testStore

            resetTestStoreBase testStore
            updateStore cachedStore das
            Right cachedFinal <- loadS cachedStore

            resetTestStoreBase testStore
            updateStore testStore das
            Right originalFinal <- loadS testStore

            pure $ cachedFinal === originalFinal

newTestStore :: IO (UpdateStore IO TestStoreDelta)
newTestStore = newStore

updateStore :: Monad m => Store m qa da -> [da] -> m ()
updateStore store = mapM_ (updateS store Nothing)

genTestStoreDeltas :: Gen TestStoreDelta
genTestStoreDeltas = elements [AddOne, AddTwo, RemoveOne]

resetTestStoreBase :: (Base da ~ TestStoreBase) => Store m qa da -> m ()
resetTestStoreBase store = writeS store emptyTestStore

emptyTestStore :: TestStoreBase
emptyTestStore = TestStoreBase []

newtype TestStoreBase = TestStoreBase [Int]
    deriving (Show, Eq)

data TestStoreDelta
    = AddOne
    | AddTwo
    | RemoveOne
    deriving (Show, Eq)

instance Delta TestStoreDelta where
    type Base TestStoreDelta = TestStoreBase
    apply AddOne = overTestStoreBase (1:)
    apply AddTwo = overTestStoreBase (2:)
    apply RemoveOne = overTestStoreBase (drop 1)

overTestStoreBase :: ([Int] -> [Int]) -> TestStoreBase -> TestStoreBase
overTestStoreBase f (TestStoreBase xs) = TestStoreBase (f xs)