package conversion_test import ( "io" "net/http" "strings" "testing" "time" . "github.com/smartystreets/goconvey/convey" "github.com/subconverter-go/internal/config" "github.com/subconverter-go/internal/conversion" "github.com/subconverter-go/internal/generator" "github.com/subconverter-go/internal/logging" "github.com/subconverter-go/internal/parser" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func newMockHTTPClient() *http.Client { return &http.Client{ Timeout: 5 * time.Second, Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { body := sampleSubscriptionForURL(req.URL.String()) status := http.StatusOK if body == "" { status = http.StatusNotFound } resp := &http.Response{ StatusCode: status, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body)), } resp.Header.Set("Content-Type", "text/plain; charset=utf-8") return resp, nil }), } } func sampleSubscriptionForURL(u string) string { switch { case strings.Contains(u, "ss-subscribe"): return "ss://aes-256-cfb:password@192.168.1.1:8388" case strings.Contains(u, "vmess-subscribe"): return "vmess://eyJhZGQiOiIxOTIuMTY4LjEuMiIsICJwcyI6IlRlc3QgVk1lc3MiLCAicG9ydCI6NDQzLCAiaWQiOiI2YjZkZTQ3Zi1kZjQ1LTQ1M2ItODI1MS1hZjM0ZTM0ODc1Y2UifQ==" case strings.Contains(u, "subscribe"): return "ss://aes-256-cfb:password@192.168.1.1:8388\nvmess://eyJhZGQiOiIxOTIuMTY4LjEuMiIsICJwcyI6IlRlc3QgVk1lc3MiLCAicG9ydCI6NDQzLCAiaWQiOiI2YjZkZTQ3Zi1kZjQ1LTQ1M2ItODI1MS1hZjM0ZTM0ODc1Y2UifQ==\ntrojan://password@192.168.1.3:443" default: return "" } } func TestConversionEngine(t *testing.T) { Convey("ConversionEngine", t, func() { // 创建日志记录器 logger, err := logging.NewLogger(&logging.LoggingConfig{ Level: "info", Format: "text", Output: "stdout", }) So(err, ShouldBeNil) // 创建配置管理器 configMgr, err := config.NewConfigManager("") So(err, ShouldBeNil) // 创建解析器管理器 parserMgr := parser.NewParserManager(logger, configMgr) // 创建生成器管理器 generatorMgr := generator.NewGeneratorManager(logger) // 创建转换引擎 engine := conversion.NewConversionEngine(logger, parserMgr, generatorMgr) engine.SetHTTPClient(newMockHTTPClient()) Convey("应该能验证转换请求", func() { // 测试有效请求 validRequest := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/subscribe", // 使用URL而不是InputText } response, err := engine.Convert(validRequest) So(err, ShouldBeNil) So(response, ShouldNotBeNil) // 测试无效请求 - 缺少URL invalidRequest1 := &conversion.ConversionRequest{ Target: "clash", } response, err = engine.Convert(invalidRequest1) So(err, ShouldBeNil) So(response.Success, ShouldBeFalse) So(response.Error, ShouldContainSubstring, "source URL cannot be empty") // 测试无效请求 - 不支持的目标格式 invalidRequest2 := &conversion.ConversionRequest{ Target: "unsupported", URL: "https://example.com/subscribe", } response, err = engine.Convert(invalidRequest2) So(err, ShouldBeNil) So(response.Success, ShouldBeFalse) So(response.Error, ShouldContainSubstring, "unsupported target format") }) Convey("应该能解析Shadowsocks配置", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/ss-subscribe", Group: "Test SS", UDP: true, } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.NodeCount, ShouldBeGreaterThan, 0) So(response.Content, ShouldNotBeNil) // Check if content contains the cipher or if it's in JSON format contentContainsCipher := strings.Contains(response.Content, "aes-256-cfb") contentIsJSON := strings.Contains(response.Content, `"cipher":"aes-256-cfb"`) So(contentContainsCipher || contentIsJSON, ShouldBeTrue) }) Convey("应该能解析VMess配置", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/vmess-subscribe", Group: "Test VMess", UDP: true, } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.NodeCount, ShouldBeGreaterThan, 0) So(response.Content, ShouldNotBeNil) }) Convey("应该能处理无效URL", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "invalid-url", Group: "Test Invalid", } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeFalse) So(response.Error, ShouldNotBeNil) }) Convey("应该能生成多种目标格式", func() { formats := []string{"clash", "surge", "quantumultx", "loon", "surfboard", "v2ray"} for _, format := range formats { request := &conversion.ConversionRequest{ Target: format, URL: "https://example.com/subscribe", Group: "Test Multi " + format, UDP: true, } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.NodeCount, ShouldBeGreaterThan, 0) So(response.TargetFormat, ShouldEqual, format) } }) Convey("应该能处理URL获取请求", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/subscribe", Group: "Test URL", } response, err := engine.Convert(request) So(err, ShouldBeNil) // 由于URL获取尚未实现,这里会使用默认配置 So(response.Success, ShouldBeTrue) }) Convey("应该能提供调试信息", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/subscribe", Group: "Test Debug", } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.DebugInfo, ShouldNotBeNil) }) Convey("应该能使用基本配置", func() { request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/subscribe", Group: "Test Basic", } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.NodeCount, ShouldBeGreaterThan, 0) }) }) } func TestConversionEngineProtocolDetection(t *testing.T) { Convey("Basic Protocol Support", t, func() { // 创建日志记录器 logger, err := logging.NewLogger(&logging.LoggingConfig{ Level: "info", Format: "text", Output: "stdout", }) So(err, ShouldBeNil) // 创建配置管理器 configMgr, err := config.NewConfigManager("") So(err, ShouldBeNil) // 创建解析器管理器 parserMgr := parser.NewParserManager(logger, configMgr) // 创建生成器管理器 generatorMgr := generator.NewGeneratorManager(logger) // 创建转换引擎 engine := conversion.NewConversionEngine(logger, parserMgr, generatorMgr) engine.SetHTTPClient(newMockHTTPClient()) // 测试基本的功能验证 request := &conversion.ConversionRequest{ Target: "clash", URL: "https://example.com/subscribe", Group: "Test Protocol", } response, err := engine.Convert(request) So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) So(response.NodeCount, ShouldBeGreaterThan, 0) }) }