packages feed

conferer-1.1.0.0: test/Conferer/ConfigSpec.hs

{-# LANGUAGE TypeApplications #-}
module Conferer.ConfigSpec where

import Test.Hspec
import Data.Text (Text)
import Data.Dynamic

import Conferer.Config
import Conferer.Source.InMemory
import Conferer.FromConfig.Internal (fromDynamics)

missingKey :: Key -> KeyLookupResult -> Bool 
missingKey expectedKey (MissingKey k) = [expectedKey] == k
missingKey _ _ = False

foundInSources :: Key -> Text -> KeyLookupResult -> Bool 
foundInSources expectedKey expected (FoundInSources k t) =  
  expected == t && expectedKey == k
foundInSources _keyExpected _expected _ =  
  False

foundInDefaults :: forall expected. (Eq expected, Typeable expected)
  => Key -> expected -> KeyLookupResult -> Bool 
foundInDefaults expectedKey expected (FoundInDefaults k d) =  
  Just expected == fromDynamics @expected d && expectedKey == k
foundInDefaults _expctedKey _expected _ =  
  False

spec :: Spec
spec = do
  describe "Config" $ do
    let mkConfig keyMappings defaults content = 
          pure (emptyConfig
                  & addKeyMappings keyMappings
                  & addDefaults defaults)
              >>= addSource (fromConfig content)
    describe "#getKey" $ do
      it "getting a non existent key returns missing key" $ do
        c <- mkConfig [] [] []
        res <- getKey "aaa" c
        res `shouldSatisfy` missingKey "aaa"

      it "getting a key only present in the defaults" $ do
        c <- mkConfig [] [("some.key", toDyn @Int 1)] []
        res <- getKey "some.key" c
        res `shouldSatisfy` foundInDefaults "some.key" (1 :: Int)

      context "with multiple sources and defaults" $ do
        let mkConfig' defaults sourceWithPriority otherSource =
              pure (emptyConfig
                & addDefaults defaults)
              >>= addSource (fromConfig sourceWithPriority)
              >>= addSource (fromConfig otherSource)

        it "getting an key returns unwraps the original map" $ do
          c <- mkConfig' [] [] [("some.key", "1")]
          res <- getKey "some.key" c
          res `shouldSatisfy` foundInSources "some.key" "1"

        it "getting an existent key returns in the bottom maps gets it" $ do
          c <- mkConfig' [] [("some.key", "2")] [("some.key", "1")]
          res <- getKey "some.key" c
          res `shouldSatisfy` foundInSources "some.key" "2"
      describe "with some key mapping" $ do
        context "with basic one to one mapping" $ 
          it "gets the value through a mapping" $ do
            c <- mkConfig
                  [ ("something", "server") ]
                  []
                  [ ("server", "aaa") ]
            res <- getKey "something" c
            res `shouldSatisfy` foundInSources "server" "aaa"
        context "with a nested key mapping" $ do
          it "goes through all the mappings and gets the right value" $ do
            c <- mkConfig
                  [ ("something", "else")
                  , ("else", "server")
                  ]
                  []
                  [ ("server", "aaa")
                  ]
            res <- getKey "something" c
            res `shouldSatisfy` foundInSources "server" "aaa"
        context "with circular mappings" $ do
          it "gets the first key" $ do
            c <- mkConfig
                  [ ("a", "b")
                  , ("b", "a")
                  , ("b", "c")
                  ]
                  []
                  [ ("c", "aaa")
                  ]
            res <- getKey "a" c
            res `shouldSatisfy` foundInSources "c" "aaa"
        context "with nested key" $ do
          it "maps the right upper key" $ do
            c <- mkConfig
                  [ ("a", "b")
                  ]
                  []
                  [ ("b.k", "aaa")
                  ]
            res <- getKey "a.k" c
            res `shouldSatisfy` foundInSources "b.k" "aaa"
        context "with some defaults" $ do
          it "maps and allows getting the default" $ do
            c <- mkConfig
                  [ ("a", "b")
                  ]
                  [ ("b", toDyn False)
                  ]
                  []
            res <- getKey "a" c
            res `shouldSatisfy` foundInDefaults "a" False
    describe "#listSubkeys" $ do
      it "return an empty list when nothing is configured" $ do
        c <- mkConfig [] [] []
        res <- listSubkeys "some" c
        res `shouldBe` []
      context "with some values in the config" $ do
        it "when nothing is configured" $ do
          c <- mkConfig [] [] [("some.key", "")]
          res <- listSubkeys "some" c
          res `shouldBe` ["some.key"]
      context "with a config that has a value configured and \
              \we get that same value" $ do
        it "returns that key in the list of subkeys" $ do
          c <- mkConfig [] [] [("some", "")]
          res <- listSubkeys "some" c
          res `shouldBe` []
      context "with a config that has a value configured but \
              \we get a different value" $ do
        it "returns an empty list" $ do
          c <- mkConfig [] [] [("some", "")]
          res <- listSubkeys "other" c
          res `shouldBe` []
      context "with a config with defaults" $ do
        it "returns those defaults in the list" $ do
          c <- mkConfig [] [("some.key", toDyn @Int 7)] []
          res <- listSubkeys "some" c
          res `shouldBe` ["some.key"]
      context "with configs in both defaults and sources" $ do
        it "returns both results combined" $ do
          c <- mkConfig [] [("some.key", toDyn @Int 7)] [("some.other", "")]
          res <- listSubkeys "some" c
          res `shouldBe` ["some.key", "some.other"]
      context "mappings" $ do
        context "with a simple mapping and a configured value" $ do
          it "returns the keys that are present based on the mapping" $ do
            c <- mkConfig
                  [ ("a", "b")
                  ]
                  []
                  [ ("b.k", "aaa")
                  ]
            res <- listSubkeys "a" c
            res `shouldBe` ["a.k"]
        context "with a multiple mappings and a configured value" $ do
          it "returns the right keys" $ do
            c <- mkConfig
                  [ ("a", "b")
                  , ("b", "c")
                  ]
                  []
                  [ ("c.k", "aaa")
                  ]
            res <- listSubkeys "a" c
            res `shouldBe` ["a.k"]
        context "with circular mappings" $ do
          it "returns the right keys" $ do
            c <- mkConfig
                  [ ("a", "b")
                  , ("b", "a")
                  ]
                  []
                  [ ("a.k", "aaa")
                  ]
            res <- listSubkeys "a" c
            res `shouldBe` ["a.k"]
        context "with reducing mappings" $ do
          xit "returns the right keys" $ do
            c <- mkConfig
                  [ ("a.k", "a")
                  ]
                  []
                  [ ("a.k", "aaa")
                  ]
            res <- listSubkeys "a" c
            -- getKey "a.k.k" c >>= print
            res `shouldBe` ["a.k", "a.k.k"]
        it "keys that are present based on the mapping" $ do
            c <- mkConfig
                  [ ("a", "b")
                  ]
                  [ ("b.k", toDyn @String "aaa")
                  ]
                  [ ]
            res <- listSubkeys "a" c
            res `shouldBe` ["a.k"]

    describe "#addDefaults" $ do
      context "with multiple defaults on the same key" $ do
        it "the last one has more priority" $ do
          let c = emptyConfig
                & addDefaults
                  [ ("some.key", toDyn @Int 1)
                  , ("some.key", toDyn @Int 2)
                  ]
          res <- getKey "some.key" c
          res `shouldSatisfy` foundInDefaults "some.key" (2 :: Int)