packages feed

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