exchangerates-0.0.0.0: test/ExchangeRates/CacheSpec.hs
{-# LANGUAGE TypeApplications #-}
module ExchangeRates.CacheSpec
( spec
) where
import TestImport
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.Map as M
import ExchangeRates.Cache
import ExchangeRates.Types
import ExchangeRates.Cache.Gen ()
import ExchangeRates.Types.Gen ()
spec :: Spec
spec = do
rateCacheSpec
exchangeRatesCacheSpec
rateCacheSpec :: Spec
rateCacheSpec = do
eqSpecOnValid @RateCache
genValidSpec @RateCache
jsonSpecOnValid @RateCache
describe "rawInsertInCache" $
it "produces valid caches if the currencies are distinct" $
forAllValid $ \d ->
forAllValid $ \c1 ->
forAll (genValid `suchThat` (/= c1)) $ \c2 ->
forAllValid $ \r ->
producesValidsOnValids $ rawInsertInCache d c1 c2 r
describe "rawLookupInCache" $ do
it "produces valid rates" $
producesValidsOnValids3 (uncurry rawLookupInCache)
it "finds what was just put there" $
forAllValid $ \d ->
forAllValid $ \c1 ->
forAllValid $ \c2 ->
forAllValid $ \r ->
forAllValid $ \fc ->
let fc' = rawInsertInCache d c1 c2 r fc
in rawLookupInCache d c1 c2 fc' `shouldBe`
Just r
describe "defaultBaseCurrency" $
it "is valid" $ shouldBeValid defaultBaseCurrency
describe "allSymbols" $
it "is valid" $ producesValidsOnValids allSymbolsExcept
describe "insertRatesInCache" $
it "produces valid caches" $
forAllValid $ \rates ->
producesValidsOnValids $ insertRatesInCache rates
describe "smartLookupRateInCache" $ do
it "produces valid rates" $
forAllValid $ \date ->
forAllValid $ \from ->
forAllValid $ \to ->
producesValidsOnValids $
smartLookupRateInCache date from to
it
"finds the same as rawLookupInCache if rawLookupInCache finds a result" $
forAllValid $ \date ->
forAllValid $ \from ->
forAllValid $ \to ->
equivalentWhenSucceedOnValid
(rawLookupInCache date from to)
(smartLookupRateInCache date from to)
it "succesfully lookups up a rate that is cached in reverse" $
forAllValid $ \date ->
forAllValid $ \from ->
forAllValid $ \to ->
forAllValid $ \rate ->
forAllValid $ \fc ->
unless (from == to) $ do
let fc' =
smartInsertInCache
date
from
to
rate
fc
shouldBeValid fc'
smartLookupRateInCache date to from fc' `shouldBe`
(Just $ divRate oneRate rate)
describe "lookupRatesInCache" $ do
it "produces valid caches" $
forAllValid $ \date ->
forAllValid $ \base ->
forAllValid $ \symbols ->
producesValidsOnValids $
lookupRatesInCache date base symbols
it "works for a cache with a single rate" $
forAllValid $ \date ->
forAllValid $ \base ->
forAll (genValid `suchThat` (/= base)) $ \symbol ->
forAllValid $ \rate ->
let cache =
RateCache
{ unRateCache =
M.fromList
[ ( date
, M.fromList
[ ( base
, M.fromList
[ ( symbol
, rate)
])
])
]
}
rates =
Rates
{ ratesBase = base
, ratesDate = date
, ratesRates =
M.fromList [(symbol, rate)]
}
in lookupRatesInCache
date
base
(Symbols $ symbol :| [])
cache `shouldBe`
Just rates
it
"finds the rates that were just added with insertRatesInCache when adding at the default base currency" $
forAllValid $ \rates' ->
forAllValid $ \fc ->
let rates =
rates'
{ ratesBase = defaultBaseCurrency
, ratesRates =
M.delete defaultBaseCurrency $
ratesRates rates'
}
fc' = insertRatesInCache rates fc
in case NE.nonEmpty (M.keys $ ratesRates rates) of
Nothing -> pure () -- Fine
Just symbols ->
lookupRatesInCache
(ratesDate rates)
(ratesBase rates)
(Symbols symbols)
fc' `shouldBe`
Just rates
it "finds the rates that were just added with insertRatesInCache" $
forAllValid $ \rates ->
forAllValid $ \fc ->
let fc' = insertRatesInCache rates fc
in case NE.nonEmpty (M.keys $ ratesRates rates) of
Nothing -> pure () -- Fine
Just symbols ->
lookupRatesInCache
(ratesDate rates)
(ratesBase rates)
(Symbols symbols)
fc' `shouldBe`
Just rates
describe "convertToBaseWithRate" $ do
it "produces valid caches" $
forAllValid $ \base ->
forAllValid $ \rate ->
producesValidsOnValids $ convertToBaseWithRate base rate
it
"converts the rates such that the new base has a 1-to-rate conversion with the old base" $
forAllValid $ \base ->
forAllValid $ \rate ->
forAllValid $ \rates ->
unless (base == ratesBase rates) $
let rates' =
rates
{ ratesRates =
M.insert base rate $ ratesRates rates
}
rates'' = convertToBaseWithRate base rate rates'
in M.lookup (ratesBase rates) (ratesRates rates'') `shouldBe`
Just (divRate oneRate rate)
exchangeRatesCacheSpec :: Spec
exchangeRatesCacheSpec = do
eqSpecOnValid @ExchangeRatesCache
genValidSpec @ExchangeRatesCache
jsonSpecOnValid @ExchangeRatesCache
describe "insertRates" $
it "produces valid caches" $
forAllValid $ \d -> producesValidsOnValids3 $ insertRates d
describe "lookupRates" $
it "produces valid results" $
forAllValid $ \dn ->
forAllValid $ \dr -> producesValidsOnValids3 $ lookupRates dn dr