Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/conferer/conferer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion packages/conferer/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

7 changes: 5 additions & 2 deletions packages/conferer/src/Conferer/FromConfig/Internal/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
, "'"
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Failed to interpret cli param '--enabled' with a value of 'maybe' as 'Bool'
HasCallStack backtrace
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Failed to interpret env var 'APP_PORT' with a value of 'not_a_number' as 'Int'
HasCallStack backtrace
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Failed to interpret key 'timeout' (on file '/tmp/test.properties') with a value of 'invalid' as 'Int'
HasCallStack backtrace
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Couldn't find a '[Char]'.

You can set it by either:
* Setting the environment variable: APP_DATABASE_HOST

HasCallStack backtrace
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
93 changes: 93 additions & 0 deletions packages/conferer/test/Conferer/ExceptionMessagesSpec.hs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Failed to interpret cli param '--server.port' with a value of 'not-a-number' as 'Int'
HasCallStack backtrace
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Failed to interpret key 'feature.enabled' (on file '/config/app.properties') with a value of 'maybe' as 'Bool'
HasCallStack backtrace
72 changes: 72 additions & 0 deletions packages/conferer/test/Conferer/MultiSourceErrorsSpec.hs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cli param '--server.port'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
env var 'APP_DATABASE_HOST'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Doing nothing, you can't use this source for that key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
key 'api.key' (on file '/etc/app.properties')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
env var 'APP_SERVER_SSL_ENABLED'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Passing the cli arg: --server.port="the value"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Setting the environment variable: APP_DATABASE_HOST
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Doing nothing, you can't use this source for that key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding a new line 'api.key=some value' to the file '/etc/app.properties'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IMPOSSIBLE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Passing the cli arg: --database.connection.timeout="the value"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Setting the environment variable: MYAPP_SERVER_SSL_ENABLED
Loading
Loading