summaryrefslogtreecommitdiff
path: root/internal/config/builder_test.go
diff options
context:
space:
mode:
authorSergeiEU <39683682+SergeiEU@users.noreply.github.com>2026-04-01 10:17:15 +0400
committerSergeiEU <39683682+SergeiEU@users.noreply.github.com>2026-04-01 10:17:15 +0400
commit1bd203c5555046b7ee4fbfe2f822eb3d03571ad7 (patch)
treed8c85273ede547e03a5727bf185f5d07e87b4a08 /internal/config/builder_test.go
downloadvpnem-main.tar.gz
vpnem-main.tar.bz2
vpnem-main.zip
Initial importHEADmain
Diffstat (limited to 'internal/config/builder_test.go')
-rw-r--r--internal/config/builder_test.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/internal/config/builder_test.go b/internal/config/builder_test.go
new file mode 100644
index 0000000..d5c0cb5
--- /dev/null
+++ b/internal/config/builder_test.go
@@ -0,0 +1,232 @@
+package config_test
+
+import (
+ "encoding/json"
+ "strings"
+ "testing"
+
+ "vpnem/internal/config"
+ "vpnem/internal/models"
+)
+
+func TestBuildConfigSocks(t *testing.T) {
+ server := models.Server{
+ Tag: "nl-1", Region: "NL", Type: "socks",
+ Server: "5.180.97.200", ServerPort: 54101, UDPOverTCP: true,
+ }
+ mode := *config.ModeByName("Lovense + OBS + AnyDesk + Discord")
+ ruleSets := []models.RuleSet{}
+
+ cfg := config.BuildConfig(server, mode, ruleSets, []string{"5.180.97.200"})
+
+ data, err := json.Marshal(cfg)
+ if err != nil {
+ t.Fatalf("marshal: %v", err)
+ }
+ s := string(data)
+
+ // Verify outbound type
+ if !strings.Contains(s, `"type":"socks"`) {
+ t.Error("expected socks outbound")
+ }
+ // Verify bypass processes present
+ if !strings.Contains(s, "chrome.exe") {
+ t.Error("expected chrome.exe in bypass processes")
+ }
+ if !strings.Contains(s, "obs64.exe") {
+ t.Error("expected obs64.exe in bypass processes")
+ }
+ // Verify Lovense regex
+ if !strings.Contains(s, "lovense") {
+ t.Error("expected lovense process regex")
+ }
+ // Verify ip_is_private
+ if !strings.Contains(s, "ip_is_private") {
+ t.Error("expected ip_is_private rule")
+ }
+ // Verify NCSI domains
+ if !strings.Contains(s, "msftconnecttest.com") {
+ t.Error("expected NCSI domain")
+ }
+ // Verify Telegram
+ if !strings.Contains(s, "telegram.org") {
+ t.Error("expected telegram domains")
+ }
+ // Verify Discord IPs
+ if !strings.Contains(s, "162.159.130.234/32") {
+ t.Error("expected discord IPs")
+ }
+ // Verify final is direct
+ if !strings.Contains(s, `"final":"direct"`) {
+ t.Error("expected final: direct")
+ }
+ // Verify TUN config
+ if !strings.Contains(s, "singbox") {
+ t.Error("expected TUN interface name singbox")
+ }
+ // Verify DNS
+ if !strings.Contains(s, "dns-proxy") {
+ t.Error("expected dns-proxy server")
+ }
+ // Verify cache_file
+ if !strings.Contains(s, "cache_file") {
+ t.Error("expected cache_file in experimental")
+ }
+ // sing-box 1.13: sniff action in route rules, not inbound
+ if strings.Contains(s, `"sniff":true`) {
+ t.Error("sniff should NOT be in inbound (removed in 1.13)")
+ }
+ if !strings.Contains(s, `"action":"sniff"`) {
+ t.Error("expected action:sniff in route rules")
+ }
+ // sing-box 1.13: hijack-dns action
+ if !strings.Contains(s, `"action":"hijack-dns"`) {
+ t.Error("expected hijack-dns action")
+ }
+ // sing-box 1.12: new DNS server format (type+server, not address)
+ if strings.Contains(s, `dns-query`) {
+ t.Error("DNS should use type+server, not address URL (deprecated in 1.12)")
+ }
+ if !strings.Contains(s, `"type":"https"`) {
+ t.Error("expected https DNS server")
+ }
+ if !strings.Contains(s, `"1.1.1.1"`) {
+ t.Error("expected 1.1.1.1 DoH server")
+ }
+ // sing-box 1.12: domain_resolver on outbounds
+ if !strings.Contains(s, "domain_resolver") {
+ t.Error("expected domain_resolver on outbounds")
+ }
+}
+
+func TestBuildConfigVLESS(t *testing.T) {
+ server := models.Server{
+ Tag: "nl-vless", Region: "NL", Type: "vless",
+ Server: "5.180.97.200", ServerPort: 443, UUID: "test-uuid",
+ TLS: &models.TLS{Enabled: true, ServerName: "test.example.com"},
+ Transport: &models.Transport{Type: "ws", Path: "/test"},
+ }
+ mode := *config.ModeByName("Full (All Traffic)")
+
+ cfg := config.BuildConfig(server, mode, nil, nil)
+ data, _ := json.Marshal(cfg)
+ s := string(data)
+
+ if !strings.Contains(s, `"type":"vless"`) {
+ t.Error("expected vless outbound")
+ }
+ if !strings.Contains(s, "test-uuid") {
+ t.Error("expected uuid")
+ }
+ if !strings.Contains(s, `"final":"proxy"`) {
+ t.Error("expected final: proxy for Full mode")
+ }
+}
+
+func TestBuildConfigShadowsocks(t *testing.T) {
+ server := models.Server{
+ Tag: "nl-ss", Region: "NL", Type: "shadowsocks",
+ Server: "5.180.97.200", ServerPort: 36728,
+ Method: "chacha20-ietf-poly1305", Password: "test-pass",
+ }
+ mode := *config.ModeByName("Discord Only")
+
+ cfg := config.BuildConfig(server, mode, nil, nil)
+ data, _ := json.Marshal(cfg)
+ s := string(data)
+
+ if !strings.Contains(s, `"type":"shadowsocks"`) {
+ t.Error("expected shadowsocks outbound")
+ }
+ if !strings.Contains(s, "chacha20-ietf-poly1305") {
+ t.Error("expected method")
+ }
+}
+
+func TestBuildConfigWithRuleSets(t *testing.T) {
+ server := models.Server{
+ Tag: "nl-1", Type: "socks", Server: "1.2.3.4", ServerPort: 1080,
+ }
+ mode := *config.ModeByName("Re-filter (обход блокировок РФ)")
+ ruleSets := []models.RuleSet{
+ {Tag: "refilter-domains", URL: "https://example.com/domains.srs", Format: "binary"},
+ {Tag: "refilter-ip", URL: "https://example.com/ip.srs", Format: "binary"},
+ {Tag: "discord-voice", URL: "https://example.com/discord.srs", Format: "binary"},
+ }
+
+ cfg := config.BuildConfig(server, mode, ruleSets, nil)
+ data, _ := json.Marshal(cfg)
+ s := string(data)
+
+ if !strings.Contains(s, "refilter-domains") {
+ t.Error("expected refilter-domains rule_set")
+ }
+ if !strings.Contains(s, "download_detour") {
+ t.Error("expected download_detour in rule_set")
+ }
+ if !strings.Contains(s, "update_interval") {
+ t.Error("expected update_interval in rule_set")
+ }
+}
+
+func TestBuildBypassIPs(t *testing.T) {
+ ips := config.BuildBypassIPs([]string{"1.2.3.4", "5.180.97.200"})
+
+ found := false
+ for _, ip := range ips {
+ if ip == "1.2.3.4/32" {
+ found = true
+ }
+ }
+ if !found {
+ t.Error("expected dynamic server IP in bypass list")
+ }
+
+ // 5.180.97.200 is already in StaticBypassIPs, should not be duplicated
+ count := 0
+ for _, ip := range ips {
+ if ip == "5.180.97.200/32" {
+ count++
+ }
+ }
+ if count != 1 {
+ t.Errorf("expected 5.180.97.200/32 exactly once, got %d", count)
+ }
+}
+
+func TestAllModes(t *testing.T) {
+ modes := config.AllModes()
+ if len(modes) != 7 {
+ t.Errorf("expected 7 modes, got %d", len(modes))
+ }
+
+ names := config.ModeNames()
+ expected := []string{
+ "Lovense + OBS + AnyDesk",
+ "Lovense + OBS + AnyDesk + Discord",
+ "Lovense + OBS + AnyDesk + Discord + Teams",
+ "Discord Only",
+ "Full (All Traffic)",
+ "Re-filter (обход блокировок РФ)",
+ "Комбо (приложения + Re-filter)",
+ }
+ for i, name := range expected {
+ if names[i] != name {
+ t.Errorf("mode %d: expected %q, got %q", i, name, names[i])
+ }
+ }
+}
+
+func TestModeByName(t *testing.T) {
+ m := config.ModeByName("Full (All Traffic)")
+ if m == nil {
+ t.Fatal("expected to find Full mode")
+ }
+ if m.Final != "proxy" {
+ t.Errorf("Full mode final should be proxy, got %s", m.Final)
+ }
+
+ if config.ModeByName("nonexistent") != nil {
+ t.Error("expected nil for nonexistent mode")
+ }
+}