first commit
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped

This commit is contained in:
Rogee
2025-09-28 10:05:07 +08:00
commit 7fcabe0225
481 changed files with 125127 additions and 0 deletions

View File

@@ -0,0 +1,364 @@
package unit
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
"github.com/subconverter-go/internal/config"
)
// TestConfigManagerCreation 测试配置管理器创建
func TestConfigManagerCreation(t *testing.T) {
convey.Convey("配置管理器创建测试", t, func() {
convey.Convey("创建默认配置管理器", func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
convey.So(manager, convey.ShouldNotBeNil)
// 验证默认配置
cfg := manager.GetConfig()
convey.So(cfg, convey.ShouldNotBeNil)
convey.So(cfg.Server.Host, convey.ShouldEqual, "0.0.0.0")
convey.So(cfg.Server.Port, convey.ShouldEqual, 25500)
convey.So(cfg.Logging.Level, convey.ShouldEqual, "info")
convey.So(cfg.Conversion.DefaultTarget, convey.ShouldEqual, "clash")
// 清理
manager.Close()
})
convey.Convey("创建带配置文件的配置管理器", func() {
// 创建临时配置文件
tempDir := t.TempDir()
configFile := filepath.Join(tempDir, "test.yaml")
configContent := `
server:
host: "127.0.0.1"
port: 8080
logging:
level: "debug"
format: "json"
security:
access_tokens: ["test-token"]
`
err := os.WriteFile(configFile, []byte(configContent), 0644)
convey.So(err, convey.ShouldBeNil)
// 创建配置管理器
manager, err := config.NewConfigManager(configFile)
convey.So(err, convey.ShouldBeNil)
convey.So(manager, convey.ShouldNotBeNil)
// 验证配置
cfg := manager.GetConfig()
convey.So(cfg.Server.Host, convey.ShouldEqual, "127.0.0.1")
convey.So(cfg.Server.Port, convey.ShouldEqual, 8080)
convey.So(cfg.Logging.Level, convey.ShouldEqual, "debug")
convey.So(len(cfg.Security.AccessTokens), convey.ShouldEqual, 1)
convey.So(cfg.Security.AccessTokens[0], convey.ShouldEqual, "test-token")
// 清理
manager.Close()
})
})
}
// TestConfigManagerDefaults 测试默认配置
func TestConfigManagerDefaults(t *testing.T) {
convey.Convey("默认配置测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
cfg := manager.GetConfig()
convey.Convey("服务器默认配置", func() {
convey.So(cfg.Server.Host, convey.ShouldEqual, "0.0.0.0")
convey.So(cfg.Server.Port, convey.ShouldEqual, 25500)
convey.So(cfg.Server.ReadTimeout, convey.ShouldEqual, 30)
convey.So(cfg.Server.WriteTimeout, convey.ShouldEqual, 30)
convey.So(cfg.Server.MaxRequestSize, convey.ShouldEqual, 10*1024*1024)
})
convey.Convey("日志默认配置", func() {
convey.So(cfg.Logging.Level, convey.ShouldEqual, "info")
convey.So(cfg.Logging.Format, convey.ShouldEqual, "json")
convey.So(cfg.Logging.Output, convey.ShouldEqual, "stdout")
})
convey.Convey("安全默认配置", func() {
convey.So(len(cfg.Security.AccessTokens), convey.ShouldEqual, 0)
convey.So(len(cfg.Security.CorsOrigins), convey.ShouldEqual, 1)
convey.So(cfg.Security.CorsOrigins[0], convey.ShouldEqual, "*")
convey.So(cfg.Security.RateLimit, convey.ShouldEqual, 0)
convey.So(cfg.Security.Timeout, convey.ShouldEqual, 60)
})
convey.Convey("转换默认配置", func() {
convey.So(cfg.Conversion.DefaultTarget, convey.ShouldEqual, "clash")
convey.So(cfg.Conversion.DefaultEmoji, convey.ShouldBeFalse)
convey.So(cfg.Conversion.DefaultUDP, convey.ShouldBeFalse)
convey.So(cfg.Conversion.MaxNodes, convey.ShouldEqual, 0)
convey.So(cfg.Conversion.CacheTimeout, convey.ShouldEqual, 60)
})
})
}
// TestConfigManagerEnvironmentVariables 测试环境变量配置
func TestConfigManagerEnvironmentVariables(t *testing.T) {
convey.Convey("环境变量配置测试", t, func() {
// 设置环境变量
os.Setenv("SUBCONVERTER_HOST", "192.168.1.100")
os.Setenv("SUBCONVERTER_PORT", "9000")
os.Setenv("SUBCONVERTER_LOG_LEVEL", "debug")
os.Setenv("SUBCONVERTER_ACCESS_TOKENS", "test-token")
defer func() {
os.Unsetenv("SUBCONVERTER_HOST")
os.Unsetenv("SUBCONVERTER_PORT")
os.Unsetenv("SUBCONVERTER_LOG_LEVEL")
os.Unsetenv("SUBCONVERTER_ACCESS_TOKENS")
}()
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
cfg := manager.GetConfig()
convey.So(cfg.Server.Host, convey.ShouldEqual, "192.168.1.100")
convey.So(cfg.Server.Port, convey.ShouldEqual, 9000)
convey.So(cfg.Logging.Level, convey.ShouldEqual, "debug")
convey.So(len(cfg.Security.AccessTokens), convey.ShouldEqual, 1)
convey.So(cfg.Security.AccessTokens[0], convey.ShouldEqual, "test-token")
})
}
// TestConfigManagerUpdateConfig 测试配置更新
func TestConfigManagerUpdateConfig(t *testing.T) {
convey.Convey("配置更新测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
convey.Convey("更新配置", func() {
newConfig := manager.GetConfig().Clone()
newConfig.Server.Host = "127.0.0.1"
newConfig.Server.Port = 8080
newConfig.Logging.Level = "debug"
err := manager.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
// 验证配置已更新
updatedConfig := manager.GetConfig()
convey.So(updatedConfig.Server.Host, convey.ShouldEqual, "127.0.0.1")
convey.So(updatedConfig.Server.Port, convey.ShouldEqual, 8080)
convey.So(updatedConfig.Logging.Level, convey.ShouldEqual, "debug")
})
convey.Convey("无效配置处理", func() {
invalidConfig := manager.GetConfig().Clone()
invalidConfig.Server.Port = -1 // 无效端口
err := manager.UpdateConfig(invalidConfig)
convey.So(err, convey.ShouldNotBeNil)
convey.So(err.Error(), convey.ShouldContain, "invalid port")
})
})
}
// TestConfigManagerPartialAccess 测试配置部分访问
func TestConfigManagerPartialAccess(t *testing.T) {
convey.Convey("配置部分访问测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
convey.Convey("获取服务器配置", func() {
serverConfig := manager.GetServerConfig()
convey.So(serverConfig, convey.ShouldNotBeNil)
convey.So(serverConfig.Host, convey.ShouldEqual, "0.0.0.0")
convey.So(serverConfig.Port, convey.ShouldEqual, 25500)
// 修改返回的配置不应该影响原始配置
serverConfig.Host = "modified"
originalConfig := manager.GetConfig()
convey.So(originalConfig.Server.Host, convey.ShouldEqual, "0.0.0.0")
})
convey.Convey("获取日志配置", func() {
loggingConfig := manager.GetLoggingConfig()
convey.So(loggingConfig, convey.ShouldNotBeNil)
convey.So(loggingConfig.Level, convey.ShouldEqual, "info")
convey.So(loggingConfig.Format, convey.ShouldEqual, "json")
})
convey.Convey("获取安全配置", func() {
securityConfig := manager.GetSecurityConfig()
convey.So(securityConfig, convey.ShouldNotBeNil)
convey.So(len(securityConfig.AccessTokens), convey.ShouldEqual, 0)
convey.So(securityConfig.RateLimit, convey.ShouldEqual, 0)
})
convey.Convey("获取转换配置", func() {
conversionConfig := manager.GetConversionConfig()
convey.So(conversionConfig, convey.ShouldNotBeNil)
convey.So(conversionConfig.DefaultTarget, convey.ShouldEqual, "clash")
convey.So(conversionConfig.DefaultEmoji, convey.ShouldBeFalse)
})
})
}
// TestConfigManagerSaveConfig 测试配置保存
func TestConfigManagerSaveConfig(t *testing.T) {
convey.Convey("配置保存测试", t, func() {
tempDir := t.TempDir()
configFile := filepath.Join(tempDir, "test.yaml")
// 创建配置管理器
manager, err := config.NewConfigManager(configFile)
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
convey.Convey("保存配置到文件", func() {
// 修改配置
newConfig := manager.GetConfig().Clone()
newConfig.Server.Host = "127.0.0.1"
newConfig.Server.Port = 9090
newConfig.Logging.Level = "debug"
err := manager.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
// 检查文件是否存在
_, err = os.Stat(configFile)
convey.So(err, convey.ShouldBeNil)
// 从文件重新加载配置验证
newManager, err := config.NewConfigManager(configFile)
convey.So(err, convey.ShouldBeNil)
defer newManager.Close()
loadedConfig := newManager.GetConfig()
convey.So(loadedConfig.Server.Host, convey.ShouldEqual, "127.0.0.1")
convey.So(loadedConfig.Server.Port, convey.ShouldEqual, 9090)
convey.So(loadedConfig.Logging.Level, convey.ShouldEqual, "debug")
})
})
}
// TestConfigManagerBasicAccess 测试基本配置访问
func TestConfigManagerBasicAccess(t *testing.T) {
convey.Convey("基本配置访问测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
convey.Convey("GetString方法", func() {
host := manager.GetString("server.host")
convey.So(host, convey.ShouldEqual, "0.0.0.0")
level := manager.GetString("logging.level")
convey.So(level, convey.ShouldEqual, "info")
})
convey.Convey("GetInt方法", func() {
port := manager.GetInt("server.port")
convey.So(port, convey.ShouldEqual, 25500)
timeout := manager.GetInt("conversion.timeout_seconds")
convey.So(timeout, convey.ShouldEqual, 30)
})
convey.Convey("GetBool方法", func() {
cors := manager.GetBool("server.enable_cors")
convey.So(cors, convey.ShouldBeTrue)
auth := manager.GetBool("security.enable_auth")
convey.So(auth, convey.ShouldBeFalse)
})
convey.Convey("GetStringSlice方法", func() {
formats := manager.GetStringSlice("conversion.supported_formats")
convey.So(formats, convey.ShouldNotBeNil)
convey.So(len(formats) > 0, convey.ShouldBeTrue)
convey.So(formats, convey.ShouldContain, "clash")
})
convey.Convey("IsSet方法", func() {
convey.So(manager.IsSet("server.host"), convey.ShouldBeTrue)
convey.So(manager.IsSet("nonexistent.key"), convey.ShouldBeFalse)
})
convey.Convey("Set方法", func() {
manager.Set("test.key", "test_value")
value := manager.GetString("test.key")
convey.So(value, convey.ShouldEqual, "test_value")
})
})
}
// TestConfigManagerWatchConfig 测试配置监听
func TestConfigManagerWatchConfig(t *testing.T) {
convey.Convey("配置监听测试", t, func() {
tempDir := t.TempDir()
configFile := filepath.Join(tempDir, "test.yaml")
// 创建初始配置文件
configContent := `
server:
host: "0.0.0.0"
port: 25500
logging:
level: "info"
`
err := os.WriteFile(configFile, []byte(configContent), 0644)
convey.So(err, convey.ShouldBeNil)
// 创建配置管理器
manager, err := config.NewConfigManager(configFile)
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
configChanged := make(chan bool, 1)
// 开始监听配置变化
manager.WatchConfig(func(cfg *config.Configuration) {
configChanged <- true
})
convey.Convey("监听配置文件变化", func() {
// 修改配置文件
newConfigContent := `
server:
host: "127.0.0.1"
port: 8080
logging:
level: "debug"
`
err := os.WriteFile(configFile, []byte(newConfigContent), 0644)
convey.So(err, convey.ShouldBeNil)
// 等待配置变化事件
select {
case changed := <-configChanged:
convey.So(changed, convey.ShouldBeTrue)
// 验证配置已更新
updatedConfig := manager.GetConfig()
convey.So(updatedConfig.Server.Host, convey.ShouldEqual, "127.0.0.1")
convey.So(updatedConfig.Server.Port, convey.ShouldEqual, 8080)
convey.So(updatedConfig.Logging.Level, convey.ShouldEqual, "debug")
case <-time.After(2 * time.Second):
convey.So(false, convey.ShouldBeTrue) // 超时失败
}
})
})
}

View File

@@ -0,0 +1,279 @@
package unit
import (
"net/http/httptest"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
"github.com/subconverter-go/internal/config"
httpserver "github.com/subconverter-go/internal/http"
)
// TestHTTPServerCreation 测试HTTP服务器创建
func TestHTTPServerCreation(t *testing.T) {
convey.Convey("HTTP服务器创建测试", t, func() {
convey.Convey("创建默认HTTP服务器", func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
convey.So(server, convey.ShouldNotBeNil)
// 验证服务器配置
stats := server.GetStats()
convey.So(stats, convey.ShouldNotBeNil)
convey.So(stats["host"], convey.ShouldEqual, "0.0.0.0")
convey.So(stats["port"], convey.ShouldEqual, 25500)
// 验证运行时间
uptime := server.GetUptime()
convey.So(uptime, convey.ShouldBeGreaterThan, 0)
})
convey.Convey("创建带自定义配置的HTTP服务器", func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
// 修改服务器配置
newConfig := manager.GetConfig().Clone()
newConfig.Server.Host = "127.0.0.1"
newConfig.Server.Port = 8080
err = manager.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
convey.So(server, convey.ShouldNotBeNil)
stats := server.GetStats()
convey.So(stats["host"], convey.ShouldEqual, "127.0.0.1")
convey.So(stats["port"], convey.ShouldEqual, 8080)
})
})
}
// TestHTTPServerRoutes 测试HTTP服务器路由
func TestHTTPServerRoutes(t *testing.T) {
convey.Convey("HTTP服务器路由测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
defer server.Stop()
app := server.GetApp()
convey.Convey("健康检查路由", func() {
req := httptest.NewRequest("GET", "/health", nil)
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
// 验证响应内容
// 这里简化测试实际项目中应该解析JSON
convey.So(resp.Header.Get("Content-Type"), convey.ShouldContain, "application/json")
})
convey.Convey("版本信息路由", func() {
req := httptest.NewRequest("GET", "/version", nil)
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
convey.So(resp.Header.Get("Content-Type"), convey.ShouldContain, "application/json")
})
convey.Convey("根路由", func() {
req := httptest.NewRequest("GET", "/", nil)
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
convey.So(resp.Header.Get("Content-Type"), convey.ShouldContain, "application/json")
})
convey.Convey("统计信息路由", func() {
req := httptest.NewRequest("GET", "/api/stats", nil)
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
convey.So(resp.Header.Get("Content-Type"), convey.ShouldContain, "application/json")
})
convey.Convey("未实现的路由", func() {
req := httptest.NewRequest("GET", "/api/sub", nil)
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 501) // Not Implemented
})
})
}
// TestHTTPServerMiddleware 测试HTTP服务器中间件
func TestHTTPServerMiddleware(t *testing.T) {
convey.Convey("HTTP服务器中间件测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
defer server.Stop()
app := server.GetApp()
convey.Convey("CORS中间件", func() {
req := httptest.NewRequest("GET", "/health", nil)
req.Header.Set("Origin", "http://localhost:3000")
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
// 验证CORS头
allowOrigin := resp.Header.Get("Access-Control-Allow-Origin")
convey.So(allowOrigin, convey.ShouldNotBeEmpty)
})
convey.Convey("OPTIONS预检请求", func() {
req := httptest.NewRequest("OPTIONS", "/health", nil)
req.Header.Set("Origin", "http://localhost:3000")
req.Header.Set("Access-Control-Request-Method", "GET")
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
})
})
}
// TestHTTPServerWithRateLimit 测试带速率限制的HTTP服务器
func TestHTTPServerWithRateLimit(t *testing.T) {
convey.Convey("带速率限制的HTTP服务器测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
// 启用速率限制
newConfig := manager.GetConfig().Clone()
newConfig.Security.RateLimit = 2 // 每分钟2个请求
err = manager.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
defer server.Stop()
app := server.GetApp()
convey.Convey("速率限制正常工作", func() {
// 发送第一个请求
req1 := httptest.NewRequest("GET", "/health", nil)
resp1, err := app.Test(req1, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp1.StatusCode, convey.ShouldEqual, 200)
// 发送第二个请求
req2 := httptest.NewRequest("GET", "/health", nil)
resp2, err := app.Test(req2, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp2.StatusCode, convey.ShouldEqual, 200)
// 发送第三个请求(应该被限制)
req3 := httptest.NewRequest("GET", "/health", nil)
resp3, err := app.Test(req3, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp3.StatusCode, convey.ShouldEqual, 429) // Too Many Requests
})
})
}
// TestHTTPServerStats 测试HTTP服务器统计信息
func TestHTTPServerStats(t *testing.T) {
convey.Convey("HTTP服务器统计信息测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
defer server.Stop()
convey.Convey("获取统计信息", func() {
stats := server.GetStats()
convey.So(stats, convey.ShouldNotBeNil)
// 验证统计信息字段
convey.So(stats["uptime"], convey.ShouldNotBeEmpty)
convey.So(stats["start_time"], convey.ShouldNotBeEmpty)
convey.So(stats["host"], convey.ShouldEqual, "0.0.0.0")
convey.So(stats["port"], convey.ShouldEqual, 25500)
convey.So(stats["max_request_size"], convey.ShouldEqual, int64(10485760)) // 10MB
convey.So(stats["read_timeout"], convey.ShouldEqual, 30)
convey.So(stats["write_timeout"], convey.ShouldEqual, 30)
})
convey.Convey("运行时间递增", func() {
uptime1 := server.GetUptime()
time.Sleep(10 * time.Millisecond)
uptime2 := server.GetUptime()
convey.So(uptime2, convey.ShouldBeGreaterThan, uptime1)
})
})
}
// TestHTTPServerGracefulShutdown 测试HTTP服务器优雅关闭
func TestHTTPServerGracefulShutdown(t *testing.T) {
convey.Convey("HTTP服务器优雅关闭测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
// 模拟服务器运行
go func() {
time.Sleep(100 * time.Millisecond)
err := server.Stop()
convey.So(err, convey.ShouldBeNil)
}()
// 验证服务器能够正常关闭
time.Sleep(200 * time.Millisecond)
})
}
// TestHTTPServerWithCustomSecurityConfig 测试自定义安全配置
func TestHTTPServerWithCustomSecurityConfig(t *testing.T) {
convey.Convey("自定义安全配置测试", t, func() {
manager, err := config.NewConfigManager("")
convey.So(err, convey.ShouldBeNil)
defer manager.Close()
// 设置自定义CORS配置
newConfig := manager.GetConfig().Clone()
newConfig.Security.CorsOrigins = []string{"http://localhost:3000", "https://example.com"}
newConfig.Security.RateLimit = 5
err = manager.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
server, err := httpserver.NewServer(manager)
convey.So(err, convey.ShouldBeNil)
defer server.Stop()
app := server.GetApp()
convey.Convey("自定义CORS生效", func() {
req := httptest.NewRequest("GET", "/health", nil)
req.Header.Set("Origin", "http://localhost:3000")
resp, err := app.Test(req, -1)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.StatusCode, convey.ShouldEqual, 200)
})
})
}

450
tests/unit/test_logger.go Normal file
View File

@@ -0,0 +1,450 @@
package unit
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
"github.com/subconverter-go/internal/logging"
)
// TestLoggerCreation 测试日志记录器的创建
func TestLoggerCreation(t *testing.T) {
convey.Convey("日志记录器创建测试", t, func() {
convey.Convey("创建默认日志记录器", func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
convey.So(logger, convey.ShouldNotBeNil)
config := logger.GetConfig()
convey.So(config, convey.ShouldNotBeNil)
convey.So(config.Level, convey.ShouldEqual, "info")
convey.So(config.Format, convey.ShouldEqual, "text")
convey.So(config.Output, convey.ShouldEqual, "stdout")
})
convey.Convey("创建自定义配置日志记录器", func() {
config := &logging.LoggingConfig{
Level: "debug",
Format: "json",
Output: "stdout",
ReportCaller: true,
EnableColor: false,
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
convey.So(logger, convey.ShouldNotBeNil)
actualConfig := logger.GetConfig()
convey.So(actualConfig.Level, convey.ShouldEqual, "debug")
convey.So(actualConfig.Format, convey.ShouldEqual, "json")
convey.So(actualConfig.ReportCaller, convey.ShouldBeTrue)
})
convey.Convey("创建空配置日志记录器", func() {
logger, err := logging.NewLogger(nil)
convey.So(err, convey.ShouldBeNil)
convey.So(logger, convey.ShouldNotBeNil)
config := logger.GetConfig()
convey.So(config, convey.ShouldNotBeNil)
convey.So(config.Level, convey.ShouldEqual, "info") // 默认值
})
})
}
// TestLoggerConfiguration 测试日志配置功能
func TestLoggerConfiguration(t *testing.T) {
convey.Convey("日志配置测试", t, func() {
convey.Convey("无效配置处理", func() {
config := &logging.LoggingConfig{
Level: "invalid_level",
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldNotBeNil)
convey.So(logger, convey.ShouldBeNil)
convey.So(err.Error(), convey.ShouldContain, "invalid log level")
})
convey.Convey("动态更新配置", func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
newConfig := &logging.LoggingConfig{
Level: "debug",
Format: "json",
Output: "stdout",
EnableColor: false,
}
err = logger.UpdateConfig(newConfig)
convey.So(err, convey.ShouldBeNil)
updatedConfig := logger.GetConfig()
convey.So(updatedConfig.Level, convey.ShouldEqual, "debug")
convey.So(updatedConfig.Format, convey.ShouldEqual, "json")
})
})
}
// TestLoggerOutput 测试日志输出功能
func TestLoggerOutput(t *testing.T) {
convey.Convey("日志输出测试", t, func() {
convey.Convey("标准输出测试", func() {
// 重定向标准输出以捕获日志
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
logger.Info("Test message")
// 恢复标准输出
w.Close()
os.Stdout = oldStdout
// 读取捕获的输出
output, _ := ioutil.ReadAll(r)
convey.So(len(output) > 0, convey.ShouldBeTrue)
convey.So(string(output), convey.ShouldContain, "Test message")
})
convey.Convey("文件输出测试", func() {
tempDir := t.TempDir()
logFile := filepath.Join(tempDir, "test.log")
config := &logging.LoggingConfig{
Level: "info",
Format: "text",
Output: "file",
FilePath: logFile,
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
logger.Info("File test message")
// 等待日志写入
time.Sleep(10 * time.Millisecond)
// 读取日志文件
content, err := ioutil.ReadFile(logFile)
convey.So(err, convey.ShouldBeNil)
convey.So(string(content), convey.ShouldContain, "File test message")
// 清理
logger.Close()
})
})
}
// TestLoggerLevels 测试不同日志级别
func TestLoggerLevels(t *testing.T) {
convey.Convey("日志级别测试", t, func() {
convey.Convey("Debug级别日志", func() {
config := &logging.LoggingConfig{
Level: "debug",
Format: "text",
Output: "stdout",
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logger.Debug("Debug message")
logger.Info("Info message")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Debug message")
convey.So(outputStr, convey.ShouldContain, "Info message")
})
convey.Convey("Info级别日志", func() {
config := &logging.LoggingConfig{
Level: "info",
Format: "text",
Output: "stdout",
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logger.Debug("Debug message") // 不应该输出
logger.Info("Info message") // 应该输出
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldNotContain, "Debug message")
convey.So(outputStr, convey.ShouldContain, "Info message")
})
})
}
// TestLoggerFormats 测试不同日志格式
func TestLoggerFormats(t *testing.T) {
convey.Convey("日志格式测试", t, func() {
convey.Convey("Text格式", func() {
config := &logging.LoggingConfig{
Level: "info",
Format: "text",
Output: "stdout",
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logger.Info("Text format test")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Text format test")
convey.So(outputStr, convey.ShouldContain, "level=info") // text格式特征
})
convey.Convey("JSON格式", func() {
config := &logging.LoggingConfig{
Level: "info",
Format: "json",
Output: "stdout",
}
logger, err := logging.NewLogger(config)
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logger.Info("JSON format test")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "JSON format test")
convey.So(outputStr, convey.ShouldContain, `"level":"info"`) // JSON格式特征
})
})
}
// TestLoggerWithFields 测试带字段的日志记录
func TestLoggerWithFields(t *testing.T) {
convey.Convey("带字段日志测试", t, func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
fields := map[string]interface{}{
"user_id": "123",
"request_id": "abc-def",
"operation": "test",
}
logger.WithFields(fields).Info("Message with fields")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Message with fields")
convey.So(outputStr, convey.ShouldContain, "user_id=123")
convey.So(outputStr, convey.ShouldContain, "request_id=abc-def")
convey.So(outputStr, convey.ShouldContain, "operation=test")
})
}
// TestLoggerLogEntry 测试使用LogEntry记录日志
func TestLoggerLogEntry(t *testing.T) {
convey.Convey("LogEntry日志测试", t, func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
entry := logging.NewLogEntryWithLocation(logging.INFO, "Test with LogEntry").
WithField("test_field", "test_value").
WithError(fmt.Errorf("test error")).
WithRequest("GET", "/test", 200).
WithDuration(100 * time.Millisecond)
err = logger.Log(entry)
convey.So(err, convey.ShouldBeNil)
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Test with LogEntry")
convey.So(outputStr, convey.ShouldContain, "test_field=test_value")
convey.So(outputStr, convey.ShouldContain, "test error")
convey.So(outputStr, convey.ShouldContain, "method=GET")
convey.So(outputStr, convey.ShouldContain, "path=/test")
convey.So(outputStr, convey.ShouldContain, "status_code=200")
convey.So(outputStr, convey.ShouldContain, "duration_ms=100")
})
}
// TestLoggerSpecialMethods 测试特殊日志方法
func TestLoggerSpecialMethods(t *testing.T) {
convey.Convey("特殊日志方法测试", t, func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// 测试操作日志
endOp := logger.LogOperation("test_operation", map[string]interface{}{"param": "value"})
time.Sleep(10 * time.Millisecond) // 模拟操作耗时
endOp()
// 测试HTTP请求日志
logger.LogHTTPRequest("GET", "/api/test", 200, 50*time.Millisecond, map[string]interface{}{"user_id": "123"})
// 测试转换日志
logger.LogConversion("clash", "https://example.com", 10, 100*time.Millisecond, true)
// 测试代理验证日志
logger.LogProxyValidation("test-proxy", "ss", true, 50, nil)
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Operation started")
convey.So(outputStr, convey.ShouldContain, "Operation completed")
convey.So(outputStr, convey.ShouldContain, "test_operation")
convey.So(outputStr, convey.ShouldContain, "GET /api/test - 200")
convey.So(outputStr, convey.ShouldContain, "Conversion")
convey.So(outputStr, convey.ShouldContain, "Proxy validation")
})
}
// TestGlobalLogger 测试全局日志记录器
func TestGlobalLogger(t *testing.T) {
convey.Convey("全局日志记录器测试", t, func() {
convey.Convey("获取默认日志记录器", func() {
logger := logging.GetDefaultLogger()
convey.So(logger, convey.ShouldNotBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
logging.Info("Global logger test")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
convey.So(len(output) > 0, convey.ShouldBeTrue)
})
convey.Convey("设置自定义全局日志记录器", func() {
customLogger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
logging.SetDefaultLogger(customLogger)
// 验证设置成功
currentLogger := logging.GetDefaultLogger()
convey.So(currentLogger, convey.ShouldEqual, customLogger)
})
})
}
// TestLoggerUtilities 测试日志工具函数
func TestLoggerUtilities(t *testing.T) {
convey.Convey("日志工具函数测试", t, func() {
convey.Convey("获取调用者信息", func() {
file, line, funcName := logging.GetCallerInfo()
convey.So(file, convey.ShouldNotBeEmpty)
convey.So(line > 0, convey.ShouldBeTrue)
convey.So(funcName, convey.ShouldNotBeEmpty)
})
})
}
// TestLoggerErrorHandling 测试错误处理
func TestLoggerErrorHandling(t *testing.T) {
convey.Convey("日志错误处理测试", t, func() {
convey.Convey("记录错误日志", func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
// 重定向输出
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
testErr := fmt.Errorf("test error occurred")
logger.WithError(testErr).Error("Error message")
w.Close()
os.Stdout = oldStdout
output, _ := ioutil.ReadAll(r)
outputStr := string(output)
convey.So(outputStr, convey.ShouldContain, "Error message")
convey.So(outputStr, convey.ShouldContain, "test error occurred")
})
convey.Convey("无效LogEntry处理", func() {
logger, err := logging.NewDefaultLogger()
convey.So(err, convey.ShouldBeNil)
err = logger.Log(nil)
convey.So(err, convey.ShouldNotBeNil)
convey.So(err.Error(), convey.ShouldContain, "cannot be nil")
})
})
}

View File

@@ -0,0 +1,159 @@
package unit
import (
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
testcase "github.com/subconverter-go/internal/testing"
)
// TestTestCaseBasic 测试 TestCase 模型的基本功能
// 验证 TestCase 模型的创建、设置和基本操作
func TestTestCaseBasic(t *testing.T) {
convey.Convey("TestCase 基本功能测试", t, func() {
convey.Convey("创建和基本配置", func() {
tc := testcase.NewUnitTest("TC001", "Basic Test")
convey.So(tc.ID, convey.ShouldEqual, "TC001")
convey.So(tc.Name, convey.ShouldEqual, "Basic Test")
convey.So(tc.Type, convey.ShouldEqual, testcase.UNIT)
convey.So(tc.Status, convey.ShouldEqual, testcase.PENDING)
convey.So(tc.Priority, convey.ShouldEqual, testcase.MEDIUM)
})
convey.Convey("链式调用配置", func() {
tc := testcase.NewUnitTest("TC002", "Chain Test").
SetDescription("Test chain calls").
SetPriority(testcase.HIGH).
SetTimeout(60*time.Second).
AddTag("smoke").
AddTag("regression")
convey.So(tc.Description, convey.ShouldEqual, "Test chain calls")
convey.So(tc.Priority, convey.ShouldEqual, testcase.HIGH)
convey.So(tc.Timeout, convey.ShouldEqual, 60*time.Second)
convey.So(tc.HasTag("smoke"), convey.ShouldBeTrue)
convey.So(tc.HasTag("regression"), convey.ShouldBeTrue)
})
convey.Convey("测试数据设置", func() {
input := map[string]interface{}{
"target": "clash",
"url": "https://example.com/subscribe",
}
expectation := map[string]interface{}{
"success": true,
"nodes": 10,
}
tc := testcase.NewUnitTest("TC003", "Data Test").
SetInput(input).
SetExpectation(expectation)
convey.So(tc.Input, convey.ShouldResemble, input)
convey.So(tc.Expectation, convey.ShouldResemble, expectation)
})
convey.Convey("执行生命周期", func() {
tc := testcase.NewUnitTest("TC004", "Lifecycle Test")
tc.SetInput("test").SetExpectation("result")
// 初始状态
convey.So(tc.Status, convey.ShouldEqual, testcase.PENDING)
convey.So(tc.Attempts, convey.ShouldEqual, 0)
convey.So(tc.ShouldRun(), convey.ShouldBeTrue)
// 开始执行
tc.Start()
convey.So(tc.Status, convey.ShouldEqual, testcase.RUNNING)
convey.So(tc.Attempts, convey.ShouldEqual, 1)
convey.So(tc.IsRunning(), convey.ShouldBeTrue)
convey.So(tc.ShouldRun(), convey.ShouldBeFalse)
// 完成执行
tc.Complete(true, "actual result", nil)
convey.So(tc.Status, convey.ShouldEqual, testcase.PASSED)
convey.So(tc.IsSuccessful(), convey.ShouldBeTrue)
convey.So(tc.IsFailed(), convey.ShouldBeFalse)
convey.So(tc.Duration > 0, convey.ShouldBeTrue)
convey.So(tc.ShouldRun(), convey.ShouldBeFalse)
})
convey.Convey("测试验证", func() {
convey.Convey("有效测试用例", func() {
tc := testcase.NewUnitTest("TC005", "Valid Test")
tc.SetInput("input").SetExpectation("output")
err := tc.Validate()
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("无效ID", func() {
tc := testcase.NewUnitTest("", "Test")
tc.SetInput("input").SetExpectation("output")
err := tc.Validate()
convey.So(err, convey.ShouldNotBeNil)
convey.So(err.Error(), convey.ShouldContain, "ID cannot be empty")
})
convey.Convey("空输入", func() {
tc := testcase.NewUnitTest("TC006", "Input Test")
tc.SetInput(nil).SetExpectation("output")
err := tc.Validate()
convey.So(err, convey.ShouldNotBeNil)
convey.So(err.Error(), convey.ShouldContain, "input cannot be nil")
})
})
convey.Convey("序列化功能", func() {
original := testcase.NewUnitTest("TC007", "Serialize Test")
original.
SetDescription("Test serialization").
SetPriority(testcase.HIGH).
SetInput("test input").
SetExpectation("expected output").
AddTag("serialize")
jsonStr, err := original.ToJSON()
convey.So(err, convey.ShouldBeNil)
convey.So(jsonStr, convey.ShouldNotBeEmpty)
convey.So(jsonStr, convey.ShouldContain, "Serialize Test")
// 反序列化
newTC := &testcase.TestCase{}
err = newTC.FromJSON(jsonStr)
convey.So(err, convey.ShouldBeNil)
convey.So(newTC.ID, convey.ShouldEqual, original.ID)
convey.So(newTC.Name, convey.ShouldEqual, original.Name)
convey.So(newTC.Description, convey.ShouldEqual, original.Description)
})
convey.Convey("克隆功能", func() {
original := testcase.NewUnitTest("TC008", "Clone Test")
original.
SetDescription("Test cloning").
SetPriority(testcase.CRITICAL).
SetInput("input").
SetExpectation("output").
AddTag("clone")
clone := original.Clone()
convey.So(clone.ID, convey.ShouldEqual, "TC008_clone")
convey.So(clone.Name, convey.ShouldEqual, original.Name)
convey.So(clone.Description, convey.ShouldEqual, original.Description)
convey.So(clone.Priority, convey.ShouldEqual, original.Priority)
convey.So(clone.Input, convey.ShouldEqual, original.Input)
convey.So(clone.Expectation, convey.ShouldEqual, original.Expectation)
convey.So(clone.Tags, convey.ShouldResemble, original.Tags)
// 验证状态被重置
convey.So(clone.Status, convey.ShouldEqual, testcase.PENDING)
convey.So(original.Status, convey.ShouldEqual, testcase.PENDING)
})
})
}

View File

@@ -0,0 +1,47 @@
package unit
import (
"testing"
"github.com/smartystreets/goconvey/convey"
testcase "github.com/subconverter-go/internal/testing"
)
func TestTestCase(t *testing.T) {
convey.Convey("TestCase 测试", t, func() {
convey.Convey("基本创建和配置", func() {
tc := testcase.NewUnitTest("TC001", "Test")
convey.So(tc.ID, convey.ShouldEqual, "TC001")
convey.So(tc.Name, convey.ShouldEqual, "Test")
convey.So(tc.Type, convey.ShouldEqual, testcase.UNIT)
})
convey.Convey("链式调用", func() {
tc := testcase.NewUnitTest("TC002", "Chain").
SetDescription("Chain test").
AddTag("test")
convey.So(tc.Description, convey.ShouldEqual, "Chain test")
convey.So(tc.HasTag("test"), convey.ShouldBeTrue)
})
convey.Convey("执行状态", func() {
tc := testcase.NewUnitTest("TC003", "Execution")
tc.SetInput("input").SetExpectation("output")
// 初始状态
convey.So(tc.Status, convey.ShouldEqual, testcase.PENDING)
convey.So(tc.ShouldRun(), convey.ShouldBeTrue)
// 开始执行
tc.Start()
convey.So(tc.Status, convey.ShouldEqual, testcase.RUNNING)
convey.So(tc.IsRunning(), convey.ShouldBeTrue)
// 完成执行
tc.Complete(true, "result", nil)
convey.So(tc.Status, convey.ShouldEqual, testcase.PASSED)
convey.So(tc.IsSuccessful(), convey.ShouldBeTrue)
})
})
}