Files
subconverter-go/internal/conversion/engine_internal_test.go
Rogee 7fcabe0225
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
first commit
2025-09-28 10:05:07 +08:00

170 lines
4.9 KiB
Go

package conversion
import (
"testing"
"github.com/subconverter-go/internal/config"
"github.com/subconverter-go/internal/generator"
"github.com/subconverter-go/internal/logging"
"github.com/subconverter-go/internal/parser"
)
func newTestConversionEngine(t *testing.T) *ConversionEngine {
t.Helper()
logger, err := logging.NewLogger(&logging.LoggingConfig{
Level: "error",
Format: "text",
Output: "stdout",
})
if err != nil {
t.Fatalf("failed to create logger: %v", err)
}
configMgr, err := config.NewConfigManager("")
if err != nil {
t.Fatalf("failed to create config manager: %v", err)
}
parserMgr := parser.NewParserManager(logger, configMgr)
generatorMgr := generator.NewGeneratorManager(logger)
return NewConversionEngine(logger, parserMgr, generatorMgr)
}
func TestTrimLineCleansCommentsAndBom(t *testing.T) {
engine := newTestConversionEngine(t)
cases := map[string]string{
"\ufeff ss://example": "ss://example",
"# leading comment": "",
" // spaced comment": "",
"\t;remark": "",
"ss://example": "ss://example",
}
for input, expected := range cases {
if got := engine.trimLine(input); got != expected {
t.Fatalf("trimLine(%q) = %q, expected %q", input, got, expected)
}
}
}
func TestParseConfigurationsHandlesLegacyProtocols(t *testing.T) {
engine := newTestConversionEngine(t)
vmessPayload := "eyJ2IjoiMiIsInBzIjoidGVzdCIsImFkZCI6IjE5Mi4xNjguMS4xIiwicG9ydCI6IjQ0MyIsImlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwiYWlkIjoiMCIsIm5ldCI6IndzIiwidHlwZSI6Im5vbmUiLCJob3N0IjoidGVzdC5jb20iLCJwYXRoIjoiL3Rlc3QiLCJ0bHMiOiJ0bHMifQ=="
input := "\ufeff# first line is a comment\n" +
"socks://user:pass@example.com:1080#socks-proxy\n" +
"vmess1://" + vmessPayload + "\n"
configs, result, err := engine.parseConfigurations(input, true)
if err != nil {
t.Fatalf("parseConfigurations returned error: %v", err)
}
if len(result.InvalidInputs) != 0 {
t.Fatalf("expected no invalid inputs, got %v", result.InvalidInputs)
}
if len(configs) != 2 {
t.Fatalf("expected 2 valid configs, got %d", len(configs))
}
protocols := []string{configs[0].Protocol, configs[1].Protocol}
expected := map[string]bool{"socks5": false, "vmess": false}
for _, protocol := range protocols {
if _, ok := expected[protocol]; !ok {
t.Fatalf("unexpected protocol parsed: %s", protocol)
}
expected[protocol] = true
}
for proto, seen := range expected {
if !seen {
t.Fatalf("protocol %s not detected in parsed configs", proto)
}
}
}
func TestGenerateStatsIncludedInDebugInfo(t *testing.T) {
engine := newTestConversionEngine(t)
req := &ConversionRequest{Target: "clash", URL: "https://example.com"}
configs := []*parser.ProxyConfig{{Protocol: "socks5"}}
parseResult := &ParseResult{
ValidConfigs: configs,
ProtocolStats: map[string]int{"socks5": 1},
}
generation := &GenerateResult{
GeneratedOutput: "payload",
FormatFeatures: map[string]bool{"success": true},
GenerateStats: &GenerateStats{
GeneratedProxies: 1,
GeneratedGroups: 2,
GeneratedRules: 3,
FormatFeatures: map[string]bool{"success": true},
},
}
resp := engine.createSuccessResponse(req, "payload", configs, parseResult, generation)
statsVal, ok := resp.DebugInfo["generate_stats"].(map[string]interface{})
if !ok {
t.Fatalf("expected generate_stats in debug info, got %v", resp.DebugInfo)
}
if statsVal["generated_proxies"].(int) != 1 {
t.Fatalf("expected generated_proxies=1, got %v", statsVal["generated_proxies"])
}
if statsVal["generated_groups"].(int) != 2 {
t.Fatalf("expected generated_groups=2, got %v", statsVal["generated_groups"])
}
if statsVal["generated_rules"].(int) != 3 {
t.Fatalf("expected generated_rules=3, got %v", statsVal["generated_rules"])
}
}
func TestRemoveLeadingEmojiHandlesVariants(t *testing.T) {
cases := map[string]string{
"🛰️ ss-192.168.1.1:8388": "ss-192.168.1.1:8388",
"🇭🇰 ss-node": "ss-node",
"🇭🇰ss-node": "ss-node",
" ss-node": "ss-node",
"": "",
}
for input, expected := range cases {
if got := removeLeadingEmoji(input); got != expected {
t.Fatalf("removeLeadingEmoji(%q) = %q, want %q", input, got, expected)
}
}
}
func TestApplyTransformationsRemoveEmojiAfterRename(t *testing.T) {
engine := newTestConversionEngine(t)
req := NewConversionRequest()
req.RenameRules = []string{".*@🇭🇰 $0"}
req.RemoveEmoji = true
config := &parser.ProxyConfig{
Name: "ss-192.168.1.1:8388",
Remarks: "",
Protocol: "ss",
Type: "ss",
Settings: map[string]interface{}{"method": "aes-256-cfb"},
}
updated, err := engine.applyTransformations([]*parser.ProxyConfig{config}, req)
if err != nil {
t.Fatalf("applyTransformations returned error: %v", err)
}
if len(updated) != 1 {
t.Fatalf("expected one config, got %d", len(updated))
}
if updated[0].Name != "ss-192.168.1.1:8388" {
t.Fatalf("expected name without emoji, got %q", updated[0].Name)
}
}