Skip to content

Commit 8ea3308

Browse files
committed
refactor: refactor config loading and strengthen error handling in tests
- Refactor config loading logic to avoid code duplication and improve error handling - Return detailed error messages when config file reading or parsing fails - Return nil for config on errors rather than partially initialized objects - Add tests for missing config files, invalid YAML content, and default config loading failures - Improve coverage for error scenarios and error messages in config tests Signed-off-by: appleboy <[email protected]>
1 parent bfa1da2 commit 8ea3308

File tree

2 files changed

+75
-20
lines changed

2 files changed

+75
-20
lines changed

config/config.go

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,6 @@ func setDefault() {
318318

319319
// LoadConf load config from file and read in environment variables that match
320320
func LoadConf(confPath ...string) (*ConfYaml, error) {
321-
conf := &ConfYaml{}
322-
323321
// load default values
324322
setDefault()
325323

@@ -328,31 +326,45 @@ func LoadConf(confPath ...string) (*ConfYaml, error) {
328326
viper.SetEnvPrefix("gorush") // will be uppercased automatically
329327
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
330328

329+
// If config path is provided, load from file
331330
if len(confPath) > 0 && confPath[0] != "" {
332331
content, err := os.ReadFile(confPath[0])
333332
if err != nil {
334-
return conf, err
333+
return nil, fmt.Errorf("failed to read config file %s: %w", confPath[0], err)
335334
}
336335

337336
if err := viper.ReadConfig(bytes.NewBuffer(content)); err != nil {
338-
return conf, err
339-
}
340-
} else {
341-
// Search config in home directory with name ".gorush" (without extension).
342-
viper.AddConfigPath("/etc/gorush/")
343-
viper.AddConfigPath("$HOME/.gorush")
344-
viper.AddConfigPath(".")
345-
viper.SetConfigName("config")
346-
347-
// If a config file is found, read it in.
348-
if err := viper.ReadInConfig(); err == nil {
349-
fmt.Println("Using config file:", viper.ConfigFileUsed())
350-
} else if err := viper.ReadConfig(bytes.NewBuffer(defaultConf)); err != nil {
351-
// load default config
352-
return conf, err
337+
return nil, fmt.Errorf("failed to parse config file %s: %w", confPath[0], err)
353338
}
339+
340+
// Early return after successfully loading config from file
341+
return loadConfigFromViper()
342+
}
343+
344+
// Search config in home directory with name ".gorush" (without extension).
345+
viper.AddConfigPath("/etc/gorush/")
346+
viper.AddConfigPath("$HOME/.gorush")
347+
viper.AddConfigPath(".")
348+
viper.SetConfigName("config")
349+
350+
// If a config file is found, read it in.
351+
if err := viper.ReadInConfig(); err == nil {
352+
fmt.Println("Using config file:", viper.ConfigFileUsed())
353+
return loadConfigFromViper()
354354
}
355355

356+
// Try to load default config as fallback
357+
if err := viper.ReadConfig(bytes.NewBuffer(defaultConf)); err != nil {
358+
return nil, fmt.Errorf("failed to load default config and no config file found: %w", err)
359+
}
360+
361+
return loadConfigFromViper()
362+
}
363+
364+
// loadConfigFromViper extracts config loading logic to avoid code duplication
365+
func loadConfigFromViper() (*ConfYaml, error) {
366+
conf := &ConfYaml{}
367+
356368
// Core
357369
conf.Core.Address = viper.GetString("core.address")
358370
conf.Core.Port = viper.GetString("core.port")

config/config_test.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,53 @@ import (
1212

1313
// Test file is missing
1414
func TestMissingFile(t *testing.T) {
15-
filename := "test"
16-
_, err := LoadConf(filename)
15+
filename := "nonexistent_file.yml"
16+
conf, err := LoadConf(filename)
1717

18+
assert.Nil(t, conf)
1819
assert.NotNil(t, err)
20+
// Check that the error message includes the filename and describes the issue
21+
assert.Contains(t, err.Error(), "failed to read config file")
22+
assert.Contains(t, err.Error(), filename)
23+
}
24+
25+
// Test invalid YAML content
26+
func TestInvalidYAMLFile(t *testing.T) {
27+
// Create a temporary file with invalid YAML
28+
tmpFile := "test_invalid.yml"
29+
content := []byte("invalid: yaml: content: [unclosed")
30+
31+
// Write invalid content to a temporary file
32+
err := os.WriteFile(tmpFile, content, 0o644)
33+
assert.NoError(t, err)
34+
defer os.Remove(tmpFile) // Clean up
35+
36+
conf, err := LoadConf(tmpFile)
37+
38+
assert.Nil(t, conf)
39+
assert.NotNil(t, err)
40+
// Check that the error message includes the filename and describes parsing failure
41+
assert.Contains(t, err.Error(), "failed to parse config file")
42+
assert.Contains(t, err.Error(), tmpFile)
43+
}
44+
45+
// Test improved error messages for default config loading
46+
func TestDefaultConfigLoadFailure(t *testing.T) {
47+
// Backup original defaultConf
48+
originalDefaultConf := defaultConf
49+
defer func() {
50+
defaultConf = originalDefaultConf
51+
}()
52+
53+
// Set invalid default config
54+
defaultConf = []byte(`invalid: yaml: [unclosed`)
55+
56+
conf, err := LoadConf()
57+
58+
assert.Nil(t, conf)
59+
assert.NotNil(t, err)
60+
// Check that the error message describes the default config loading failure
61+
assert.Contains(t, err.Error(), "failed to load default config and no config file found")
1962
}
2063

2164
func TestEmptyConfig(t *testing.T) {

0 commit comments

Comments
 (0)