{-# 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