实现 .NET 10 托盘版网关保活

包含分层探测、可补偿的 IPv4 路由/DNS 切换、自动策略、托盘与分组设置界面、只读诊断工具、离线测试和发布脚本。
This commit is contained in:
chuzhongzai
2026-10-23 23:35:20 +08:00
parent 59a2982b2c
commit 2d85527656
57 changed files with 8623 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
namespace GatewayKeepalive.Tests;
internal static class Program
{
private static async Task<int> Main()
{
var tests = new (string Name, Func<Task> Run)[]
{
("SwitchPolicy: success threshold, cap, and recovery cooldown", SwitchPolicyTests.SuccessThresholdAndCooldownAsync),
("SwitchPolicy: failure threshold and failure cooldown", SwitchPolicyTests.FailureThresholdAndCooldownAsync),
("SwitchPolicy: failure retry has a 30 second floor", SwitchPolicyTests.FailureRetryUsesThirtySecondFloorAsync),
("SwitchPolicy: paused, fixed, and observe-only modes reset counts", SwitchPolicyTests.NonAutoModesResetCountsAsync),
("SwitchPolicy: switch, failure, and explicit reset clear counters", SwitchPolicyTests.CountsResetAfterEventsAsync),
("SwitchService: DNS failure after route write rolls back", SwitchServiceTests.DnsFailureAfterRouteWriteRollsBackAsync),
("SwitchService: egress verification failure rolls back", SwitchServiceTests.EgressFailureRollsBackAsync),
("SwitchService: rollback failure retains journal and raises RecoveryRequired", SwitchServiceTests.RollbackFailureRetainsJournalAsync),
("SwitchService: external modification is never overwritten", SwitchServiceTests.ExternalModificationIsNotOverwrittenAsync),
("SwitchService: cross-network rollback is rejected", SwitchServiceTests.CrossNetworkRollbackIsRejectedAsync),
("SwitchService: profile DNS is rejected", SwitchServiceTests.ProfileDnsIsRejectedAsync),
("SwitchService: destination gateway must be on the adapter subnet", SwitchServiceTests.GatewayMustBeOnAdapterSubnetAsync),
("SwitchService: failed baseline restore compensates original configuration", SwitchServiceTests.BaselineRestoreFailureCompensatesAsync),
("SwitchService: plan preserves an already preferred gateway metric", SwitchServiceTests.PlanPreservesPreferredMetricAsync),
("SwitchService: identical configuration skips route and DNS writes", SwitchServiceTests.SwitchSameConfigurationSkipsWritesAsync),
("SwitchService: only changed DNS is written", SwitchServiceTests.SwitchWritesOnlyChangedDnsAsync),
("SwitchService: only changed routes are written", SwitchServiceTests.SwitchWritesOnlyChangedRoutesAsync),
("SwitchService: unchanged rollback skips route and DNS writes", SwitchServiceTests.RollbackSameConfigurationSkipsWritesAsync),
("SwitchService: unchanged baseline restore skips route and DNS writes", SwitchServiceTests.BaselineRestoreSameConfigurationSkipsWritesAsync),
("GatewayController: unconfigured refresh does no probe or write", GatewayControllerTests.InitialUnconfiguredDoesNotProbeOrWriteAsync),
("GatewayController: first paused save preserves real direct DNS", GatewayControllerTests.ExplicitPausedSavePreservesRealDirectDnsAsync),
("GatewayController: external configuration is not overwritten", GatewayControllerTests.ExternalConfigurationChangeIsNotOverwrittenAsync),
("GatewayController: legacy empty session fingerprint is not writable", GatewayControllerTests.LegacySessionWithoutFingerprintIsNotWritableAsync),
("GatewayController: failed save does not become writable", GatewayControllerTests.SaveFailureAfterSessionWriteDoesNotBecomeWritableAsync),
("GatewayController: unfinished journal blocks automatic control", GatewayControllerTests.UnfinishedJournalBlocksAutomaticControlAsync),
("GatewayController: fixed rollback failure keeps fixed policy", GatewayControllerTests.FixedFailureKeepsFixedPolicyAfterRollbackAsync),
("GatewayController: concurrent refreshes are coalesced", GatewayControllerTests.ConcurrentRefreshIsCoalescedAsync),
("GatewayController: save and refresh writes are serialized", GatewayControllerTests.SaveAndRefreshAreSerializedAsync),
("GatewayController: stop restores when RestoreOnExit is enabled", GatewayControllerTests.StopRestoresWhenRequestedAsync),
("GatewayController: stop restores persisted session before first refresh", GatewayControllerTests.StopRestoresPersistedSessionBeforeFirstRefreshAsync),
("GatewayController: stop leaves network when restore is disabled", GatewayControllerTests.StopLeavesNetworkWhenRestoreIsDisabledAsync),
("GatewayController: failed stop restore leaves controller available", GatewayControllerTests.StopRestoreFailureLeavesControllerAvailableAsync),
("Recovery: failed durable Pause prevents all restore writes", RecoveryRegressionTests.RestorePauseWriteFailurePreventsNetworkWritesAsync),
("Recovery: Pause precedes network restore and survives restart", RecoveryRegressionTests.RestorePersistsPauseBeforeWritesAndRestartAsync),
("Recovery: baseline metadata failure retains journal and durable Pause", RecoveryRegressionTests.RestoreSessionFailureRetainsBaselineJournalAsync),
("Recovery: explicit rollback metadata failure retains journal and durable Pause", RecoveryRegressionTests.RestoreSessionFailureRetainsRollbackJournalAsync),
("Recovery: Stop resolves pending journal then restores original baseline", RecoveryRegressionTests.StopPendingJournalContinuesToOriginalBaselineAsync),
("Guarded switch: foreign route after preflight is preserved", RecoveryRegressionTests.ForeignRouteAfterPreflightIsProtectedAsync),
("Guarded switch: foreign DNS after preflight is preserved", RecoveryRegressionTests.ForeignDnsAfterPreflightIsProtectedAsync),
("Guarded switch: foreign route after routes and before DNS is preserved", RecoveryRegressionTests.ForeignRouteBeforeDnsIsProtectedAsync),
("Guarded switch: foreign DNS after routes and before DNS is preserved", RecoveryRegressionTests.ForeignDnsBeforeDnsIsProtectedAsync),
("NetworkComparison: every owned route, DNS, and network field participates", NetworkComparisonTests.EachOwnedFieldParticipatesAsync),
("NetworkComparison: transaction recovery accepts only exact before/intended rows", NetworkComparisonTests.RecoveryRejectsForeignRowsAsync),
("JsonStore: round trip, backup, and failed serialization preserve the last complete file", JsonStoreTests.AtomicReplacementAsync),
("Windows NativeLayout: x64 structure sizes and metric offsets", NativeLayoutTests.X64LayoutAsync),
};
var failures = 0;
var started = DateTime.UtcNow;
Console.WriteLine($"Running {tests.Length} offline GatewayKeepalive tests.");
foreach (var test in tests)
{
try
{
await test.Run();
Console.WriteLine($"PASS {test.Name}");
}
catch (Exception exception)
{
failures++;
Console.WriteLine($"FAIL {test.Name}");
Console.WriteLine(exception);
}
}
Console.WriteLine($"Completed in {(DateTime.UtcNow - started).TotalSeconds:F2}s: {tests.Length - failures} passed, {failures} failed.");
return failures == 0 ? 0 : 1;
}
}