Files
QVPN/tests/QVPN.Tests/SubscriptionRefreshPolicyTests.cs
Курнат Андрей aae6dd28dd Release QVPN 1.0.5 with Xray and resilient tunnel recovery
Integrate Xray 26.9.9 with the sing-box system tunnel and verify Windows routes during health checks.

Improve endpoint selection, network recovery, subscription refresh deadlines, diagnostics, packaging, and regression coverage.
2026-10-30 06:15:08 +03:00

62 lines
3.1 KiB
C#

using QVPN.Core;
internal static class SubscriptionRefreshPolicyTests
{
public static async Task RunAsync(Func<string, Action, Task> test)
{
var now = DateTimeOffset.Parse("2026-10-06T21:00:00Z");
await test("Service startup refreshes stale or undated subscription before health succeeds", () =>
{
var policy = new SubscriptionRefreshPolicy();
Check(policy.ShouldRefreshOnConnect(now.AddHours(-2), now));
Check(policy.ShouldRefreshOnConnect(null, now));
Check(policy.ShouldRefreshOnConnect(now.AddMinutes(-15), now));
Check(!policy.ShouldRefreshOnConnect(now.AddMinutes(-14), now));
});
await test("A new explicit connect checks current endpoint even with recently cached subscription", () =>
{
var policy = new SubscriptionRefreshPolicy();
Check(policy.ShouldRefreshOnConnect(now, now, explicitConnect: true));
policy.Attempted(now);
Check(!policy.ShouldRefreshOnConnect(now, now.AddSeconds(59), explicitConnect: true));
Check(policy.ShouldRefreshOnConnect(now, now.AddSeconds(60), explicitConnect: true));
});
await test("Endpoint outage refresh is allowed without a connected engine and limited to once per minute", () =>
{
var policy = new SubscriptionRefreshPolicy();
Check(policy.ShouldRefreshAfterEndpointFailure(now));
policy.Attempted(now);
for (int seconds = 0; seconds < 60; seconds++) Check(!policy.ShouldRefreshAfterEndpointFailure(now.AddSeconds(seconds)));
Check(policy.ShouldRefreshAfterEndpointFailure(now.AddSeconds(60)));
});
await test("Manual or routine subscription attempts also suppress immediate outage refresh loops", () =>
{
var policy = new SubscriptionRefreshPolicy();
policy.Attempted(now);
Check(!policy.ShouldRefreshAfterEndpointFailure(now.AddSeconds(1)));
Check(!policy.ShouldRefreshOnConnect(now.AddHours(-2), now.AddSeconds(1)));
policy.Attempted(now.AddSeconds(60));
Check(!policy.ShouldRefreshAfterEndpointFailure(now.AddSeconds(61)));
});
await test("A fresh successful subscription prevents automatic startup refresh after the retry cooldown", () =>
{
var policy = new SubscriptionRefreshPolicy();
policy.Attempted(now);
Check(!policy.ShouldRefreshOnConnect(now, now.AddMinutes(2)));
Check(policy.ShouldRefreshOnConnect(now, now.AddMinutes(15)));
});
await test("Clock rollback does not cause repeated automatic subscription refresh", () =>
{
var policy = new SubscriptionRefreshPolicy();
policy.Attempted(now);
Check(!policy.ShouldRefreshAfterEndpointFailure(now.AddMinutes(-5)));
Check(!policy.ShouldRefreshOnConnect(now.AddHours(-2), now.AddMinutes(-5), explicitConnect: true));
});
}
private static void Check(bool condition)
{
if (!condition) throw new InvalidOperationException("Subscription refresh policy regression.");
}
}