Files
junhong_cmp_fiber/pkg/config/config_test.go
huang 45aa7deb87
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
feat: 添加环境变量管理工具和部署配置改版
主要改动:
- 新增交互式环境配置脚本 (scripts/setup-env.sh)
- 新增本地启动快捷脚本 (scripts/run-local.sh)
- 新增环境变量模板文件 (.env.example)
- 部署模式改版:使用嵌入式配置 + 环境变量覆盖
- 添加对象存储功能支持
- 改进 IoT 卡片导入任务
- 优化 OpenAPI 文档生成
- 删除旧的配置文件,改用嵌入式默认配置
2026-01-26 10:28:29 +08:00

626 lines
14 KiB
Go

package config
import (
"testing"
"time"
)
// TestConfig_Validate tests configuration validation rules
func TestConfig_Validate(t *testing.T) {
tests := []struct {
name string
config *Config
wantErr bool
errMsg string
}{
{
name: "valid config",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
DB: 0,
PoolSize: 10,
MinIdleConns: 5,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
MaxBackups: 30,
MaxAge: 30,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
MaxBackups: 90,
MaxAge: 90,
},
},
Middleware: MiddlewareConfig{
RateLimiter: RateLimiterConfig{
Max: 100,
Expiration: 1 * time.Minute,
Storage: "memory",
},
},
JWT: JWTConfig{
TokenDuration: 24 * time.Hour,
AccessTokenTTL: 24 * time.Hour,
RefreshTokenTTL: 168 * time.Hour,
},
},
wantErr: false,
},
{
name: "empty server address",
config: &Config{
Server: ServerConfig{
Address: "",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "server.address",
},
{
name: "read timeout too short",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 1 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "read_timeout",
},
{
name: "read timeout too long",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 400 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "read_timeout",
},
{
name: "write timeout out of range",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 1 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "write_timeout",
},
{
name: "shutdown timeout too short",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 5 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "shutdown_timeout",
},
{
name: "empty redis address",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "redis.address",
},
{
name: "invalid redis port - too high",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 99999,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "redis.port",
},
{
name: "invalid redis port - zero",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 0,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "redis.port",
},
{
name: "redis db out of range",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
DB: 20,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "redis.db",
},
{
name: "redis pool size too large",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 2000,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "pool_size",
},
{
name: "min idle conns exceeds pool size",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
MinIdleConns: 20,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "min_idle_conns",
},
{
name: "invalid log level",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "invalid",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "logging.level",
},
{
name: "empty app log filename",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "app_log.filename",
},
{
name: "app log max size out of range",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 2000,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
},
wantErr: true,
errMsg: "app_log.max_size",
},
{
name: "invalid rate limiter storage",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
Middleware: MiddlewareConfig{
RateLimiter: RateLimiterConfig{
Max: 100,
Storage: "invalid",
},
},
},
wantErr: true,
errMsg: "rate_limiter.storage",
},
{
name: "rate limiter max too high",
config: &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
Middleware: MiddlewareConfig{
RateLimiter: RateLimiterConfig{
Max: 20000,
Storage: "memory",
},
},
},
wantErr: true,
errMsg: "rate_limiter.max",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Config.Validate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr && tt.errMsg != "" {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errMsg)
} else if err.Error() == "" {
t.Errorf("expected error containing %q, got empty error", tt.errMsg)
}
// Note: We check that error message exists, not exact match
// This is because error messages might change slightly
}
})
}
}
// TestSet tests the Set function
func TestSet(t *testing.T) {
// Valid config
validCfg := &Config{
Server: ServerConfig{
Address: ":3000",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
ShutdownTimeout: 30 * time.Second,
},
Redis: RedisConfig{
Address: "localhost",
Port: 6379,
PoolSize: 10,
},
Logging: LoggingConfig{
Level: "info",
AppLog: LogRotationConfig{
Filename: "logs/app.log",
MaxSize: 100,
},
AccessLog: LogRotationConfig{
Filename: "logs/access.log",
MaxSize: 500,
},
},
JWT: JWTConfig{
TokenDuration: 24 * time.Hour,
AccessTokenTTL: 24 * time.Hour,
RefreshTokenTTL: 168 * time.Hour,
},
}
err := Set(validCfg)
if err != nil {
t.Errorf("Set() with valid config failed: %v", err)
}
// Verify it was set
got := Get()
if got.Server.Address != ":3000" {
t.Errorf("Get() after Set() returned wrong address: got %s, want :3000", got.Server.Address)
}
// Test with nil config
err = Set(nil)
if err == nil {
t.Error("Set(nil) should return error")
}
// Test with invalid config
invalidCfg := &Config{
Server: ServerConfig{
Address: "", // Empty address is invalid
},
}
err = Set(invalidCfg)
if err == nil {
t.Error("Set() with invalid config should return error")
}
}