解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
5.30 KiB
X
using System.Collections;
using System.ComponentModel;
using System.Net;
using System.Reflection;
using NewLife.Net;
using Xunit;

namespace XUnitTest.Net;

public class DnsResolverTests
{
    [Fact]
    [DisplayName("DnsResolver_缓存未过期_直接返回缓存")]
    public void FreshCache_ReturnsDirectly()
    {
        var resolver = new DnsResolver { Expire = TimeSpan.FromMinutes(5) };
        resolver.Set("localhost", [IPAddress.Parse("1.2.3.4")], 300);

        var addrs = resolver.Resolve("localhost");

        Assert.NotNull(addrs);
        Assert.Equal("1.2.3.4", addrs[0].ToString());
    }

    [Fact]
    [DisplayName("DnsResolver_陈旧数据超阈值_同步刷新返回最新地址")]
    public void StaleCache_SyncRefresh()
    {
        // 模拟长时间无人访问后仍指向过期地址的缓存:一小时前更新
        var resolver = new DnsResolver { Expire = TimeSpan.FromMilliseconds(1), StaleTime = TimeSpan.FromMinutes(10) };
        resolver.Set("localhost", [IPAddress.Parse("1.2.3.4")], -3600);

        var addrs = resolver.Resolve("localhost");

        Assert.NotNull(addrs);
        // 超过陈旧阈值时同步刷新,返回本机真实解析结果,而不是缓存中的假地址
        Assert.DoesNotContain(IPAddress.Parse("1.2.3.4"), addrs);
        Assert.Contains(addrs, IPAddress.IsLoopback);
    }

    [Fact]
    [DisplayName("DnsResolver_轻度超时_异步刷新不阻塞本次请求")]
    public void SlightlyExpired_AsyncRefresh()
    {
        // 仅超过 Expire 而未达 StaleTime 的缓存,由后台异步刷新,本次请求立即返回旧值
        var resolver = new DnsResolver { Expire = TimeSpan.FromMilliseconds(1), StaleTime = TimeSpan.FromMinutes(10) };

        // 使用必然解析失败的主机,保证后台刷新不会替换缓存,结果可确定断言
        var host = new String('a', 300) + ".invalid";
        resolver.Set(host, [IPAddress.Parse("1.2.3.4")], -10);

        var addrs = resolver.Resolve(host);

        // 后台异步刷新不影响本次返回,仍为旧地址
        Assert.NotNull(addrs);
        Assert.Equal("1.2.3.4", addrs[0].ToString());
        // 异步路径不设置退避时间,被设置即说明误入同步分支
        Assert.Equal(DateTime.MinValue, GetNextSyncTime(resolver, host));
    }

    [Fact]
    [DisplayName("DnsResolver_同步刷新失败_进入退避不再同步阻塞")]
    public void SyncRefreshFails_BacksOff()
    {
        var resolver = new DnsResolver { Expire = TimeSpan.FromSeconds(30), StaleTime = TimeSpan.FromMilliseconds(1) };

        // 使用必然解析失败的超长主机名,模拟DNS异常
        var host = new String('a', 300) + ".invalid";
        resolver.Set(host, [IPAddress.Parse("1.2.3.4")], -3600);

        // 第一次:陈旧数据触发同步刷新,解析失败后保留旧值
        var addrs = resolver.Resolve(host);
        Assert.NotNull(addrs);
        Assert.Equal("1.2.3.4", addrs[0].ToString());

        // 同步刷新失败后进入退避,避免DNS异常期间每个请求都被同步阻塞
        var nextSync = GetNextSyncTime(resolver, host);
        Assert.True(nextSync > DateTime.Now, "同步刷新失败后应设置退避时间");
    }

    [Fact]
    [DisplayName("DnsResolver_退避期内_不再同步刷新")]
    public void Backoff_NoSyncRefresh()
    {
        var resolver = new DnsResolver { Expire = TimeSpan.FromSeconds(30), StaleTime = TimeSpan.FromMilliseconds(1) };
        var host = new String('a', 300) + ".invalid";
        resolver.Set(host, [IPAddress.Parse("1.2.3.4")], -3600);

        // 第一次:同步刷新失败,进入退避
        var addrs = resolver.Resolve(host);
        Assert.NotNull(addrs);
        Assert.Equal("1.2.3.4", addrs[0].ToString());
        var nextSync1 = GetNextSyncTime(resolver, host);

        // 退避期内再次解析:改走异步刷新,不再同步阻塞,退避时间也不应被延长
        Thread.Sleep(50);
        addrs = resolver.Resolve(host);
        Assert.NotNull(addrs);
        Assert.Equal("1.2.3.4", addrs[0].ToString());
        Assert.Equal(nextSync1, GetNextSyncTime(resolver, host));
    }

    [Theory]
    [DisplayName("DnsResolver_空域名_返回null")]
    [InlineData(null)]
    [InlineData("")]
    public void EmptyHost_ReturnsNull(String host)
    {
        var resolver = new DnsResolver();

        Assert.Null(resolver.Resolve(host));
    }

    [Fact]
    [DisplayName("DnsResolver_首次解析_同步等待并返回本机地址")]
    public void FirstResolve_Sync()
    {
        var resolver = new DnsResolver();

        var addrs = resolver.Resolve("localhost");

        Assert.NotNull(addrs);
        Assert.Contains(addrs, IPAddress.IsLoopback);
    }

    /// <summary>读取缓存项的下次同步刷新时间。退避行为无法从公开返回值观察,只能反射验证</summary>
    /// <param name="resolver">解析器</param>
    /// <param name="host">域名</param>
    /// <returns></returns>
    private static DateTime GetNextSyncTime(DnsResolver resolver, String host)
    {
        var cacheField = typeof(DnsResolver).GetField("_cache", BindingFlags.NonPublic | BindingFlags.Instance);
        var cache = (IDictionary)cacheField!.GetValue(resolver)!;
        var item = cache[host]!;
        return (DateTime)item.GetType().GetProperty("NextSyncTime")!.GetValue(item)!;
    }
}