module Test.Gen.Mock
( MockInit (..)
, genMockInit
, TimeoutGenOptions
, requireSomeTimeLimit
, noTimeoutResolution
)
where
import Internal.Prelude
import Test.Gen.General
import Test.QuickCheck (Gen)
import Test.QuickCheck.Gen qualified as Gen
import Time
import Yesod.Session.Persist
data MockInit = MockInit
{ randomSeed :: Int
, time :: UTCTime
, timing :: TimingOptions NominalDiffTime
}
deriving stock (Eq, Show)
genMockInit :: (TimeoutGenOptions -> TimeoutGenOptions) -> Gen MockInit
genMockInit timeoutOptions = do
randomSeed <- arbitrary
time <-
chooseTime
(UTCTime (fromOrdinalDate 1950 0) 0, UTCTime (fromOrdinalDate 2050 0) 0)
timing <- genTimingOptions (timeoutOptions defaultTimeoutGenOptions)
pure MockInit {..}
data TimeoutGenOptions = TimeoutGenOptions
{ requireSomeLimit :: Bool
, generateResolution :: Maybe Bool
}
defaultTimeoutGenOptions :: TimeoutGenOptions
defaultTimeoutGenOptions =
TimeoutGenOptions
{ requireSomeLimit = False
, generateResolution = Nothing
}
-- | Ensure that the generated settings have at least and idle or an absolute timeout
--
-- Use this for tests that require an expired session, since without timeout
-- settings no session can expire.
requireSomeTimeLimit :: TimeoutGenOptions -> TimeoutGenOptions
requireSomeTimeLimit x = x {requireSomeLimit = True}
-- | Ensure that the generate settings do not have a timeout resolution
--
-- This disables the optimization that prevents a session from being saved to the
-- database when the only change is a small increment in the access time.
-- Use this when the optimization would overcomplicate a test's assertion that it
-- performs an update to an existing session.
noTimeoutResolution :: TimeoutGenOptions -> TimeoutGenOptions
noTimeoutResolution x = x {generateResolution = Just False}
genTimingOptions :: TimeoutGenOptions -> Gen (TimingOptions NominalDiffTime)
genTimingOptions x = do
timeout <- do
(requireIdle, requireAbsolute) <-
if x.requireSomeLimit
then Gen.elements [(False, True), (True, False)]
else pure (False, False)
idle <-
(if requireIdle then fmap Just else genMaybe)
$ chooseNominalDiffTime (secondsToNominalDiffTime 60, nominalDay)
absolute <-
(if requireAbsolute then fmap Just else genMaybe)
$ maybe id (+) idle -- Absolute timeout should be greater than idle timeout
<$> chooseNominalDiffTime (secondsToNominalDiffTime 60, nominalDay)
pure Timeout {idle, absolute}
let resolutionGenerator = case timeout.idle <|> timeout.absolute of
Just t ->
-- Resolution should be a fraction of the smaller of the timeout limits
chooseNominalDiffTime (t / 10, t / 2)
Nothing ->
chooseNominalDiffTime (secondsToNominalDiffTime 2, secondsToNominalDiffTime 60)
resolution <- case x.generateResolution of
Nothing -> genMaybe resolutionGenerator
Just True -> Just <$> resolutionGenerator
Just False -> pure Nothing
pure TimingOptions {timeout, resolution}