Files
QVPN/tests/QVPN.Tests/SubscriptionTransportTests.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

64 lines
3.4 KiB
C#

using System.Text;
using QVPN.Core;
public static class SubscriptionTransportTests
{
public static async Task RunAsync(Func<string, Action, Task> test)
{
await test("Subscription keeps distinct flow and fingerprint variants of one endpoint", () =>
{
var nodes = Subscription.Parse(string.Join('\n',
Link("xtls-rprx-vision", "chrome"), Link("", "chrome"),
Link("xtls-rprx-vision", "firefox"), Link("xtls-rprx-vision-udp443", "chrome")));
Check(nodes.Count == 4 && nodes.Select(n => n.Id).Distinct().Count() == 4);
Check(nodes.Select(n => (n.Flow, n.Fingerprint)).SequenceEqual(new[]
{
("xtls-rprx-vision", "chrome"), ("", "chrome"),
("xtls-rprx-vision", "firefox"), ("xtls-rprx-vision-udp443", "chrome")
}));
});
await test("Subscription presentation and parameter order do not duplicate a transport", () =>
{
var first = Link("xtls-rprx-vision", "chrome", "First location");
var same = Link("xtls-rprx-vision", "chrome", "Renamed location");
int question = same.IndexOf('?'), fragment = same.IndexOf('#');
same = same[..(question + 1)] + string.Join('&', same[(question + 1)..fragment].Split('&').Reverse()) + same[fragment..];
var nodes = Subscription.Parse(first + "\n" + same);
Check(nodes.Count == 1 && nodes[0].Name == "First location");
Check(Subscription.Parse(same).Single().Id == nodes[0].Id);
});
await test("Vision UDP443 is preserved in plain and encoded subscriptions and Xray", () =>
{
string link = Link("xtls-rprx-vision-udp443", "chrome");
foreach (string text in new[] { link, Convert.ToBase64String(Encoding.UTF8.GetBytes(link)) })
{
var node = Subscription.Parse(text).Single();
Check(node.Flow == "xtls-rprx-vision-udp443");
var user = XrayConfigBuilder.Build(node)["outbounds"]![0]!["settings"]!["vnext"]![0]!["users"]![0]!;
Check(user["flow"]!.GetValue<string>() == node.Flow);
}
});
await test("Subscription accepts only the exact supported flow names", () =>
{
foreach (string flow in new[] { "xtls-rprx-vision-udp", "xtls-rprx-vision-udp443-extra", "XTLS-rprx-vision-udp443", "xtls-rprx-vision\n" })
{
try { Subscription.Parse(Link(flow, "chrome")); }
catch (InvalidDataException) { continue; }
throw new InvalidOperationException("Unsupported subscription flow was accepted.");
}
});
}
private static string Link(string flow, string fingerprint, string name = "Synthetic location")
{
string uuid = new Guid(Enumerable.Range(1, 16).Select(i => (byte)i).ToArray()).ToString();
string key = Convert.ToBase64String(Enumerable.Range(1, 32).Select(i => (byte)i).ToArray()).TrimEnd('=').Replace('+', '-').Replace('/', '_');
return $"vless://{uuid}@8.8.8.8:443?security=reality&type=tcp&encryption=none&pbk={key}&sid=abcd&sni=example.com&fp={fingerprint}&flow={Uri.EscapeDataString(flow)}#{Uri.EscapeDataString(name)}";
}
private static void Check(bool condition)
{
if (!condition) throw new InvalidOperationException("Subscription transport regression.");
}
}