110 lines
6.7 KiB
C#
110 lines
6.7 KiB
C#
using System.Text.Json.Nodes;
|
||
|
||
namespace Monitor.Backend.Tests;
|
||
|
||
/// <summary>真实的后端 + 真实的 Agent + 真实的 Probe(都在进程内,回环网络)。</summary>
|
||
[Collection("probe-e2e")]
|
||
public sealed class ProbeEndToEndTests
|
||
{
|
||
private readonly BackendFixture _backend;
|
||
|
||
public ProbeEndToEndTests(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 Probe_ConnectsThroughTheBootstrapList_RunsTheLineTasks_AndTheBackendStoresMergedChunks()
|
||
{
|
||
await using AgentHarness agent = await AgentHarness.StartAsync(_backend);
|
||
await agent.OverrideLineTasksAsync(rateHz: 30);
|
||
await using ProbeHarness probe = await ProbeHarness.StartAsync(_backend);
|
||
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(probe.App.IsConnected(agent.Id)), because: "the probe should connect to the bootstrap agent");
|
||
await BackendFixture.EventuallyAsync(async () => (await AgentViewAsync(agent))["probe_connected"]!.GetValue<bool>());
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(probe.App.Connection(agent.Id)!.RoleCount == 2), because: "udp_echo and tcp_echo");
|
||
|
||
// 每 10 秒切一块,回显类再等 2 秒宽限
|
||
await BackendFixture.EventuallyAsync(async () => await ChunkCountAsync(agent, "udp_echo") >= 1 && await ChunkCountAsync(agent, "tcp_echo") >= 1, TimeSpan.FromSeconds(40));
|
||
Assert.True(agent.App.Counters.Fragments >= 2);
|
||
Assert.Equal(0, agent.App.Counters.FragmentsDiscarded);
|
||
|
||
// 时钟偏移已经估计出来:进程内两端共用同一个单调时钟,所以偏移应当接近 0(真实线路上是两台机器各自的时钟原点之差)
|
||
Assert.InRange(probe.App.Connection(agent.Id)!.ClockOffsetUs, -20_000, 20_000);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Probe_FollowsTheAgentListThePanelKnows_AndSavesItForTheNextStart()
|
||
{
|
||
await using AgentHarness first = await AgentHarness.StartAsync(_backend);
|
||
await using ProbeHarness probe = await ProbeHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(probe.App.IsConnected(first.Id)));
|
||
|
||
await using AgentHarness second = await AgentHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(() => Task.FromResult(probe.App.IsConnected(second.Id)), TimeSpan.FromSeconds(30), "a new agent should be picked up through agents_changed: " + string.Join(";", probe.App.Directory.Agents.Select(a => $"{a.AgentId}@{a.Host}:{a.TcpPort}")) + " v" + probe.App.Directory.Version + " want " + second.Id);
|
||
Assert.Contains(probe.App.Directory.Agents, a => a.AgentId == second.Id);
|
||
Assert.True(File.Exists(Path.Combine(probe.Directory, "agents.json")));
|
||
|
||
// 停用一个 Agent:Probe 断开它
|
||
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(!probe.App.IsConnected(second.Id)), TimeSpan.FromSeconds(30));
|
||
}
|
||
|
||
[Fact]
|
||
public async Task Hy2Ladder_WithTheRealProbe_Succeeds_AndEveryFragmentIsStored()
|
||
{
|
||
await using AgentHarness agent = await AgentHarness.StartAsync(_backend);
|
||
await agent.OverrideLineTasksAsync();
|
||
await using ProbeHarness probe = await ProbeHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(async () => (await AgentViewAsync(agent))["probe_connected"]!.GetValue<bool>());
|
||
|
||
using HttpResponseMessage accepted = await _backend.Admin.PostAsync($"/api/admin/lines/{agent.Id}~{probe.ProbeId}/tests", BackendFixture.Json(new JsonObject { ["plan"] = "hy2_ladder", ["params"] = new JsonObject { ["size"] = 1000 } }));
|
||
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, "udp_train") >= 5, because: "50 and 100 Mbit/s phases are split into several fragments");
|
||
Assert.Equal(0, agent.App.Counters.FragmentsDiscarded);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task RealitySession_WithTheRealProbe_RunsTcpBulkAndStoresBytesAndKernelRows()
|
||
{
|
||
await using AgentHarness agent = await AgentHarness.StartAsync(_backend);
|
||
await agent.OverrideLineTasksAsync();
|
||
await using ProbeHarness probe = await ProbeHarness.StartAsync(_backend);
|
||
await BackendFixture.EventuallyAsync(async () => (await AgentViewAsync(agent))["probe_connected"]!.GetValue<bool>());
|
||
|
||
using HttpResponseMessage accepted = await _backend.Admin.PostAsync($"/api/admin/lines/{agent.Id}~{probe.ProbeId}/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_bulk") >= 1, because: "the bulk phase stores its fragments");
|
||
Assert.Equal(0, agent.App.Counters.FragmentsDiscarded);
|
||
}
|
||
}
|