test(backend): parallelize isolated fixtures [CI] [SKIP TESTS]
This commit is contained in:
1 parent
77d8404f59
commit
238046d959
29 files changed
+84
-66
No files matched your search
@@ -8,8 +8,7 @@ using MoShiro.TestSupport;
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>public_host 和 ipv6_host 自动识别:Agent 上报公网地址,手填的优先,可以撤销手填。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class AddressTests
|
||||
public sealed class AddressTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private const string V4 = "8.8.4.4";
|
||||
private const string V6 = "2606:4700:4700::1111";
|
||||
|
||||
@@ -6,8 +6,7 @@ using MoShiro.TestSupport;
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>真实的后端 + 真实的 Agent(进程内)+ 假 Probe:覆盖 Agent 与 Backend 的链路、Probe 与 Agent 的点对点会话和数据面。</summary>
|
||||
[Collection("backend")]
|
||||
public sealed class AgentEndToEndTests
|
||||
public sealed class AgentEndToEndTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ using System.Text.Json.Nodes;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class ApiBasicsTests
|
||||
public sealed class ApiBasicsTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
@@ -226,9 +225,3 @@ public sealed class ApiBasicsTests
|
||||
Assert.Contains("moshiro_analysis_queue_length", text);
|
||||
}
|
||||
}
|
||||
|
||||
[CollectionDefinition("backend")]
|
||||
public sealed class BackendCollection : ICollectionFixture<BackendFixture>;
|
||||
|
||||
[CollectionDefinition("probe-e2e")]
|
||||
public sealed class ProbeEndToEndCollection : ICollectionFixture<BackendFixture>;
|
||||
@@ -4,8 +4,7 @@ using System.Text.Json.Nodes;
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>审计日志:写操作、登录成败都会记一行;谁做的(用户名或 key:令牌名)、结果、细节。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class AuditTests
|
||||
public sealed class AuditTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ using MoShiro.Backend.Application;
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>能力模型(docs/security/access.md 第 3 节):每个管理端点声明能力;密钥只能拿到自己拥有能力的子集;写蕴含读。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class CapabilityTests
|
||||
public sealed class CapabilityTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class EnrollmentAndTaskSetTests
|
||||
public sealed class EnrollmentAndTaskSetTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -115,8 +115,7 @@ public sealed class HostSamplerTests
|
||||
}
|
||||
}
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class HostStatusTests
|
||||
public sealed class HostStatusTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class IngestAndQueryTests
|
||||
public sealed class IngestAndQueryTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private const long Minute = 60_000;
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// 一行安装:Agent 和 Probe 共用同一份<b>静态</b>脚本(所有人拿到的字节都一样,所以哈希固定、可以先核对再执行),
|
||||
/// 面板地址、角色、一次性代码都走命令里的环境变量;脚本再向面板取 CI 签名的发布清单来验证镜像。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class InstallTests
|
||||
public sealed class InstallTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@ namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>真实双栈回环:同一身份的两种会话、原始块、查询与测试结果必须各自独立。</summary>
|
||||
// 慢的端到端测试(真实时间的阶梯、要等测量块出来):CI 里单独一批跑(Category=Slow),不拦着出镜像,见 ci/verify-backend.sh
|
||||
[Collection("probe-e2e")]
|
||||
[Trait("Category", "Slow")]
|
||||
public sealed class Ipv6Tests
|
||||
public sealed class Ipv6Tests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class LinkTests
|
||||
public sealed class LinkTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class LiveTests
|
||||
public sealed class LiveTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -174,8 +174,7 @@ public sealed partial class NodeInfoTests
|
||||
/// 访客能拿到的东西里不能有 IP 等敏感信息:公开接口的整个响应里不出现 IP 形状的字符串,也没有 ip、city、asn、经纬度这些字段。
|
||||
/// Agent 和 Probe 这里是真的连上了(Probe 的来源地址是 127.0.0.1),所以如果哪里把地址带出来了,这里一定能发现。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed partial class NodePrivacyTests
|
||||
public sealed partial class NodePrivacyTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private static readonly string[] s_forbiddenKeys = ["ip", "city", "asn", "latitude", "longitude", "cidr", "remote", "public_host", "geo_host", "host_name", "hostname"];
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class OnDemandTestsTests
|
||||
public sealed class OnDemandTestsTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// 对照 docs/api/api.md 第 7 节 检查运行时生成的 OpenAPI 文档:operationId、路径风格、错误响应格式、字段命名。
|
||||
/// 新增端点违反规范时,这里会直接失败(等价于文档里的 <c>eng/check-openapi</c>)。
|
||||
/// </summary>
|
||||
[Collection("backend")]
|
||||
public sealed partial class OpenApiConformanceTests
|
||||
public sealed partial class OpenApiConformanceTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("backend")]
|
||||
public sealed class OperationsAndSecurityTests
|
||||
public sealed class OperationsAndSecurityTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private const long Minute = 60_000;
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ public sealed class OwnerPolicyTests
|
||||
}
|
||||
}
|
||||
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class OwnerEndToEndTests : IDisposable
|
||||
public sealed class OwnerEndToEndTests : IDisposable, IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
private readonly BackendOptions _options;
|
||||
|
||||
@@ -86,8 +86,7 @@ public sealed class SoftwarePasskey : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>passkey 登录:注册、登录、拒绝各种错误的断言、只能管理自己的 passkey。设计见 docs/security/access.md 第 4 节。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class PasskeyTests
|
||||
public sealed class PasskeyTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@ namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>真实的后端 + 真实的 Agent + 真实的 Probe(都在进程内,回环网络)。</summary>
|
||||
// 慢的端到端测试(真实时间的阶梯、要等测量块出来):CI 里单独一批跑(Category=Slow),不拦着出镜像,见 ci/verify-backend.sh
|
||||
[Collection("probe-e2e")]
|
||||
[Trait("Category", "Slow")]
|
||||
public sealed class ProbeEndToEndTests
|
||||
public sealed class ProbeEndToEndTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// 二进制通道都有对应的收件箱,文档里写了每一个方法。新增方法绕不过声明,声明了没人处理也会被发现。
|
||||
/// 注册处理函数时路由器本身还会核对方向、类型和角色,不符就在启动时失败。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class ProtocolDeclarationTests
|
||||
public sealed class ProtocolDeclarationTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private static readonly MethodTable s_table = MoShiroMethodTable.Table;
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// 可信资产来源:官方发布密钥始终信任;部署的人可以另外导入自己的发布公钥(默认没有),用自己的私钥给自己构建的清单签名。
|
||||
/// 面板只收公钥,拒绝私钥;删除之后只有它签过的清单不再被接受,已登记的也丢掉。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class ReleaseKeyTests
|
||||
public sealed class ReleaseKeyTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private static long s_seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds() * 1000 + 500_000;
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// CI 登记清单不需要令牌,但清单必须带 CI 发布密钥的有效签名:令牌存在面板的数据库里,清库、重装或还没有 passkey 的时候就拿不到,
|
||||
/// 而清单本来就是签名的,面板直接验签更简单,也没有长期令牌可偷。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class ReleaseOfferTests
|
||||
public sealed class ReleaseOfferTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
// openssl 生成的测试密钥对和签名(`openssl dgst -sha256 -sign`,DER):证明验签接受 CI 脚本实际产出的格式
|
||||
private const string TestKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQwlE2KjYWszeaTsng6CfPxhOqeIV\n+7dm940f7XfHJr9A6/OTiZ2YnhFWWipZ4Z4poPtb6hMW04tIARO8+Km34w==\n-----END PUBLIC KEY-----";
|
||||
|
||||
@@ -7,8 +7,7 @@ using MoShiro.TestSupport;
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
/// <summary>定时重启:默认开、北京时间 04:30;节点连上时补发,管理端改了设置马上群发,关掉就发空。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class ScheduleTests
|
||||
public sealed class ScheduleTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -132,8 +132,7 @@ public sealed class StaticSiteTests
|
||||
}
|
||||
|
||||
/// <summary>HTTP 层:后端自己的路径不会被前端的回退吞掉;响应头对。</summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class StaticSiteHttpTests
|
||||
public sealed class StaticSiteHttpTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[assembly: Xunit.v3.Parallelization(MaxThreads = 16, Mode = Xunit.Sdk.ParallelMode.Collections)]
|
||||
@@ -11,11 +11,26 @@ namespace MoShiro.Backend.Tests;
|
||||
public sealed class TestReleaseKey : IReleaseKey
|
||||
{
|
||||
private static readonly ECDsa s_key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
||||
private static readonly object s_gate = new();
|
||||
|
||||
public static TestReleaseKey Instance { get; } = new();
|
||||
|
||||
public string Pem => s_key.ExportSubjectPublicKeyInfoPem();
|
||||
public string Pem
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (s_gate)
|
||||
{
|
||||
return s_key.ExportSubjectPublicKeyInfoPem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string Sign(string manifest) =>
|
||||
Convert.ToBase64String(s_key.SignData(Encoding.UTF8.GetBytes(manifest), HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence));
|
||||
public static string Sign(string manifest)
|
||||
{
|
||||
lock (s_gate)
|
||||
{
|
||||
return Convert.ToBase64String(s_key.SignData(Encoding.UTF8.GetBytes(manifest), HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@ namespace MoShiro.Backend.Tests;
|
||||
/// 信任根:操作员(passkey)签操作员集合;安装命令里带着创世指纹和属主(都是公开值),节点注册时验证面板的集合并固定在本地。
|
||||
/// 设计见 docs/security/access.md 第 8 节。
|
||||
/// </summary>
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class TrustTests
|
||||
public sealed class TrustTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private readonly BackendFixture _backend;
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ using MoShiro.TestSupport;
|
||||
|
||||
namespace MoShiro.Backend.Tests;
|
||||
|
||||
[Collection("probe-e2e")]
|
||||
public sealed class UpdateTests
|
||||
public sealed class UpdateTests : IClassFixture<BackendFixture>
|
||||
{
|
||||
private const string Digest = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
|
||||
|
||||
+39
-5
@@ -14,8 +14,8 @@ set -euo pipefail
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
mode="${1:-fast}"
|
||||
case "$mode" in
|
||||
fast) filter=(--filter-not-trait "Category=Slow") ;;
|
||||
slow) filter=(--filter-trait "Category=Slow") ;;
|
||||
fast) filter=(--filter-not-trait "Category=Slow"); test_timeout=300 ;;
|
||||
slow) filter=(--filter-trait "Category=Slow"); test_timeout=900 ;;
|
||||
*) echo "usage: verify-backend.sh [fast|slow]" >&2; exit 2 ;;
|
||||
esac
|
||||
cache="${MOSHIRO_CI_CACHE:-/home/justme/woodpecker/cache/backend-tests}"
|
||||
@@ -70,9 +70,43 @@ quic_runtime="$cache/quic-runtime"
|
||||
bash "$root/ci/fetch-quic-test-runtime.sh" "$quic_runtime"
|
||||
export LD_LIBRARY_PATH="$quic_runtime/runtime${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
status=0
|
||||
# 别的程序集里没有匹配的测试会因为「零个测试」返回 8,忽略它
|
||||
(cd "$src/backend" && dotnet test --solution MoShiro.slnx -c Release --no-build "${filter[@]}" --ignore-exit-code 8) 2>&1 | tee "$log" || status=${PIPESTATUS[0]}
|
||||
# Each test module can spawn its own testhost. Run the whole command in a new
|
||||
# session and time out its process group, so orphaned testhosts cannot keep the
|
||||
# Woodpecker step and the shared cache lock alive.
|
||||
if ! command -v setsid >/dev/null 2>&1; then
|
||||
echo "setsid is required to enforce the backend test timeout" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
run_with_timeout() {
|
||||
local seconds="$1" marker="$cache/test-timeout.marker" test_pid watcher_pid result=0
|
||||
shift
|
||||
rm -f "$marker"
|
||||
(
|
||||
cd "$src/backend"
|
||||
setsid "$@" &
|
||||
test_pid=$!
|
||||
setsid bash -c 'sleep "$1"; : > "$2"; kill -TERM -- "-$3" 2>/dev/null || true' _ "$seconds" "$marker" "$test_pid" >/dev/null 2>&1 &
|
||||
watcher_pid=$!
|
||||
wait "$test_pid" || result=$?
|
||||
if [[ -f "$marker" ]]; then
|
||||
kill -KILL -- "-$test_pid" 2>/dev/null || true
|
||||
result=124
|
||||
fi
|
||||
kill -TERM -- "-$watcher_pid" 2>/dev/null || true
|
||||
wait "$watcher_pid" 2>/dev/null || true
|
||||
rm -f "$marker"
|
||||
return "$result"
|
||||
)
|
||||
}
|
||||
|
||||
# Ignore exit 8 from other assemblies with no matching tests; both suites have a hard wall-clock cap.
|
||||
run_with_timeout "$test_timeout" dotnet test --solution MoShiro.slnx -c Release --no-build --max-parallel-test-modules 16 "${filter[@]}" --ignore-exit-code 8 2>&1 | tee "$log" || status=${PIPESTATUS[0]}
|
||||
[[ "$status" == 0 ]] && exit 0
|
||||
if [[ "$status" == 124 || "$status" == 137 ]]; then
|
||||
echo "backend tests exceeded the ${test_timeout}s timeout" >&2
|
||||
exit "$status"
|
||||
fi
|
||||
|
||||
# 个别端到端测试会偶发失败(比如 Hy2 阶梯测试里探针掉线):失败的测试单独重跑(最多两次),重跑通过就当通过(日志里留着第一次的失败);
|
||||
# 失败太多说明是真的坏了,不重跑
|
||||
@@ -87,7 +121,7 @@ for name in "${failed[@]}"; do
|
||||
for attempt in 1 2; do
|
||||
echo "retrying $name (attempt $attempt of 2)"
|
||||
# 过滤要带通配符;别的程序集里没有匹配的测试,会因为「零个测试」返回 8,忽略它
|
||||
if (cd "$src/backend" && dotnet test --solution MoShiro.slnx -c Release --no-build --filter-method "*$name*" --ignore-exit-code 8); then
|
||||
if run_with_timeout 90 dotnet test --solution MoShiro.slnx -c Release --no-build --max-parallel-test-modules 16 --filter-method "*$name*" --ignore-exit-code 8; then
|
||||
passed=true
|
||||
break
|
||||
fi
|
||||
|
||||
Reference in new issue
Block a user