227 lines
6.4 KiB
Go
227 lines
6.4 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestTransportMigrateSingBoxDNSConfigMapLegacyUDP(t *testing.T) {
|
|
root := map[string]any{
|
|
"dns": map[string]any{
|
|
"servers": []any{
|
|
map[string]any{
|
|
"tag": "dns-direct",
|
|
"address": "1.1.1.1",
|
|
"address_resolver": "dns-local",
|
|
"address_strategy": "prefer_ipv4",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
changed, warns := transportMigrateSingBoxDNSConfigMap(root)
|
|
if !changed {
|
|
t.Fatalf("expected changed=true")
|
|
}
|
|
if len(warns) != 0 {
|
|
t.Fatalf("unexpected warnings: %#v", warns)
|
|
}
|
|
|
|
dns := root["dns"].(map[string]any)
|
|
servers := dns["servers"].([]any)
|
|
srv := servers[0].(map[string]any)
|
|
if strings.TrimSpace(asString(srv["type"])) != "udp" {
|
|
t.Fatalf("expected type=udp, got %#v", srv["type"])
|
|
}
|
|
if strings.TrimSpace(asString(srv["server"])) != "1.1.1.1" {
|
|
t.Fatalf("expected server=1.1.1.1, got %#v", srv["server"])
|
|
}
|
|
if _, ok := srv["address"]; ok {
|
|
t.Fatalf("legacy address key must be removed")
|
|
}
|
|
if strings.TrimSpace(asString(srv["domain_resolver"])) != "dns-local" {
|
|
t.Fatalf("expected domain_resolver migration, got %#v", srv["domain_resolver"])
|
|
}
|
|
if strings.TrimSpace(asString(srv["domain_strategy"])) != "prefer_ipv4" {
|
|
t.Fatalf("expected domain_strategy migration, got %#v", srv["domain_strategy"])
|
|
}
|
|
if _, ok := srv["detour"]; ok {
|
|
t.Fatalf("detour=direct should be removed in migrated config")
|
|
}
|
|
}
|
|
|
|
func TestTransportMigrateSingBoxDNSConfigMapHTTPS(t *testing.T) {
|
|
root := map[string]any{
|
|
"dns": map[string]any{
|
|
"servers": []any{
|
|
map[string]any{
|
|
"tag": "dns-doh",
|
|
"address": "https://1.1.1.1/dns-query",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
changed, warns := transportMigrateSingBoxDNSConfigMap(root)
|
|
if !changed {
|
|
t.Fatalf("expected changed=true")
|
|
}
|
|
if len(warns) != 0 {
|
|
t.Fatalf("unexpected warnings: %#v", warns)
|
|
}
|
|
dns := root["dns"].(map[string]any)
|
|
srv := dns["servers"].([]any)[0].(map[string]any)
|
|
if strings.TrimSpace(asString(srv["type"])) != "https" {
|
|
t.Fatalf("expected type=https, got %#v", srv["type"])
|
|
}
|
|
if strings.TrimSpace(asString(srv["server"])) != "1.1.1.1" {
|
|
t.Fatalf("expected server=1.1.1.1, got %#v", srv["server"])
|
|
}
|
|
if _, ok := srv["path"]; ok {
|
|
t.Fatalf("default /dns-query path should not be forced into config")
|
|
}
|
|
}
|
|
|
|
func TestTransportMigrateSingBoxDNSConfigMapTypedServerDetourDirectRemoved(t *testing.T) {
|
|
root := map[string]any{
|
|
"dns": map[string]any{
|
|
"servers": []any{
|
|
map[string]any{
|
|
"tag": "dns-direct",
|
|
"type": "udp",
|
|
"server": "1.1.1.1",
|
|
"detour": "direct",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
changed, warns := transportMigrateSingBoxDNSConfigMap(root)
|
|
if !changed {
|
|
t.Fatalf("expected changed=true when removing direct detour")
|
|
}
|
|
if len(warns) != 0 {
|
|
t.Fatalf("unexpected warnings: %#v", warns)
|
|
}
|
|
dns := root["dns"].(map[string]any)
|
|
srv := dns["servers"].([]any)[0].(map[string]any)
|
|
if _, ok := srv["detour"]; ok {
|
|
t.Fatalf("detour should be removed for typed DNS server")
|
|
}
|
|
}
|
|
|
|
func TestTransportSystemdBackendProvisionSingBoxDNSMigrationStrictFail(t *testing.T) {
|
|
origRunner := transportRunCommand
|
|
origUnitsDir := transportSystemdUnitsDir
|
|
defer func() {
|
|
transportRunCommand = origRunner
|
|
transportSystemdUnitsDir = origUnitsDir
|
|
}()
|
|
|
|
transportSystemdUnitsDir = t.TempDir()
|
|
transportRunCommand = func(_ time.Duration, _ string, _ ...string) (string, string, int, error) {
|
|
return "", "", 0, nil
|
|
}
|
|
|
|
cfgDir := t.TempDir()
|
|
cfg := filepath.Join(cfgDir, "singbox.json")
|
|
if err := os.WriteFile(cfg, []byte("{broken-json"), 0o644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
|
|
client := TransportClient{
|
|
ID: "sg-mig-strict",
|
|
Kind: TransportClientSingBox,
|
|
Config: map[string]any{
|
|
"runner": "systemd",
|
|
"unit": "sg-mig-strict.service",
|
|
"bin": "/usr/bin/sing-box",
|
|
"singbox_config_path": cfg,
|
|
"singbox_dns_migrate_legacy": true,
|
|
"singbox_dns_migrate_strict": true,
|
|
},
|
|
}
|
|
res := selectTransportBackend(client).Provision(client)
|
|
if res.OK {
|
|
t.Fatalf("expected strict migration failure, got %#v", res)
|
|
}
|
|
if res.Code != "TRANSPORT_BACKEND_SINGBOX_DNS_MIGRATE_FAILED" {
|
|
t.Fatalf("unexpected code: %#v", res)
|
|
}
|
|
}
|
|
|
|
func TestTransportSystemdBackendProvisionSingBoxDNSMigrationWritesBackup(t *testing.T) {
|
|
origRunner := transportRunCommand
|
|
origUnitsDir := transportSystemdUnitsDir
|
|
defer func() {
|
|
transportRunCommand = origRunner
|
|
transportSystemdUnitsDir = origUnitsDir
|
|
}()
|
|
|
|
tmpDir := t.TempDir()
|
|
transportSystemdUnitsDir = tmpDir
|
|
transportRunCommand = func(_ time.Duration, name string, args ...string) (string, string, int, error) {
|
|
cmd := name + " " + strings.Join(args, " ")
|
|
if cmd == "systemctl daemon-reload" {
|
|
return "", "", 0, nil
|
|
}
|
|
return "", "", 0, nil
|
|
}
|
|
|
|
cfgDir := t.TempDir()
|
|
cfg := filepath.Join(cfgDir, "singbox.json")
|
|
legacy := map[string]any{
|
|
"dns": map[string]any{
|
|
"servers": []any{
|
|
map[string]any{
|
|
"tag": "dns-direct",
|
|
"address": "1.1.1.1",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
raw, _ := json.Marshal(legacy)
|
|
if err := os.WriteFile(cfg, raw, 0o644); err != nil {
|
|
t.Fatalf("write config: %v", err)
|
|
}
|
|
|
|
client := TransportClient{
|
|
ID: "sg-mig-ok",
|
|
Kind: TransportClientSingBox,
|
|
Config: map[string]any{
|
|
"runner": "systemd",
|
|
"unit": "sg-mig-ok.service",
|
|
"bin": "/usr/bin/sing-box",
|
|
"singbox_config_path": cfg,
|
|
"singbox_dns_migrate_legacy": true,
|
|
},
|
|
}
|
|
res := selectTransportBackend(client).Provision(client)
|
|
if !res.OK {
|
|
t.Fatalf("expected provision success, got %#v", res)
|
|
}
|
|
if !strings.Contains(strings.ToLower(res.Stdout), "dns-migrate:") {
|
|
t.Fatalf("expected migration note in stdout, got %#v", res.Stdout)
|
|
}
|
|
if _, err := os.Stat(cfg + ".legacy-dns.bak"); err != nil {
|
|
t.Fatalf("expected backup file, stat err=%v", err)
|
|
}
|
|
updatedRaw, err := os.ReadFile(cfg)
|
|
if err != nil {
|
|
t.Fatalf("read updated config: %v", err)
|
|
}
|
|
var updated map[string]any
|
|
if err := json.Unmarshal(updatedRaw, &updated); err != nil {
|
|
t.Fatalf("parse updated config: %v", err)
|
|
}
|
|
dns := updated["dns"].(map[string]any)
|
|
srv := dns["servers"].([]any)[0].(map[string]any)
|
|
if strings.TrimSpace(asString(srv["type"])) != "udp" {
|
|
t.Fatalf("expected migrated type=udp, got %#v", srv["type"])
|
|
}
|
|
if _, ok := srv["address"]; ok {
|
|
t.Fatalf("legacy address key should be removed after migration")
|
|
}
|
|
}
|