Skip to content

Commit

Permalink
fix: Fix tests and make the new config work properly
Browse files Browse the repository at this point in the history
* Using the `reflect` package to set automatic defaults for all config
  values
* Because of the above, we should be able to load all config values from
  the environment
* The `ConfigFileNotFoundError` does not work as expected when
  `SetConfigFile` is used, so I created a utility function instead. ref: spf13/viper#1491
  • Loading branch information
tarkatronic committed Oct 24, 2023
1 parent 4e77cfa commit c98d4d7
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 45 deletions.
53 changes: 36 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

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

"github.com/underdog-tech/vulnbot/logger"
Expand All @@ -28,38 +30,55 @@ type Config struct {
Team []TeamConfig
}

func fileExists(fname string) bool {
if _, err := os.Stat(fname); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

func GetUserConfig(configFile string) (Config, error) {
log := logger.Get()

userCfg := Config{}

// Set up env var overrides
replacer := strings.NewReplacer("-", "_")
viper.SetEnvKeyReplacer(replacer)
viper.SetEnvPrefix("vulnbot")
viper.AutomaticEnv()
// Use reflection to register all config fields in Viper to set up defaults
cfgFields := reflect.ValueOf(userCfg)
cfgType := cfgFields.Type()

for i := 0; i < cfgFields.NumField(); i++ {
viper.SetDefault(cfgType.Field(i).Name, cfgFields.Field(i).Interface())
}

// Load the main config file
if !fileExists(configFile) {
log.Fatal().Str("config", configFile).Msg("Config file not found.")

Check warning on line 57 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L57

Added line #L57 was not covered by tests
}
viper.SetConfigFile(configFile)
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
log.Fatal().Str("config", configFile).Err(err).Msg("Config file not found.")
} else {
log.Fatal().Err(err).Msg("Error reading config file.")
}
log.Fatal().Err(err).Msg("Error reading config file.")

Check warning on line 61 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L61

Added line #L61 was not covered by tests
}
viper.Unmarshal(&userCfg)

// (Optionally) Load a .env file
viper.SetConfigFile("./.env")
viper.SetConfigType("env")
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
log.Warn().Msg("No .env file found; not loaded.")
} else {
if fileExists("./.env") {
viper.SetConfigFile("./.env")
viper.SetConfigType("env")
if err := viper.ReadInConfig(); err != nil {
log.Error().Err(err).Msg("Error loading .env file.")
}

Check warning on line 70 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L66-L70

Added lines #L66 - L70 were not covered by tests
} else {
log.Warn().Msg("No .env file found; not loaded.")
}

// Set up env var overrides
replacer := strings.NewReplacer("-", "_")
viper.SetEnvKeyReplacer(replacer)
viper.SetEnvPrefix("vulnbot")
viper.AutomaticEnv()

// Finally, copy all loaded values into the config object
viper.Unmarshal(&userCfg)

Check failure on line 82 in config/config.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `viper.Unmarshal` is not checked (errcheck)

return userCfg, nil
Expand Down
47 changes: 19 additions & 28 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,38 +69,29 @@ func getCurrentDir() (string, error) {
return cwd, nil
}

func TestLoadConfig(t *testing.T) {
expectedSlackChannel := "testing_slack_channel"
func TestGetUserConfigFromFile(t *testing.T) {
currentDir, err := getCurrentDir()
if err != nil {
assert.Error(t, err)
}
assert.Nil(t, err)
testDataPath := filepath.Join(currentDir, "/testdata/test_config.toml")

var cfg config.Config
_ = config.LoadConfig(config.ViperParams{
Output: &cfg,
ConfigPath: &testDataPath,
})

assert.IsType(t, config.Config{}, cfg)
assert.Equal(t, expectedSlackChannel, cfg.Default_slack_channel)
cfg, err := config.GetUserConfig(testDataPath)
assert.Nil(t, err)
assert.Equal(t, "testing_slack_channel", cfg.Default_slack_channel)
assert.Equal(t, []config.EcosystemConfig{{Label: "Go", Slack_emoji: ":golang:"}}, cfg.Ecosystem)
}

func TestLoadEnv(t *testing.T) {
expectedSlackAuthToken := "testing"
currentDir, err := getCurrentDir()
if err != nil {
assert.Error(t, err)
}
testDataPath := filepath.Join(currentDir, "/testdata/config.env")
func TestGetUserConfigFromEnv(t *testing.T) {
t.Setenv("VULNBOT_DISABLE_SLACK", "1")
t.Setenv("VULNBOT_GITHUB_ORG", "hitchhikers")
// This should override the config file
t.Setenv("VULNBOT_DEFAULT_SLACK_CHANNEL", "other_slack_channel")

var env config.Env
_ = config.LoadEnv(config.ViperParams{
EnvFileName: &testDataPath,
Output: &env,
})
currentDir, err := getCurrentDir()
assert.Nil(t, err)
testDataPath := filepath.Join(currentDir, "/testdata/test_config.toml")
cfg, err := config.GetUserConfig(testDataPath)
assert.Nil(t, err)

assert.IsType(t, config.Env{}, env)
assert.Equal(t, expectedSlackAuthToken, env.SlackAuthToken)
assert.True(t, cfg.Disable_slack)
assert.Equal(t, "hitchhikers", cfg.Github_org)
assert.Equal(t, "other_slack_channel", cfg.Default_slack_channel)
}

0 comments on commit c98d4d7

Please sign in to comment.