移除所有测试代码和测试要求
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:
2026-02-11 17:13:42 +08:00
parent 804145332b
commit 353621d923
218 changed files with 11787 additions and 41983 deletions

View File

@@ -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
}
})
}

View File

@@ -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")
}
}

View File

@@ -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)))
}