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
259 lines
7.4 KiB
Go
259 lines
7.4 KiB
Go
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)
|
||
})
|
||
}
|