2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 06:47:20 +08:00
Files
panel/pkg/dns/dns_test.go
Copilot 47537e282b feat(dns): 优化系统工具箱DNS设置以适配现代Linux系统 (#1202)
* Initial plan

* feat(dns): 优化系统工具箱DNS设置以适配现代Linux系统

- 创建 pkg/dns 包实现多种DNS管理方式
- 支持 NetworkManager (RHEL 9.x/10.x)
- 支持 netplan (Debian 12+/Ubuntu 22+)
- 回退到直接修改 /etc/resolv.conf
- 更新前端显示当前DNS管理方式
- 添加单元测试

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复代码审查发现的问题

- 提取 shell 参数转义逻辑到独立函数
- 修正 netplan 配置文件选择的注释说明
- 使用常量替代硬编码的缩进值
- 添加前端空数组安全检查

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 优化

* fix: 优化

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 06:33:23 +08:00

326 lines
8.4 KiB
Go

package dns
import (
"os"
"testing"
"github.com/stretchr/testify/suite"
)
type DNSTestSuite struct {
suite.Suite
}
func TestDNSTestSuite(t *testing.T) {
suite.Run(t, &DNSTestSuite{})
}
func (s *DNSTestSuite) SetupTest() {
if _, err := os.Stat("testdata"); os.IsNotExist(err) {
s.NoError(os.MkdirAll("testdata", 0755))
}
}
func (s *DNSTestSuite) TearDownTest() {
s.NoError(os.RemoveAll("testdata"))
}
func (s *DNSTestSuite) TestManagerString() {
s.Equal("NetworkManager", ManagerNetworkManager.String())
s.Equal("netplan", ManagerNetplan.String())
s.Equal("resolv.conf", ManagerResolvConf.String())
s.Equal("unknown", ManagerUnknown.String())
}
func (s *DNSTestSuite) TestDetectManager() {
// DetectManager 会返回一个有效的 Manager 类型
manager := DetectManager()
s.True(manager >= ManagerUnknown && manager <= ManagerResolvConf)
}
func (s *DNSTestSuite) TestUpdateNetplanDNS() {
// 测试基本的 netplan 配置更新
content := `network:
version: 2
ethernets:
eth0:
dhcp4: true`
result, err := updateNetplanDNS(content, "8.8.8.8", "8.8.4.4")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "8.8.8.8")
s.Contains(result, "8.8.4.4")
}
func (s *DNSTestSuite) TestUpdateNetplanDNSWithExisting() {
// 测试替换现有的 DNS 配置
content := `network:
version: 2
ethernets:
eth0:
dhcp4: true
nameservers:
addresses: [1.1.1.1, 1.0.0.1]`
result, err := updateNetplanDNS(content, "8.8.8.8", "8.8.4.4")
s.NoError(err)
s.Contains(result, "8.8.8.8")
s.Contains(result, "8.8.4.4")
// 旧的 DNS 应该被移除
s.NotContains(result, "1.1.1.1")
s.NotContains(result, "1.0.0.1")
}
// TestUpdateNetplanDNSWithWifi 测试 wifi 网络接口配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithWifi() {
content := `network:
version: 2
wifis:
wlan0:
dhcp4: true
access-points:
"my-wifi":
password: "secret"`
result, err := updateNetplanDNS(content, "8.8.8.8", "")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "8.8.8.8")
}
// TestUpdateNetplanDNSWithMultipleInterfaces 测试多接口配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithMultipleInterfaces() {
content := `network:
version: 2
ethernets:
eth0:
dhcp4: true
eth1:
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1`
result, err := updateNetplanDNS(content, "8.8.8.8", "114.114.114.114")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "8.8.8.8")
s.Contains(result, "114.114.114.114")
}
// TestUpdateNetplanDNSWithRoutes 测试带路由配置的接口
func (s *DNSTestSuite) TestUpdateNetplanDNSWithRoutes() {
content := `network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1`
result, err := updateNetplanDNS(content, "223.5.5.5", "223.6.6.6")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "223.5.5.5")
s.Contains(result, "223.6.6.6")
// 路由配置应该被保留
s.Contains(result, "routes:")
}
// TestUpdateNetplanDNSWithBond 测试 bond 网络配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithBond() {
content := `network:
version: 2
ethernets:
eth0: {}
eth1: {}
bonds:
bond0:
interfaces:
- eth0
- eth1
addresses:
- 10.0.0.100/24
parameters:
mode: active-backup
primary: eth0`
result, err := updateNetplanDNS(content, "8.8.8.8", "8.8.4.4")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "8.8.8.8")
// bond 配置应该被保留
s.Contains(result, "bonds:")
s.Contains(result, "bond0:")
}
// TestUpdateNetplanDNSWithRenderer 测试带 renderer 的配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithRenderer() {
content := `network:
version: 2
renderer: networkd
ethernets:
ens3:
dhcp4: true`
result, err := updateNetplanDNS(content, "8.8.8.8", "")
s.NoError(err)
s.Contains(result, "renderer: networkd")
s.Contains(result, "nameservers:")
}
// TestUpdateNetplanDNSPreserveSearch 测试保留 search 域
func (s *DNSTestSuite) TestUpdateNetplanDNSPreserveSearch() {
content := `network:
version: 2
ethernets:
eth0:
dhcp4: true
nameservers:
addresses: [1.1.1.1]
search: [example.com, local]`
result, err := updateNetplanDNS(content, "8.8.8.8", "8.8.4.4")
s.NoError(err)
s.Contains(result, "8.8.8.8")
s.Contains(result, "8.8.4.4")
// 旧 DNS 应该被替换
s.NotContains(result, "1.1.1.1")
// search 域应该被保留
s.Contains(result, "search:")
s.Contains(result, "example.com")
}
// TestUpdateNetplanDNSEmptyConfig 测试空配置
// 注意:当配置为空时,会尝试检测活动网络接口并自动添加配置
func (s *DNSTestSuite) TestUpdateNetplanDNSEmptyConfig() {
content := `network:
version: 2`
result, err := updateNetplanDNS(content, "8.8.8.8", "")
// 如果系统有活动网络接口,应该成功
// 如果没有(如在 CI 环境),应该返回错误
if err == nil {
s.Contains(result, "nameservers:")
s.Contains(result, "8.8.8.8")
}
}
// TestUpdateNetplanDNSInvalidYAML 测试无效的 YAML
func (s *DNSTestSuite) TestUpdateNetplanDNSInvalidYAML() {
content := `network:
version: 2
ethernets:
eth0:
invalid yaml here`
_, err := updateNetplanDNS(content, "8.8.8.8", "")
s.Error(err)
}
// TestUpdateNetplanDNSWithMatch 测试带 match 规则的配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithMatch() {
content := `network:
version: 2
ethernets:
id0:
match:
macaddress: "00:11:22:33:44:55"
set-name: eth0
dhcp4: true`
result, err := updateNetplanDNS(content, "8.8.8.8", "")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "match:")
s.Contains(result, "macaddress")
}
// TestUpdateNetplanDNSWithVlan 测试 VLAN 配置
func (s *DNSTestSuite) TestUpdateNetplanDNSWithVlan() {
content := `network:
version: 2
ethernets:
eth0:
dhcp4: false
vlans:
vlan100:
id: 100
link: eth0
addresses:
- 192.168.100.1/24`
result, err := updateNetplanDNS(content, "8.8.8.8", "8.8.4.4")
s.NoError(err)
s.Contains(result, "nameservers:")
s.Contains(result, "vlans:")
s.Contains(result, "vlan100:")
}
func (s *DNSTestSuite) TestFindNetplanConfig() {
// findNetplanConfig 应该能正常执行不崩溃
// 在实际系统上可能会找到配置文件也可能找不到
configPath, err := findNetplanConfig()
if err == nil {
// 如果找到了配置文件,验证文件确实存在
s.FileExists(configPath)
}
// 无论是否找到配置文件,函数都应该正常返回
}
func (s *DNSTestSuite) TestSetDNSWithResolvConf() {
// 这个测试需要 root 权限才能写入 /etc/resolv.conf
// 在非特权环境中跳过
s.T().Skip("需要 root 权限")
}
func (s *DNSTestSuite) TestGetDNS() {
// GetDNS 应该能返回当前的 DNS 配置
dns, manager, err := GetDNS()
// 即使出错也不应该 panic
if err != nil {
s.T().Logf("获取 DNS 出错(可能没有权限): %v", err)
return
}
s.NotNil(dns)
s.True(manager >= ManagerUnknown && manager <= ManagerResolvConf)
}
// TestIsValidNetworkInterface 测试网络接口名称验证
func (s *DNSTestSuite) TestIsValidNetworkInterface() {
// 有效的接口名
s.True(isValidNetworkInterface("eth0"))
s.True(isValidNetworkInterface("ens3"))
s.True(isValidNetworkInterface("enp0s3"))
s.True(isValidNetworkInterface("wlan0"))
s.True(isValidNetworkInterface("bond0"))
s.True(isValidNetworkInterface("br0"))
// 无效的接口名(虚拟接口)
s.False(isValidNetworkInterface("lo"))
s.False(isValidNetworkInterface("docker0"))
s.False(isValidNetworkInterface("veth12345"))
s.False(isValidNetworkInterface("br-abc123"))
s.False(isValidNetworkInterface("virbr0"))
s.False(isValidNetworkInterface("tun0"))
s.False(isValidNetworkInterface("tap0"))
s.False(isValidNetworkInterface("flannel.1"))
s.False(isValidNetworkInterface("cni0"))
s.False(isValidNetworkInterface("cali12345"))
s.False(isValidNetworkInterface(""))
}
// TestDetectActiveInterface 测试活动接口检测
func (s *DNSTestSuite) TestDetectActiveInterface() {
// 这个测试依赖系统环境,只验证函数不会 panic
iface := detectActiveInterface()
// 在有网络的环境下应该能检测到接口
// 在 CI 环境可能返回空字符串
if iface != "" {
s.True(isValidNetworkInterface(iface))
}
}