Files
junhong_cmp_fiber/pkg/config/config_test.go
2025-11-11 15:53:01 +08:00

616 lines
13 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",
},
},
},
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,
},
},
}
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")
}
}