Files
chuzhongzai 2d85527656 实现 .NET 10 托盘版网关保活
包含分层探测、可补偿的 IPv4 路由/DNS 切换、自动策略、托盘与分组设置界面、只读诊断工具、离线测试和发布脚本。
2026-10-23 23:35:20 +08:00

97 lines
5.6 KiB
C#

using GatewayKeepalive.Core;
namespace GatewayKeepalive.Tests;
internal static class NetworkComparisonTests
{
public static Task EachOwnedFieldParticipatesAsync()
{
var baseline = Fixtures.Baseline();
var route = baseline.Routes[0];
Check.True(NetworkComparison.SameConfiguration(baseline, baseline with { CapturedAt = baseline.CapturedAt.AddMinutes(1) }), "Capture time is observational metadata, not network configuration");
Check.True(NetworkComparison.SameConfiguration(baseline, baseline with { EffectiveDns = ["192.168.6.254"] }), "Effective DNS is derived state; configured DNS owns the snapshot");
Check.True(NetworkComparison.SameConfiguration(baseline, baseline with { Routes = [route] }), "Identical route rows must compare equal");
foreach (var changed in new[]
{
baseline with { Routes = [route with { Metric = route.Metric + 1 }] },
baseline with { Routes = [route with { Protocol = route.Protocol + 1 }] },
baseline with { Routes = [route with { Publish = !route.Publish }] },
baseline with { Routes = [route with { Immortal = !route.Immortal }] },
baseline with { Routes = [route with { InterfaceLuid = route.InterfaceLuid + 1 }] },
baseline with { Routes = [route with { InterfaceIndex = route.InterfaceIndex + 1 }] },
baseline with { Routes = [route with { Gateway = "192.168.6.2" }] },
baseline with { Dns = new DnsConfiguration(Automatic: false, Servers: [], ProfileServers: []) },
baseline with { Dns = new DnsConfiguration(Automatic: true, Servers: ["192.168.6.1"], ProfileServers: []) },
baseline with { Dns = new DnsConfiguration(Automatic: true, Servers: [], ProfileServers: ["192.168.6.1"]) },
baseline with { Adapter = baseline.Adapter with { Id = Guid.NewGuid() } },
baseline with { Adapter = baseline.Adapter with { Index = baseline.Adapter.Index + 1 } },
baseline with { Adapter = baseline.Adapter with { NetworkFingerprint = "network-b" } },
baseline with { Adapter = baseline.Adapter with { NetworkFingerprint = " " } },
})
{
Check.False(NetworkComparison.SameConfiguration(baseline, changed), "Every identity, route ownership field, and DNS component must participate in equality");
}
Check.False(
NetworkComparison.SameRoutes([route], [route, new RouteEntry("192.168.6.2", 1, 0x1000, 7)]),
"An added route must change the owned route set");
Check.True(
NetworkComparison.SameRoutes(
[route, new RouteEntry("192.168.6.2", 1, 0x1000, 7)],
[new RouteEntry("192.168.6.2", 1, 0x1000, 7), route]),
"Route order should not matter");
return Task.CompletedTask;
}
public static Task RecoveryRejectsForeignRowsAsync()
{
var before = Fixtures.Baseline();
var direct = before.Routes[0];
var proxy = new RouteEntry("192.168.6.2", 1, direct.InterfaceLuid, direct.InterfaceIndex);
var intended = before with
{
Routes = [direct with { Metric = 101 }, proxy],
Dns = new DnsConfiguration(Automatic: false, Servers: ["192.168.6.2"], ProfileServers: []),
};
var journal = new SwitchJournal(before, intended, "ApplyingDns", DateTimeOffset.UtcNow);
Check.True(NetworkComparison.BelongsToTransaction(journal, before), "The untouched pre-switch state belongs to the transaction");
Check.True(NetworkComparison.BelongsToTransaction(journal, intended), "The fully intended state belongs to the transaction");
Check.True(
NetworkComparison.BelongsToTransaction(journal, before with { Routes = intended.Routes }),
"Routes may be applied while DNS still has its prior value");
Check.True(
NetworkComparison.BelongsToTransaction(journal, before with { Dns = intended.Dns }),
"DNS may be applied while routes still have their prior value");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Routes = [direct with { Metric = 50 }, proxy] }),
"A same-gateway row with a foreign metric must not be treated as owned");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Routes = [direct, proxy with { Metric = 2 }] }),
"A same-gateway row with a foreign proxy metric must not be treated as owned");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Routes = [proxy] }),
"A missing row required by both before and intended must stop recovery");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Routes = [.. intended.Routes, new RouteEntry("10.1.2.3", 1, 0x2000, 8)] }),
"A foreign route added by another tool must stop recovery");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Dns = new DnsConfiguration(false, ["10.1.2.53"], []) }),
"Foreign DNS must stop recovery");
Check.False(
NetworkComparison.BelongsToTransaction(journal, before with { Adapter = before.Adapter with { NetworkFingerprint = "network-b" } }),
"A different network must stop recovery");
var reordered = before with
{
Routes = [proxy, direct with { Metric = 101 }],
Dns = intended.Dns,
};
Check.True(NetworkComparison.BelongsToTransaction(journal, reordered), "Owned route rows should be compared independently of order");
return Task.CompletedTask;
}
}