diff --git a/packages/conferer/conferer.cabal b/packages/conferer/conferer.cabal index 96e881c..a3c051e 100644 --- a/packages/conferer/conferer.cabal +++ b/packages/conferer/conferer.cabal @@ -73,6 +73,7 @@ test-suite specs main-is: ConfererSpecMain.hs other-modules: Conferer.ConfigSpec + Conferer.ExceptionMessagesSpec Conferer.FromConfig.BoolSpec Conferer.FromConfig.Extended Conferer.FromConfig.FileSpec @@ -83,13 +84,16 @@ test-suite specs Conferer.FromConfigSpec Conferer.GenericsSpec Conferer.KeySpec + Conferer.MultiSourceErrorsSpec Conferer.Source.CLIArgsSpec Conferer.Source.EnvSpec + Conferer.Source.ExplanationsSpec Conferer.Source.InMemorySpec Conferer.Source.NamespacedSpec Conferer.Source.NullSpec Conferer.Source.PropertiesFileSpec ConfererSpec + Tests Paths_conferer hs-source-dirs: test @@ -101,6 +105,8 @@ test-suite specs RecordWildCards StrictData ghc-options: -Wall -Wredundant-constraints -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -main-is ConfererSpecMain + build-tool-depends: + hspec-discover:hspec-discover build-depends: QuickCheck , base >=4.3 && <5 @@ -111,6 +117,9 @@ test-suite specs , directory >=1.2 && <2.0 , filepath >=1.0 && <2.0 , hspec + , hspec-core + , hspec-golden + , template-haskell , text >=1.1 && <2.2 default-language: Haskell2010 if impl(ghc >= 8.4.1) diff --git a/packages/conferer/package.yaml b/packages/conferer/package.yaml index a27019e..5291ffa 100644 --- a/packages/conferer/package.yaml +++ b/packages/conferer/package.yaml @@ -21,9 +21,13 @@ tests: specs: main: ConfererSpecMain source-dirs: test + build-tools: + - hspec-discover dependencies: - conferer - hspec + - hspec-core + - hspec-golden + - template-haskell - deepseq - QuickCheck - diff --git a/packages/conferer/src/Conferer/FromConfig/Internal/Types.hs b/packages/conferer/src/Conferer/FromConfig/Internal/Types.hs index 4c74fde..007dd10 100644 --- a/packages/conferer/src/Conferer/FromConfig/Internal/Types.hs +++ b/packages/conferer/src/Conferer/FromConfig/Internal/Types.hs @@ -14,6 +14,7 @@ module Conferer.FromConfig.Internal.Types where import Control.Exception import Conferer.Key (Key) import Data.Text (Text) +import qualified Data.Text as Text import Data.Typeable import Conferer.Config.Internal.Types import Conferer.Source.Internal @@ -28,11 +29,13 @@ data ConfigParsingError = type OriginalValue = Text instance Exception ConfigParsingError where - displayException (ConfigParsingError key _value aTypeRep sourceIndex c) = + displayException (ConfigParsingError key value aTypeRep sourceIndex c) = concat [ "Failed to interpret " , explainSettedKey (configSources c !! sourceIndex) key - , " as '" + , " with a value of '" + , Text.unpack value + , "' as '" , show aTypeRep , "'" ] diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Bool parsing error from CLI args/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Bool parsing error from CLI args/golden new file mode 100644 index 0000000..21ade9f --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Bool parsing error from CLI args/golden @@ -0,0 +1,2 @@ +Failed to interpret cli param '--enabled' with a value of 'maybe' as 'Bool' +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from env source/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from env source/golden new file mode 100644 index 0000000..b9dbb17 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from env source/golden @@ -0,0 +1,2 @@ +Failed to interpret env var 'APP_PORT' with a value of 'not_a_number' as 'Int' +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from properties file/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from properties file/golden new file mode 100644 index 0000000..97d5d84 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/ConfigParsingError exception messages/formats Int parsing error from properties file/golden @@ -0,0 +1,2 @@ +Failed to interpret key 'timeout' (on file '/tmp/test.properties') with a value of 'invalid' as 'Int' +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats missing key with complex type/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats missing key with complex type/golden new file mode 100644 index 0000000..655791c --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats missing key with complex type/golden @@ -0,0 +1,6 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Setting the environment variable: APP_ALLOWED_HOSTS_KEYS + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from CLI source/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from CLI source/golden new file mode 100644 index 0000000..db3d2e9 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from CLI source/golden @@ -0,0 +1,6 @@ +Couldn't find a 'Int'. + +You can set it by either: +* Passing the cli arg: --server.port="the value" + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from env source/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from env source/golden new file mode 100644 index 0000000..d654fb8 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from env source/golden @@ -0,0 +1,6 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Setting the environment variable: APP_DATABASE_HOST + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from multiple sources/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from multiple sources/golden new file mode 100644 index 0000000..eae08ff --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from multiple sources/golden @@ -0,0 +1,8 @@ +Couldn't find a 'Int'. + +You can set it by either: +* Setting the environment variable: APP_CACHE_TTL +* Passing the cli arg: --cache.ttl="the value" +* Adding a new line 'cache.ttl=some value' to the file '/etc/app.properties' + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from non-existent properties file/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from non-existent properties file/golden new file mode 100644 index 0000000..5ee7033 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from non-existent properties file/golden @@ -0,0 +1,6 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Creating a file '/path/to/missing.properties' (it doesn't exist now) and adding a line 'redis.host=some value'. + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from properties file/golden b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from properties file/golden new file mode 100644 index 0000000..98de90b --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.golden/MissingRequiredKey exception messages/formats single key missing from properties file/golden @@ -0,0 +1,6 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Adding a new line 'api.key=some value' to the file '/etc/app.properties' + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/ExceptionMessagesSpec.hs b/packages/conferer/test/Conferer/ExceptionMessagesSpec.hs new file mode 100644 index 0000000..f391022 --- /dev/null +++ b/packages/conferer/test/Conferer/ExceptionMessagesSpec.hs @@ -0,0 +1,93 @@ +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} +module Conferer.ExceptionMessagesSpec where + +import Test.Hspec +import Test.Hspec.Golden +import Control.Exception +import Data.Text (Text) +import qualified Data.Text as Text +import Data.Typeable + +import Conferer +import Conferer.Config +import Conferer.FromConfig +import Conferer.Source +import Conferer.Source.Env (fromEnvList) +import Conferer.Source.CLIArgs (fromArgs) +import Conferer.Source.PropertiesFile (fromFileContent) +import Conferer.Source.Null (NullSource(..)) +import Tests + +spec :: Spec +spec = do + describe "ConfigParsingError exception messages" $ do + $(betterGolden) "formats Int parsing error from env source" $ do + config <- mkConfigWithEnv [("APP_PORT", "not_a_number")] + captureException $ fetchFromConfig @Int "port" config + + $(betterGolden) "formats Bool parsing error from CLI args" $ do + config <- mkConfigWithCLI ["--enabled=maybe"] + captureException $ fetchFromConfig @Bool "enabled" config + + $(betterGolden) "formats Int parsing error from properties file" $ do + config <- mkConfigWithProperties "/tmp/test.properties" "timeout=invalid" + captureException $ fetchFromConfig @Int "timeout" config + + describe "MissingRequiredKey exception messages" $ do + $(betterGolden) "formats single key missing from env source" $ do + config <- mkConfigWithEnv [] + captureException $ fetchFromConfig @String "database.host" config + + $(betterGolden) "formats single key missing from CLI source" $ do + config <- mkConfigWithCLI [] + captureException $ fetchFromConfig @Int "server.port" config + + $(betterGolden) "formats single key missing from properties file" $ do + config <- mkConfigWithProperties "/etc/app.properties" "" + captureException $ fetchFromConfig @String "api.key" config + + $(betterGolden) "formats single key missing from non-existent properties file" $ do + config <- mkConfigWithNonExistentProperties "/path/to/missing.properties" + captureException $ fetchFromConfig @String "redis.host" config + + $(betterGolden) "formats single key missing from multiple sources" $ do + config <- mkConfigWithMultipleSources + captureException $ fetchFromConfig @Int "cache.ttl" config + + $(betterGolden) "formats missing key with complex type" $ do + config <- mkConfigWithEnv [] + captureException $ fetchFromConfig @[String] "allowed.hosts" config + +mkConfigWithEnv :: [(String, String)] -> IO Config +mkConfigWithEnv envVars = + addSource (\_ -> return $ fromEnvList envVars "APP") emptyConfig + +mkConfigWithCLI :: [String] -> IO Config +mkConfigWithCLI args = + addSource (\_ -> return $ fromArgs args) emptyConfig + +mkConfigWithProperties :: FilePath -> Text -> IO Config +mkConfigWithProperties path content = + addSource (\_ -> return $ fromFileContent path content) emptyConfig + +mkConfigWithNonExistentProperties :: FilePath -> IO Config +mkConfigWithNonExistentProperties path = do + -- Use Null source with a custom explanation that simulates non-existent file + let mkSource = \_ -> return $ Source $ NullSource $ \key -> + concat + [ "Creating a file '" + , path + , "' (it doesn't exist now) and adding a line '" + , Text.unpack $ Text.intercalate "." $ rawKeyComponents key + , "=some value'." + ] + addSource mkSource emptyConfig + +mkConfigWithMultipleSources :: IO Config +mkConfigWithMultipleSources = do + config1 <- addSource (\_ -> return $ fromEnvList [] "APP") emptyConfig + config2 <- addSource (\_ -> return $ fromArgs []) config1 + addSource (\_ -> return $ fromFileContent "/etc/app.properties" "") config2 diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources for missing nested key/golden b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources for missing nested key/golden new file mode 100644 index 0000000..a5bd525 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources for missing nested key/golden @@ -0,0 +1,8 @@ +Couldn't find a 'Int'. + +You can set it by either: +* Setting the environment variable: APP_APP_WORKER_THREADS +* Passing the cli arg: --app.worker.threads="the value" +* Adding a new line 'app.worker.threads=some value' to the file '/etc/myapp.properties' + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources when key is missing from env + CLI + properties/golden b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources when key is missing from env + CLI + properties/golden new file mode 100644 index 0000000..86a8cd7 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows all sources when key is missing from env + CLI + properties/golden @@ -0,0 +1,8 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Setting the environment variable: APP_DATABASE_PASSWORD +* Passing the cli arg: --database.password="the value" +* Adding a new line 'database.password=some value' to the file '/etc/myapp.properties' + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows combined env, CLI, and in-memory sources/golden b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows combined env, CLI, and in-memory sources/golden new file mode 100644 index 0000000..0df6247 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows combined env, CLI, and in-memory sources/golden @@ -0,0 +1,9 @@ +Couldn't find a '[Char]'. + +You can set it by either: +* Setting the environment variable: APP_ALLOWED_ORIGINS_KEYS +* Passing the cli arg: --allowed.origins.keys="the value" +* Adding a new line 'allowed.origins.keys=some value' to the file '/etc/myapp.properties' +* Setting key '"allowed.origins.keys"' in the config + +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows correct source when parsing fails in second source/golden b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows correct source when parsing fails in second source/golden new file mode 100644 index 0000000..23e3402 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows correct source when parsing fails in second source/golden @@ -0,0 +1,2 @@ +Failed to interpret cli param '--server.port' with a value of 'not-a-number' as 'Int' +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows parsing error with correct source index from properties file/golden b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows parsing error with correct source index from properties file/golden new file mode 100644 index 0000000..8017911 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.golden/Multi-source error messages/shows parsing error with correct source index from properties file/golden @@ -0,0 +1,2 @@ +Failed to interpret key 'feature.enabled' (on file '/config/app.properties') with a value of 'maybe' as 'Bool' +HasCallStack backtrace \ No newline at end of file diff --git a/packages/conferer/test/Conferer/MultiSourceErrorsSpec.hs b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.hs new file mode 100644 index 0000000..f360ae6 --- /dev/null +++ b/packages/conferer/test/Conferer/MultiSourceErrorsSpec.hs @@ -0,0 +1,72 @@ +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TemplateHaskell #-} +module Conferer.MultiSourceErrorsSpec where + +import Test.Hspec +import Test.Hspec.Golden +import Control.Exception +import Data.Text (Text) +import qualified Data.Text as Text + +import Conferer +import Conferer.Config +import Conferer.Source.Env (fromEnvList) +import Conferer.Source.CLIArgs (fromArgs) +import Conferer.Source.PropertiesFile (fromFileContent) +import Conferer.Source.InMemory (fromAssociations, ExplainNotFound (..), ExplainSettedKey (..)) +import Tests + +spec :: Spec +spec = do + describe "Multi-source error messages" $ do + $(betterGolden) "shows all sources when key is missing from env + CLI + properties" $ do + putStrLn "lelle" + config <- mkConfigTripleSource + captureException $ fetchFromConfig @String "database.password" config + + $(betterGolden) "shows correct source when parsing fails in second source" $ do + config <- mkConfigWithInvalidValueInCLI + captureException $ fetchFromConfig @Int "server.port" config + + $(betterGolden) "shows all sources for missing nested key" $ do + config <- mkConfigTripleSource + captureException $ fetchFromConfig @Int "app.worker.threads" config + + $(betterGolden) "shows combined env, CLI, and in-memory sources" $ do + config <- mkConfigQuadSource + captureException $ fetchFromConfig @[String] "allowed.origins" config + + $(betterGolden) "shows parsing error with correct source index from properties file" $ do + config <- mkConfigWithInvalidValueInProperties + captureException $ fetchFromConfig @Bool "feature.enabled" config + +-- Helper functions + +mkConfigTripleSource :: IO Config +mkConfigTripleSource = do + pure emptyConfig + >>= addSource (\_ -> return $ fromEnvList [] "APP") + >>= addSource (\_ -> return $ fromArgs []) + >>= addSource (\_ -> return $ fromFileContent "/etc/myapp.properties" "") + +mkConfigQuadSource :: IO Config +mkConfigQuadSource = do + -- InMemory source needs explanation functions + let explainNotF = ExplainNotFound $ \k -> "Setting key '" ++ show k ++ "' in the config" + explainSetF = ExplainSettedKey $ \k -> "key '" ++ show k ++ "' from config" + pure emptyConfig + >>= addSource (\_ -> return $ fromEnvList [] "APP") + >>= addSource (\_ -> return $ fromArgs []) + >>= addSource (\_ -> return $ fromFileContent "/etc/myapp.properties" "") + >>= addSource (\_ -> return $ fromAssociations explainNotF explainSetF []) + +mkConfigWithInvalidValueInCLI :: IO Config +mkConfigWithInvalidValueInCLI = do + config1 <- addSource (\_ -> return $ fromEnvList [] "APP") emptyConfig + addSource (\_ -> return $ fromArgs ["--server.port=not-a-number"]) config1 + +mkConfigWithInvalidValueInProperties :: IO Config +mkConfigWithInvalidValueInProperties = do + config1 <- addSource (\_ -> return $ fromEnvList [] "APP") emptyConfig + config2 <- addSource (\_ -> return $ fromArgs []) config1 + addSource (\_ -> return $ fromFileContent "/config/app.properties" "feature.enabled=maybe") config2 diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in CLIArgsSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in CLIArgsSource/golden new file mode 100644 index 0000000..a35f4b3 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in CLIArgsSource/golden @@ -0,0 +1 @@ +cli param '--server.port' \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in EnvSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in EnvSource/golden new file mode 100644 index 0000000..e2ca92d --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in EnvSource/golden @@ -0,0 +1 @@ +env var 'APP_DATABASE_HOST' \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in NamespacedSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in NamespacedSource/golden new file mode 100644 index 0000000..615f23b --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in NamespacedSource/golden @@ -0,0 +1 @@ +Doing nothing, you can't use this source for that key \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in PropertiesFileSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in PropertiesFileSource/golden new file mode 100644 index 0000000..97d416c --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found key in PropertiesFileSource/golden @@ -0,0 +1 @@ +key 'api.key' (on file '/etc/app.properties') \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found nested key in EnvSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found nested key in EnvSource/golden new file mode 100644 index 0000000..caf5280 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains a found nested key in EnvSource/golden @@ -0,0 +1 @@ +env var 'APP_SERVER_SSL_ENABLED' \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in CLIArgsSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in CLIArgsSource/golden new file mode 100644 index 0000000..aea16ea --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in CLIArgsSource/golden @@ -0,0 +1 @@ +Passing the cli arg: --server.port="the value" \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in EnvSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in EnvSource/golden new file mode 100644 index 0000000..95b1a2a --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in EnvSource/golden @@ -0,0 +1 @@ +Setting the environment variable: APP_DATABASE_HOST \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in NamespacedSource wrapping Env/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in NamespacedSource wrapping Env/golden new file mode 100644 index 0000000..615f23b --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in NamespacedSource wrapping Env/golden @@ -0,0 +1 @@ +Doing nothing, you can't use this source for that key \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in PropertiesFileSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in PropertiesFileSource/golden new file mode 100644 index 0000000..9ff6b5f --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in PropertiesFileSource/golden @@ -0,0 +1 @@ +Adding a new line 'api.key=some value' to the file '/etc/app.properties' \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in non-existent PropertiesFileSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in non-existent PropertiesFileSource/golden new file mode 100644 index 0000000..6b29869 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a key in non-existent PropertiesFileSource/golden @@ -0,0 +1 @@ +IMPOSSIBLE \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in CLIArgsSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in CLIArgsSource/golden new file mode 100644 index 0000000..9389d4e --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in CLIArgsSource/golden @@ -0,0 +1 @@ +Passing the cli arg: --database.connection.timeout="the value" \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in EnvSource/golden b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in EnvSource/golden new file mode 100644 index 0000000..8f37703 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.golden/Source explainNotFound/explains how to set a nested key in EnvSource/golden @@ -0,0 +1 @@ +Setting the environment variable: MYAPP_SERVER_SSL_ENABLED \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/ExplanationsSpec.hs b/packages/conferer/test/Conferer/Source/ExplanationsSpec.hs new file mode 100644 index 0000000..da638cf --- /dev/null +++ b/packages/conferer/test/Conferer/Source/ExplanationsSpec.hs @@ -0,0 +1,78 @@ +{-# LANGUAGE BlockArguments #-} +{-# LANGUAGE TemplateHaskell #-} +module Conferer.Source.ExplanationsSpec where + +import Test.Hspec +import Test.Hspec.Golden +import qualified Data.Text as Text + +import Conferer.Source +import Tests +import Conferer.Source.Env (fromEnvList) +import Conferer.Source.CLIArgs (fromArgs) +import Conferer.Source.PropertiesFile (fromFileContent) +import Conferer.Source.Namespaced (fromInner) +import Conferer.Source.Null (NullSource(..)) + +spec :: Spec +spec = do + describe "Source explainNotFound" $ do + $(betterGolden) "explains how to set a key in EnvSource" $ do + let source = fromEnvList [] "APP" + explanation = explainNotFound source "database.host" + pure explanation + + $(betterGolden) "explains how to set a nested key in EnvSource" $ do + let source = fromEnvList [] "MYAPP" + explanation = explainNotFound source "server.ssl.enabled" + pure explanation + + $(betterGolden) "explains how to set a key in CLIArgsSource" $ do + let source = fromArgs [] + explanation = explainNotFound source "server.port" + pure explanation + + $(betterGolden) "explains how to set a nested key in CLIArgsSource" $ do + let source = fromArgs [] + explanation = explainNotFound source "database.connection.timeout" + pure explanation + + $(betterGolden) "explains how to set a key in PropertiesFileSource" $ do + let source = fromFileContent "/etc/app.properties" "" + pure $ explainNotFound source "api.key" + + $(betterGolden) "explains how to set a key in non-existent PropertiesFileSource" $ do + let source = Source $ NullSource $ const "IMPOSSIBLE" + pure $ explainNotFound source "redis.host" + + $(betterGolden) "explains how to set a key in NamespacedSource wrapping Env" $ do + let envSource = fromEnvList [] "APP" + source = fromInner "worker" envSource + explanation = explainNotFound source "queue.name" + pure explanation + + $(betterGolden) "explains a found key in EnvSource" $ do + let source = fromEnvList [("APP_DATABASE_HOST", "localhost")] "APP" + explanation = explainSettedKey source "database.host" + pure explanation + + $(betterGolden) "explains a found key in CLIArgsSource" $ do + let source = fromArgs ["--server.port=8080"] + explanation = explainSettedKey source "server.port" + pure explanation + + $(betterGolden) "explains a found key in PropertiesFileSource" $ do + let source = fromFileContent "/etc/app.properties" "api.key=secret123" + explanation = explainSettedKey source "api.key" + pure explanation + + $(betterGolden) "explains a found nested key in EnvSource" $ do + let source = fromEnvList [("APP_SERVER_SSL_ENABLED", "true")] "APP" + explanation = explainSettedKey source "server.ssl.enabled" + pure explanation + + $(betterGolden) "explains a found key in NamespacedSource" $ do + let envSource = fromEnvList [("APP_WORKER_QUEUE_NAME", "jobs")] "APP" + source = fromInner "worker" envSource + explanation = explainSettedKey source "queue.name" + pure explanation diff --git a/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainNotFound/recommends adding the right line to the file/golden b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainNotFound/recommends adding the right line to the file/golden new file mode 100644 index 0000000..2c39164 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainNotFound/recommends adding the right line to the file/golden @@ -0,0 +1 @@ +Adding a new line 'some.key=some value' to the file 'file.properties' \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainSettedKey/recommends adding the right line to the file/golden b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainSettedKey/recommends adding the right line to the file/golden new file mode 100644 index 0000000..5743815 --- /dev/null +++ b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.golden/with a properties file config/#explainSettedKey/recommends adding the right line to the file/golden @@ -0,0 +1 @@ +key 'some.key' (on file 'file.properties') \ No newline at end of file diff --git a/packages/conferer/test/Conferer/Source/PropertiesFileSpec.hs b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.hs index ab55718..c31e370 100644 --- a/packages/conferer/test/Conferer/Source/PropertiesFileSpec.hs +++ b/packages/conferer/test/Conferer/Source/PropertiesFileSpec.hs @@ -40,4 +40,3 @@ spec = do s <- mk "some.key=thing" explainSettedKey s "some.key" `shouldBe` "key 'some.key' (on file 'file.properties')" - diff --git a/packages/conferer/test/Tests.hs b/packages/conferer/test/Tests.hs new file mode 100644 index 0000000..d9c767e --- /dev/null +++ b/packages/conferer/test/Tests.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +module Tests where + +import Test.Hspec +import Test.Hspec.Golden +import Test.Hspec.Core.Spec + +import Language.Haskell.TH +import Language.Haskell.TH.Syntax +import Data.List (intercalate) +import System.FilePath ((), dropExtension) +import System.Directory (removeFile) +import Control.Monad (when) +import Control.Exception +import qualified Data.Text as Text + +-- | Get the absolute path of the current module's source file +getCurrentModulePath :: Q Exp +getCurrentModulePath = do + loc <- qLocation + let path = loc_filename loc + [| path |] + + +golden2 + :: HasCallStack + => Bool -- ^ Should override old value + -> FilePath -- ^ Path of current file + -> String -- ^ Test description + -> IO String -- ^ Content (@return content@ for pure functions) + -> Spec +golden2 shouldOverride sourceFilePath description runAction = do + pathBasedOnHspec <- intercalate "/" <$> drop 1 <$> (++ [description]) <$> getSpecDescriptionPath + it description $ do + result <- runAction + let pathBasedOnCurrentFile = dropExtension sourceFilePath ++ ".golden" + let path = pathBasedOnCurrentFile pathBasedOnHspec + let goldenPath = path "golden" + let actualPath = path "actual" + + when shouldOverride $ removeFile goldenPath + + pure $ Golden + { output = result + , encodePretty = show + , writeToFile = writeFile + , readFromFile = readFile + , goldenFile = goldenPath + , actualFile = Just actualPath + , failFirstTime = False + } + +betterGoldenOverride :: Q Exp +betterGoldenOverride = do + loc <- qLocation + let path = loc_filename loc + [| golden2 True path |] + +betterGolden :: Q Exp +betterGolden = do + loc <- qLocation + let path = (++ ".golden") $ dropExtension $ loc_filename loc + [| golden2 False path |] + + +-- Helper functions + +captureException :: IO a -> IO String +captureException action = do + result <- try @SomeException action + case result of + Left e -> return $ + Text.unpack $ + fst $ + Text.breakOnEnd "HasCallStack backtrace" $ + Text.pack $ + displayException e + Right _ -> fail "Expected an exception but got a successful result"