1
0
Files
nex/backend/internal/config/config_test.go
lanyuanxiaoyao cfb0edf802 feat: 引入 Viper 实现多层配置管理
引入 Viper 配置管理框架,支持 CLI 参数、环境变量、配置文件和默认值四种配置方式。

主要变更:
- 引入 Viper、pflag、validator、mapstructure 依赖
- 实现配置优先级:CLI > ENV > File > Default
- 所有 13 个配置项支持 CLI 参数和环境变量
- 规范化命名:server.port → NEX_SERVER_PORT → --server-port
- 使用结构体验证器进行配置验证
- 添加配置摘要输出功能

新增能力:
- cli-config: 命令行参数配置支持
- env-config: 环境变量配置支持(符合 12-Factor App)
- config-priority: 配置优先级管理

修改能力:
- config-management: 扩展为多层配置源支持

使用示例:
  ./server --server-port 9000 --log-level debug
  export NEX_SERVER_PORT=9000 && ./server
  ./server --config /path/to/custom.yaml
2026-04-20 18:04:42 +08:00

235 lines
5.8 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestDefaultConfig(t *testing.T) {
cfg := DefaultConfig()
require.NotNil(t, cfg)
assert.Equal(t, 9826, cfg.Server.Port)
assert.Equal(t, 30*time.Second, cfg.Server.ReadTimeout)
assert.Equal(t, 30*time.Second, cfg.Server.WriteTimeout)
assert.Equal(t, 10, cfg.Database.MaxIdleConns)
assert.Equal(t, 100, cfg.Database.MaxOpenConns)
assert.Equal(t, 1*time.Hour, cfg.Database.ConnMaxLifetime)
assert.Equal(t, "info", cfg.Log.Level)
assert.Equal(t, 100, cfg.Log.MaxSize)
assert.Equal(t, 10, cfg.Log.MaxBackups)
assert.Equal(t, 30, cfg.Log.MaxAge)
assert.Equal(t, true, cfg.Log.Compress)
}
func TestConfig_Validate(t *testing.T) {
tests := []struct {
name string
modify func(*Config)
wantErr bool
errMsg string
}{
{
name: "默认配置有效",
modify: func(c *Config) {},
wantErr: false,
},
{
name: "端口号为0无效",
modify: func(c *Config) { c.Server.Port = 0 },
wantErr: true,
errMsg: "配置验证失败",
},
{
name: "端口号超出范围无效",
modify: func(c *Config) { c.Server.Port = 70000 },
wantErr: true,
errMsg: "配置验证失败",
},
{
name: "端口号为1有效",
modify: func(c *Config) { c.Server.Port = 1 },
wantErr: false,
},
{
name: "端口号为65535有效",
modify: func(c *Config) { c.Server.Port = 65535 },
wantErr: false,
},
{
name: "无效日志级别",
modify: func(c *Config) { c.Log.Level = "invalid" },
wantErr: true,
errMsg: "配置验证失败",
},
{
name: "debug级别有效",
modify: func(c *Config) { c.Log.Level = "debug" },
wantErr: false,
},
{
name: "warn级别有效",
modify: func(c *Config) { c.Log.Level = "warn" },
wantErr: false,
},
{
name: "error级别有效",
modify: func(c *Config) { c.Log.Level = "error" },
wantErr: false,
},
{
name: "数据库路径为空无效",
modify: func(c *Config) { c.Database.Path = "" },
wantErr: true,
errMsg: "配置验证失败",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := DefaultConfig()
tt.modify(cfg)
err := cfg.Validate()
if tt.wantErr {
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.errMsg)
} else {
assert.NoError(t, err)
}
})
}
}
func TestGetConfigDir(t *testing.T) {
dir, err := GetConfigDir()
require.NoError(t, err)
assert.NotEmpty(t, dir)
assert.Contains(t, dir, ".nex")
}
func TestGetDBPath(t *testing.T) {
path, err := GetDBPath()
require.NoError(t, err)
assert.NotEmpty(t, path)
assert.Contains(t, path, "config.db")
}
func TestGetConfigPath(t *testing.T) {
path, err := GetConfigPath()
require.NoError(t, err)
assert.NotEmpty(t, path)
assert.Contains(t, path, "config.yaml")
}
func TestSaveAndLoadConfig(t *testing.T) {
// 使用临时目录覆盖配置路径
dir := t.TempDir()
cfg := &Config{
Server: ServerConfig{
Port: 9999,
ReadTimeout: 10 * time.Second,
WriteTimeout: 20 * time.Second,
},
Database: DatabaseConfig{
Path: filepath.Join(dir, "test.db"),
MaxIdleConns: 5,
MaxOpenConns: 50,
ConnMaxLifetime: 30 * time.Minute,
},
Log: LogConfig{
Level: "debug",
Path: filepath.Join(dir, "log"),
MaxSize: 50,
MaxBackups: 5,
MaxAge: 7,
Compress: false,
},
}
// 保存配置
configPath := filepath.Join(dir, "config.yaml")
data, err := yaml.Marshal(cfg)
require.NoError(t, err)
err = os.WriteFile(configPath, data, 0644)
require.NoError(t, err)
// 加载配置
data, err = os.ReadFile(configPath)
require.NoError(t, err)
loaded := &Config{}
err = yaml.Unmarshal(data, loaded)
require.NoError(t, err)
assert.Equal(t, cfg.Server.Port, loaded.Server.Port)
assert.Equal(t, cfg.Log.Level, loaded.Log.Level)
assert.Equal(t, cfg.Database.MaxIdleConns, loaded.Database.MaxIdleConns)
assert.Equal(t, cfg.Log.Compress, loaded.Log.Compress)
}
func TestCLIConfig(t *testing.T) {
// 测试 CLI 参数配置(简化版本)
// 注意:由于 flag.Parse 只能调用一次,这里只测试配置加载流程
t.Run("配置加载流程", func(t *testing.T) {
// 使用默认配置路径测试
cfg := DefaultConfig()
require.NotNil(t, cfg)
// 验证默认值正确
assert.Equal(t, 9826, cfg.Server.Port)
assert.Equal(t, "info", cfg.Log.Level)
})
}
func TestEnvConfig(t *testing.T) {
// 测试环境变量配置(简化版本)
t.Run("环境变量前缀", func(t *testing.T) {
// 验证环境变量前缀设置正确
// 实际的环境变量测试需要独立的进程,这里只验证配置结构
cfg := DefaultConfig()
require.NotNil(t, cfg)
assert.Equal(t, 9826, cfg.Server.Port)
})
}
func TestConfigPriority(t *testing.T) {
// 测试配置优先级(简化版本)
t.Run("默认值设置", func(t *testing.T) {
cfg := DefaultConfig()
require.NotNil(t, cfg)
// 验证所有默认值
assert.Equal(t, 9826, cfg.Server.Port)
assert.Equal(t, 30*time.Second, cfg.Server.ReadTimeout)
assert.Equal(t, 30*time.Second, cfg.Server.WriteTimeout)
assert.Equal(t, 10, cfg.Database.MaxIdleConns)
assert.Equal(t, 100, cfg.Database.MaxOpenConns)
assert.Equal(t, 1*time.Hour, cfg.Database.ConnMaxLifetime)
assert.Equal(t, "info", cfg.Log.Level)
assert.Equal(t, 100, cfg.Log.MaxSize)
assert.Equal(t, 10, cfg.Log.MaxBackups)
assert.Equal(t, 30, cfg.Log.MaxAge)
assert.Equal(t, true, cfg.Log.Compress)
})
}
func TestPrintSummary(t *testing.T) {
// 测试配置摘要输出
t.Run("打印配置摘要", func(t *testing.T) {
cfg := DefaultConfig()
// PrintSummary 只是打印,不会返回错误
// 这里主要验证不会 panic
assert.NotPanics(t, func() {
cfg.PrintSummary()
})
})
}