packages feed

kiroku-store-0.7.0.0: test/Test/HistoryRetentionMock.hs

module Test.HistoryRetentionMock (spec) where

import Control.Monad.IO.Class (liftIO)
import Data.IORef (IORef, modifyIORef', newIORef, readIORef)
import Data.Text (Text)
import Data.Time.Calendar (fromGregorian)
import Data.Time.Clock (UTCTime (..), secondsToDiffTime)
import Data.UUID qualified as UUID
import Data.Vector qualified as Vector
import Effectful (Eff, IOE, runEff, (:>))
import Effectful.Dispatch.Dynamic (interpret_)
import Kiroku.Store.Effect (Store (..))
import Kiroku.Store.HistoryRetention
import Kiroku.Store.Types (GlobalPosition (..))
import Test.Hspec

spec :: Spec
spec = describe "history retention mock" $ do
    it "dispatches every closed lease operation exactly once" $ do
        calls <- newIORef ([] :: [Text])
        actual <- runEff $ runMock calls $ do
            acquired <- acquireHistoryRetentionLease sampleRequest
            renewed <- renewHistoryRetentionLease sampleHandle sampleDuration
            released <- releaseHistoryRetentionLease sampleHandle
            inventory <- historyRetentionLeaseInventory sampleQuery
            pruned <- pruneHistoryRetentionLeases sampleTime
            pure (acquired, renewed, released, inventory, pruned)
        actual
            `shouldBe` ( sampleLease
                       , Right sampleLease
                       , HistoryRetentionReleased sampleLease
                       , Vector.singleton sampleLease
                       , HistoryRetentionPruneResult 2 3
                       )
        readIORef calls
            `shouldReturn` ["acquire", "renew", "release", "inventory", "prune"]

runMock :: (IOE :> es) => IORef [Text] -> Eff (Store : es) a -> Eff es a
runMock calls = interpret_ $ \case
    AcquireHistoryRetentionLease request -> do
        liftIO $ request `shouldBe` sampleRequest
        record calls "acquire"
        pure sampleLease
    RenewHistoryRetentionLease handle duration -> do
        liftIO $ (handle, duration) `shouldBe` (sampleHandle, sampleDuration)
        record calls "renew"
        pure (Right sampleLease)
    ReleaseHistoryRetentionLease handle -> do
        liftIO $ handle `shouldBe` sampleHandle
        record calls "release"
        pure (HistoryRetentionReleased sampleLease)
    GetHistoryRetentionLeaseInventory query -> do
        liftIO $ query `shouldBe` sampleQuery
        record calls "inventory"
        pure (Vector.singleton sampleLease)
    PruneHistoryRetentionLeases cutoff -> do
        liftIO $ cutoff `shouldBe` sampleTime
        record calls "prune"
        pure (HistoryRetentionPruneResult 2 3)
    _ -> error "unexpected Store operation in history retention mock"

record :: (IOE :> es) => IORef [Text] -> Text -> Eff es ()
record calls name = liftIO $ modifyIORef' calls (<> [name])

sampleRequest :: HistoryRetentionLeaseRequest
sampleRequest =
    HistoryRetentionLeaseRequest sampleOwner sampleReason sampleDuration

sampleHandle :: HistoryRetentionLeaseHandle
sampleHandle = HistoryRetentionLeaseHandle sampleId sampleOwner

sampleLease :: HistoryRetentionLease
sampleLease =
    HistoryRetentionLease
        sampleId
        sampleOwner
        sampleReason
        (GlobalPosition 17)
        sampleTime
        sampleTime
        sampleExpiry
        Nothing
        HistoryRetentionLeaseActive

sampleId :: HistoryRetentionLeaseId
sampleId = HistoryRetentionLeaseId UUID.nil

sampleOwner :: HistoryRetentionLeaseOwner
sampleOwner = either (error . show) (\value -> value) (mkHistoryRetentionLeaseOwner "mock-owner")

sampleReason :: HistoryRetentionLeaseReason
sampleReason = either (error . show) (\value -> value) (mkHistoryRetentionLeaseReason "mock-reason")

sampleDuration :: HistoryRetentionLeaseDuration
sampleDuration = either (error . show) (\value -> value) (mkHistoryRetentionLeaseDuration (secondsToDiffTime 60))

sampleQuery :: HistoryRetentionInventoryQuery
sampleQuery =
    HistoryRetentionInventoryQuery $
        either (error . show) (\value -> value) (mkHistoryRetentionInventoryLimit 10)

sampleTime :: UTCTime
sampleTime = UTCTime (fromGregorian 2026 8 13) 0

sampleExpiry :: UTCTime
sampleExpiry = UTCTime (fromGregorian 2026 8 13) (secondsToDiffTime 60)