lolly/internal/integration/resolver_test.go
xfy 9495a548e6 feat(proxy,server,logging): 集成变量系统和 DNS 解析器
变量系统集成:
- logging: 访问日志使用 variable 包展开模板
- rewrite: 重写规则支持变量展开
- proxy: 请求/响应头设置支持变量展开

DNS 解析器集成:
- app: 创建并启用 Resolver
- server: SetResolver/GetResolver 方法传递给 proxy
- proxy: SetResolver/Start 方法,后台 DNS 刷新协程
- proxy_dns.go: DNS 刷新逻辑和 IP 直连支持

新增集成测试:
- internal/integration/variable_test.go
- internal/integration/resolver_test.go

文档更新:
- docs/config-reference.md 配置参考文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 11:36:49 +08:00

225 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// resolver_integration_test.go - DNS 解析器集成测试
//
// 测试 DNS 解析器与代理的集成
//
// 作者xfy
package integration
import (
"context"
"testing"
"time"
"rua.plus/lolly/internal/config"
"rua.plus/lolly/internal/resolver"
)
// TestResolverBasicLookup 测试基本 DNS 解析功能
func TestResolverBasicLookup(t *testing.T) {
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"8.8.8.8:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 测试解析一个已知域名
ips, err := r.LookupHost(ctx, "dns.google")
if err != nil {
t.Skipf("Skipping DNS test (network unavailable): %v", err)
_ = r.Stop()
return
}
if len(ips) == 0 {
t.Error("expected at least one IP for dns.google")
}
_ = r.Stop()
}
// TestResolverCache 测试 DNS 缓存功能
func TestResolverCache(t *testing.T) {
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"8.8.8.8:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
ctx := context.Background()
// 第一次查询(缓存未命中)
start := time.Now()
ips1, err := r.LookupHostWithCache(ctx, "dns.google")
if err != nil {
t.Skipf("Skipping DNS test (network unavailable): %v", err)
_ = r.Stop()
return
}
duration1 := time.Since(start)
// 第二次查询(应该命中缓存)
start = time.Now()
ips2, err := r.LookupHostWithCache(ctx, "dns.google")
if err != nil {
t.Errorf("second lookup failed: %v", err)
_ = r.Stop()
return
}
duration2 := time.Since(start)
// 验证缓存命中(第二次应该更快)
if duration2 > duration1 {
t.Logf("Warning: cached lookup (%v) slower than uncached (%v)", duration2, duration1)
}
// 验证返回的 IP 相同
if len(ips1) != len(ips2) {
t.Errorf("cached result different: got %d IPs, expected %d", len(ips2), len(ips1))
}
// 检查统计信息
stats := r.Stats()
if stats.CacheHits < 1 {
t.Error("expected at least 1 cache hit")
}
if stats.CacheMisses < 1 {
t.Error("expected at least 1 cache miss")
}
_ = r.Stop()
}
// TestResolverTimeout 测试 DNS 查询超时
func TestResolverTimeout(t *testing.T) {
// 使用一个不可达的地址
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"192.0.2.1:53"}, // TEST-NET-1不会响应
Valid: 30 * time.Second,
Timeout: 1 * time.Second, // 短超时
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
ctx := context.Background()
start := time.Now()
_, err := r.LookupHost(ctx, "example.com")
elapsed := time.Since(start)
// 注意:在某些网络环境下,这个地址可能会被防火墙快速拒绝
// 所以我们只验证超时时间是否合理,不要求一定返回错误
if err == nil {
t.Log("Warning: expected timeout error but got success (network may have different behavior)")
}
// 验证超时时间不超过合理范围(允许一些偏差)
if elapsed > 3*time.Second {
t.Errorf("timeout took too long: %v", elapsed)
}
_ = r.Stop()
}
// TestResolverNXDOMAIN 测试 NXDOMAIN 错误处理
func TestResolverNXDOMAIN(t *testing.T) {
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"8.8.8.8:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
ctx := context.Background()
// 查询一个不存在的域名
_, err := r.LookupHost(ctx, "this-should-not-exist-12345.example")
// 注意:某些 DNS 服务器可能配置为返回特定响应而不是 NXDOMAIN
// 这里我们只记录结果,不强制要求错误
if err == nil {
t.Log("Warning: no error for non-existent domain (DNS server may have different behavior)")
}
_ = r.Stop()
}
// TestResolverStats 测试解析器统计信息
func TestResolverStats(t *testing.T) {
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"8.8.8.8:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
// 初始状态
stats := r.Stats()
if stats.CacheEntries != 0 {
t.Errorf("expected 0 cache entries initially, got %d", stats.CacheEntries)
}
ctx := context.Background()
// 执行几次查询
_, _ = r.LookupHostWithCache(ctx, "dns.google")
_, _ = r.LookupHostWithCache(ctx, "cloudflare.com")
// 验证缓存条目
stats = r.Stats()
if stats.CacheEntries < 2 {
t.Errorf("expected at least 2 cache entries, got %d", stats.CacheEntries)
}
_ = r.Stop()
}
// TestResolverRefresh 测试 DNS 缓存刷新
func TestResolverRefresh(t *testing.T) {
cfg := &config.ResolverConfig{
Enabled: true,
Addresses: []string{"8.8.8.8:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
}
r := resolver.New(cfg)
ctx := context.Background()
// 先查询一次
_, _ = r.LookupHostWithCache(ctx, "dns.google")
// 刷新缓存
err := r.Refresh("dns.google")
if err != nil {
t.Errorf("refresh failed: %v", err)
}
_ = r.Stop()
}