91 lines
5.6 KiB
C#
91 lines
5.6 KiB
C#
using System.Text.Json.Nodes;
|
||
|
||
namespace MoShiro.Backend.Tests;
|
||
|
||
/// <summary>真实的后端 + 真实的 Agent + 真实的拨号侧 Agent(都在进程内,回环网络)。</summary>
|
||
// 慢的端到端测试(真实时间的阶梯、要等测量块出来):CI 里单独一批跑(Category=Slow),不拦着出镜像,见 ci/verify-backend.sh
|
||
[Trait("Category", "Slow")]
|
||
public sealed class DialerEndToEndTests : IClassFixture<BackendFixture>
|
||
{
|
||
private readonly BackendFixture _backend;
|
||
|
||
public DialerEndToEndTests(BackendFixture backend)
|
||
{
|
||
_backend = backend;
|
||
}
|
||
|
||
private async Task<JsonNode> AgentViewAsync(AgentHarness agent) => await _backend.GetJsonAsync(_backend.Admin, "/api/admin/agents/" + agent.Id);
|
||
|
||
private async Task<int> ChunkCountAsync(AgentHarness agent, string kind) =>
|
||
(await _backend.GetJsonAsync(_backend.Admin, $"/api/admin/raw/chunks?agent_id={agent.Id}"))["items"]!.AsArray().Count(c => c!["kind"]!.GetValue<string>() == kind);
|
||
|
||
[Fact]
|
||
public async Task Dialer_FollowsTheAgentListThePanelKnows_AndSavesItForTheNextStart()
|
||
{
|
||
await using AgentHarness first = await AgentHarness.StartAsync(_backend);
|
||
await using DialerHarness dialer = await DialerHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(dialer.App.Dialer.IsConnected(first.Id)));
|
||
|
||
await using AgentHarness second = await AgentHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(dialer.App.Dialer.IsConnected(second.Id)), TimeSpan.FromSeconds(30), "a new Agent should arrive through dial_targets state sync: " + string.Join(";", dialer.App.Dialer.Directory.Targets.Select(a => $"{a.NodeId}@{a.Host}:{a.TcpPort}")) + " v" + dialer.App.Dialer.Directory.Version + " want " + second.Id);
|
||
Assert.Contains(dialer.App.Dialer.Directory.Targets, a => a.NodeId == second.Id);
|
||
Assert.True(File.Exists(Path.Combine(dialer.Directory, "dial_targets.json")));
|
||
|
||
// 停用一个 Agent:Dialer 断开它
|
||
using HttpResponseMessage disabled = await _backend.Admin.PostAsync($"/api/admin/agents/{second.Id}/disable", BackendFixture.Json(new JsonObject()));
|
||
Assert.True(disabled.IsSuccessStatusCode, await disabled.Content.ReadAsStringAsync());
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(!dialer.App.Dialer.IsConnected(second.Id)), TimeSpan.FromSeconds(30));
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Hy2Ladder_WithTheRealDialer_Succeeds_AndEveryFragmentIsStored()
|
||
{
|
||
await using AgentHarness agent = await AgentHarness.StartAsync(_backend);
|
||
await agent.OverrideLineTasksAsync();
|
||
await using DialerHarness dialer = await DialerHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(async () => (await AgentViewAsync(agent))["dialer_connected"]!.GetValue<bool>());
|
||
|
||
using HttpResponseMessage accepted = await _backend.Admin.PostAsync($"/api/admin/lines/{agent.Id}~{dialer.DialerId}/tests", BackendFixture.Json(new JsonObject { ["plan"] = "hy2_ladder", ["params"] = new JsonObject { ["size"] = 1000, ["duration_s"] = 5 } }));
|
||
Assert.True(accepted.StatusCode == System.Net.HttpStatusCode.Accepted, await accepted.Content.ReadAsStringAsync());
|
||
string testId = JsonNode.Parse(await accepted.Content.ReadAsStringAsync())!["id"]!.GetValue<string>();
|
||
|
||
JsonNode test = null!;
|
||
await BackendFixture.EventuallyAsync(
|
||
async () =>
|
||
{
|
||
test = await _backend.GetJsonAsync(_backend.Admin, "/api/public/tests/" + testId);
|
||
return test["status"]!.GetValue<string>() is "succeeded" or "failed" or "aborted";
|
||
},
|
||
TimeSpan.FromSeconds(90));
|
||
Assert.True(test["status"]!.GetValue<string>() == "succeeded", test.ToJsonString());
|
||
|
||
await BackendFixture.EventuallyAsync(async () => await ChunkCountAsync(agent, "quic_stream") >= 5, because: "the 100 Mbit/s phase (5 s of 1000 B packets, over 50000 records) is split into two fragments");
|
||
Assert.Equal(0, agent.App.Counters.FragmentsDiscarded);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task RealitySession_WithTheRealDialer_RunsTcpBulkAndStoresBytesAndKernelRows()
|
||
{
|
||
await using AgentHarness agent = await AgentHarness.StartAsync(_backend);
|
||
await agent.OverrideLineTasksAsync();
|
||
await using DialerHarness dialer = await DialerHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(async () => (await AgentViewAsync(agent))["dialer_connected"]!.GetValue<bool>());
|
||
|
||
using HttpResponseMessage accepted = await _backend.Admin.PostAsync($"/api/admin/lines/{agent.Id}~{dialer.DialerId}/tests", BackendFixture.Json(new JsonObject { ["plan"] = "reality_session" }));
|
||
Assert.True(accepted.StatusCode == System.Net.HttpStatusCode.Accepted, await accepted.Content.ReadAsStringAsync());
|
||
string testId = JsonNode.Parse(await accepted.Content.ReadAsStringAsync())!["id"]!.GetValue<string>();
|
||
|
||
JsonNode test = null!;
|
||
await BackendFixture.EventuallyAsync(
|
||
async () =>
|
||
{
|
||
test = await _backend.GetJsonAsync(_backend.Admin, "/api/public/tests/" + testId);
|
||
return test["status"]!.GetValue<string>() is "succeeded" or "failed" or "aborted";
|
||
},
|
||
TimeSpan.FromSeconds(150));
|
||
Assert.True(test["status"]!.GetValue<string>() == "succeeded", test.ToJsonString());
|
||
await BackendFixture.EventuallyAsync(async () => await ChunkCountAsync(agent, "tcp_transfer") >= 1, because: "the bulk phase stores its fragments");
|
||
Assert.Equal(0, agent.App.Counters.FragmentsDiscarded);
|
||
}
|
||
}
|