移除所有测试代码和测试要求
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m33s
**变更说明**: - 删除所有 *_test.go 文件(单元测试、集成测试、验收测试、流程测试) - 删除整个 tests/ 目录 - 更新 CLAUDE.md:用"测试禁令"章节替换所有测试要求 - 删除测试生成 Skill (openspec-generate-acceptance-tests) - 删除测试生成命令 (opsx:gen-tests) - 更新 tasks.md:删除所有测试相关任务 **新规范**: - ❌ 禁止编写任何形式的自动化测试 - ❌ 禁止创建 *_test.go 文件 - ❌ 禁止在任务中包含测试相关工作 - ✅ 仅当用户明确要求时才编写测试 **原因**: 业务系统的正确性通过人工验证和生产环境监控保证,测试代码维护成本高于价值。 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// BenchmarkGet 测试配置获取性能
|
||||
func BenchmarkGet(b *testing.B) {
|
||||
// 设置配置文件路径
|
||||
_ = os.Setenv(constants.EnvConfigPath, "../../configs/config.yaml")
|
||||
defer func() { _ = os.Unsetenv(constants.EnvConfigPath) }()
|
||||
|
||||
// 初始化配置
|
||||
_, err := Load()
|
||||
if err != nil {
|
||||
b.Fatalf("加载配置失败: %v", err)
|
||||
}
|
||||
|
||||
b.Run("GetServer", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Get().Server
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("GetRedis", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Get().Redis
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("GetLogging", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Get().Logging
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("GetMiddleware", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Get().Middleware
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("FullConfigAccess", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cfg := Get()
|
||||
_ = cfg.Server.Address
|
||||
_ = cfg.Redis.Address
|
||||
_ = cfg.Logging.Level
|
||||
_ = cfg.Middleware.EnableRateLimiter
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,625 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoad_EmbeddedConfig(t *testing.T) {
|
||||
clearEnvVars(t)
|
||||
setRequiredEnvVars(t)
|
||||
defer clearEnvVars(t)
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load() 失败: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Server.Address != ":3000" {
|
||||
t.Errorf("server.address 期望 :3000, 实际 %s", cfg.Server.Address)
|
||||
}
|
||||
if cfg.Server.ReadTimeout != 30*time.Second {
|
||||
t.Errorf("server.read_timeout 期望 30s, 实际 %v", cfg.Server.ReadTimeout)
|
||||
}
|
||||
if cfg.Logging.Level != "info" {
|
||||
t.Errorf("logging.level 期望 info, 实际 %s", cfg.Logging.Level)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_EnvOverride(t *testing.T) {
|
||||
clearEnvVars(t)
|
||||
setRequiredEnvVars(t)
|
||||
defer clearEnvVars(t)
|
||||
|
||||
os.Setenv("JUNHONG_SERVER_ADDRESS", ":8080")
|
||||
os.Setenv("JUNHONG_LOGGING_LEVEL", "debug")
|
||||
defer func() {
|
||||
os.Unsetenv("JUNHONG_SERVER_ADDRESS")
|
||||
os.Unsetenv("JUNHONG_LOGGING_LEVEL")
|
||||
}()
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load() 失败: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Server.Address != ":8080" {
|
||||
t.Errorf("server.address 期望 :8080, 实际 %s", cfg.Server.Address)
|
||||
}
|
||||
if cfg.Logging.Level != "debug" {
|
||||
t.Errorf("logging.level 期望 debug, 实际 %s", cfg.Logging.Level)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_MissingRequired(t *testing.T) {
|
||||
clearEnvVars(t)
|
||||
defer clearEnvVars(t)
|
||||
|
||||
_, err := Load()
|
||||
if err == nil {
|
||||
t.Fatal("Load() 缺少必填配置时应返回错误")
|
||||
}
|
||||
|
||||
expectedFields := []string{"database.host", "database.user", "database.password", "database.dbname", "redis.address", "jwt.secret_key"}
|
||||
for _, field := range expectedFields {
|
||||
if !containsString(err.Error(), field) {
|
||||
t.Errorf("错误信息应包含 %q, 实际: %s", field, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_PartialRequired(t *testing.T) {
|
||||
clearEnvVars(t)
|
||||
defer clearEnvVars(t)
|
||||
|
||||
os.Setenv("JUNHONG_DATABASE_HOST", "localhost")
|
||||
os.Setenv("JUNHONG_DATABASE_USER", "user")
|
||||
|
||||
_, err := Load()
|
||||
if err == nil {
|
||||
t.Fatal("Load() 部分必填配置缺失时应返回错误")
|
||||
}
|
||||
|
||||
if containsString(err.Error(), "database.host") {
|
||||
t.Error("database.host 已设置,不应在错误信息中")
|
||||
}
|
||||
if containsString(err.Error(), "database.user") {
|
||||
t.Error("database.user 已设置,不应在错误信息中")
|
||||
}
|
||||
if !containsString(err.Error(), "database.password") {
|
||||
t.Error("database.password 未设置,应在错误信息中")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_GlobalConfig(t *testing.T) {
|
||||
clearEnvVars(t)
|
||||
setRequiredEnvVars(t)
|
||||
defer clearEnvVars(t)
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load() 失败: %v", err)
|
||||
}
|
||||
|
||||
globalCfg := Get()
|
||||
if globalCfg == nil {
|
||||
t.Fatal("Get() 返回 nil")
|
||||
}
|
||||
|
||||
if globalCfg.Server.Address != cfg.Server.Address {
|
||||
t.Errorf("全局配置与返回配置不一致")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequired(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "all required set",
|
||||
cfg: &Config{
|
||||
Database: DatabaseConfig{
|
||||
Host: "localhost",
|
||||
User: "user",
|
||||
Password: "pass",
|
||||
DBName: "db",
|
||||
},
|
||||
Redis: RedisConfig{Address: "localhost"},
|
||||
JWT: JWTConfig{SecretKey: "12345678901234567890123456789012"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing database host",
|
||||
cfg: &Config{
|
||||
Database: DatabaseConfig{
|
||||
User: "user",
|
||||
Password: "pass",
|
||||
DBName: "db",
|
||||
},
|
||||
Redis: RedisConfig{Address: "localhost"},
|
||||
JWT: JWTConfig{SecretKey: "12345678901234567890123456789012"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing redis address",
|
||||
cfg: &Config{
|
||||
Database: DatabaseConfig{
|
||||
Host: "localhost",
|
||||
User: "user",
|
||||
Password: "pass",
|
||||
DBName: "db",
|
||||
},
|
||||
Redis: RedisConfig{},
|
||||
JWT: JWTConfig{SecretKey: "12345678901234567890123456789012"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing jwt secret",
|
||||
cfg: &Config{
|
||||
Database: DatabaseConfig{
|
||||
Host: "localhost",
|
||||
User: "user",
|
||||
Password: "pass",
|
||||
DBName: "db",
|
||||
},
|
||||
Redis: RedisConfig{Address: "localhost"},
|
||||
JWT: JWTConfig{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.cfg.ValidateRequired()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateRequired() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setRequiredEnvVars(t *testing.T) {
|
||||
t.Helper()
|
||||
os.Setenv("JUNHONG_DATABASE_HOST", "localhost")
|
||||
os.Setenv("JUNHONG_DATABASE_USER", "testuser")
|
||||
os.Setenv("JUNHONG_DATABASE_PASSWORD", "testpass")
|
||||
os.Setenv("JUNHONG_DATABASE_DBNAME", "testdb")
|
||||
os.Setenv("JUNHONG_REDIS_ADDRESS", "localhost")
|
||||
os.Setenv("JUNHONG_JWT_SECRET_KEY", "12345678901234567890123456789012")
|
||||
}
|
||||
|
||||
func clearEnvVars(t *testing.T) {
|
||||
t.Helper()
|
||||
envVars := []string{
|
||||
"JUNHONG_DATABASE_HOST",
|
||||
"JUNHONG_DATABASE_PORT",
|
||||
"JUNHONG_DATABASE_USER",
|
||||
"JUNHONG_DATABASE_PASSWORD",
|
||||
"JUNHONG_DATABASE_DBNAME",
|
||||
"JUNHONG_REDIS_ADDRESS",
|
||||
"JUNHONG_REDIS_PORT",
|
||||
"JUNHONG_REDIS_PASSWORD",
|
||||
"JUNHONG_JWT_SECRET_KEY",
|
||||
"JUNHONG_SERVER_ADDRESS",
|
||||
"JUNHONG_LOGGING_LEVEL",
|
||||
}
|
||||
for _, v := range envVars {
|
||||
os.Unsetenv(v)
|
||||
}
|
||||
}
|
||||
|
||||
func containsString(s, substr string) bool {
|
||||
return len(s) >= len(substr) && (s == substr || len(s) > 0 && (s[:len(substr)] == substr || containsString(s[1:], substr)))
|
||||
}
|
||||
Reference in New Issue
Block a user