Skip to content

Commit

Permalink
chore: provide methods to access config init errors (#406)
Browse files Browse the repository at this point in the history
Co-authored-by: Francesco Casula <fracasula@users.noreply.github.com>
  • Loading branch information
lvrach and fracasula committed Apr 8, 2024
1 parent 08eef2f commit 8579877
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 19 deletions.
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ type Config struct {
reloadableVars map[string]any
reloadableVarsMisuses map[string]string
reloadableVarsLock sync.RWMutex // used to protect both the reloadableVars and reloadableVarsMisuses maps
configPath string
configPathErr error
godotEnvErr error
}

// GetBool gets bool value from config
Expand Down
53 changes: 53 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"context"
"fmt"
"os"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -516,6 +517,15 @@ func TestGetEnvThroughViper(t *testing.T) {
require.Equal(t, expectedValue, tc.GetString("Key.Var1VarVar", ""))
})

t.Run("with env prefix", func(t *testing.T) {
envPrefix := "ANOTHER_PREFIX"

tc := New(WithEnvPrefix(envPrefix))
t.Setenv(envPrefix+"_KEY_VAR1_VAR_VAR", expectedValue)

require.Equal(t, expectedValue, tc.GetString("Key.Var1VarVar", ""))
})

t.Run("detects uppercase env variables", func(t *testing.T) {
t.Setenv("SOMEENVVARIABLE", expectedValue)
tc := New()
Expand Down Expand Up @@ -671,3 +681,46 @@ func TestConfigLocking(t *testing.T) {

require.NoError(t, g.Wait())
}

func TestConfigLoad(t *testing.T) {
// create a temporary file:
f, err := os.CreateTemp("", "*config.yaml")
require.NoError(t, err)
defer os.Remove(f.Name())

t.Setenv("CONFIG_PATH", f.Name())
c := New()
require.NoError(t, err)

t.Run("successfully loaded config file", func(t *testing.T) {
configFile, err := c.ConfigFileUsed()
require.NoError(t, err)
require.Equal(t, f.Name(), configFile)
})

err = os.Remove(f.Name())
require.NoError(t, err)

t.Run("attempt to load non-existent config file", func(t *testing.T) {
c := New()
configFile, err := c.ConfigFileUsed()
require.Error(t, err)
require.Equal(t, f.Name(), configFile)
})

t.Run("dot env error", func(t *testing.T) {
c := New()
err := c.DotEnvLoaded()
require.Error(t, err)
})

t.Run("dot env found", func(t *testing.T) {
c := New()
f, err := os.Create(".env")
require.NoError(t, err)
defer os.Remove(f.Name())

err = c.DotEnvLoaded()
require.Error(t, err)
})
}
38 changes: 19 additions & 19 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package config

import (
"fmt"
"os"
"reflect"
"strings"
"time"

"github.com/fsnotify/fsnotify"
Expand All @@ -17,9 +15,7 @@ func (c *Config) load() {
c.hotReloadableConfig = make(map[string][]*configValue)
c.envs = make(map[string]string)

if err := godotenv.Load(); err != nil && !isTest() {
fmt.Println("INFO: No .env file found.")
}
c.godotEnvErr = godotenv.Load()

configPath := getEnv("CONFIG_PATH", "./config/config.yaml")

Expand All @@ -28,11 +24,12 @@ func (c *Config) load() {
bindLegacyEnv(v)

v.SetConfigFile(configPath)
err := v.ReadInConfig() // Find and read the config file
// Don't panic if config.yaml is not found or error with parsing. Use the default config values instead
if err != nil && !isTest() {
fmt.Printf("[Config] :: Failed to parse config file from path %q, using default values: %v\n", configPath, err)
}

// Find and read the config file
// If config.yaml is not found or error with parsing. Use the default config values instead
c.configPathErr = v.ReadInConfig()
c.configPath = v.ConfigFileUsed()

v.OnConfigChange(func(e fsnotify.Event) {
c.onConfigChange()
})
Expand All @@ -41,6 +38,18 @@ func (c *Config) load() {
c.v = v
}

// ConfigFileUsed returns the file used to load the config.
// If we failed to load the config file, it also returns an error.
func (c *Config) ConfigFileUsed() (string, error) {
return c.configPath, c.configPathErr
}

// DotEnvLoaded returns an error if there was an error loading the .env file.
// It returns nil otherwise.
func (c *Config) DotEnvLoaded() error {
return c.godotEnvErr
}

func (c *Config) onConfigChange() {
defer func() {
if r := recover(); r != nil {
Expand Down Expand Up @@ -230,12 +239,3 @@ func mapDeepEqual[K comparable, V any](a, b map[K]V) bool {
}
return true
}

func isTest() bool {
for _, arg := range os.Args {
if strings.HasPrefix(arg, "-test.") {
return true
}
}
return false
}

0 comments on commit 8579877

Please sign in to comment.