test: share one operator and trust root across the trust tests; make the setup test cleanup best-effort [CI SKIP]

This commit is contained in:
justme committed 2026-10-08 15:30:12 +08:00
1 parent f22fdf8b68
commit 4a1e48db20
4 files changed
+91 -87

No files matched your search

@@ -20,9 +20,6 @@ namespace MoShiro.Backend.Tests;
[Collection("probe-e2e")]
public sealed class TrustTests
{
private static readonly ConditionalWeakTable<BackendFixture, Operator> s_operators = [];
private static readonly SemaphoreSlim s_gate = new(1, 1);
private readonly BackendFixture _backend;
public TrustTests(BackendFixture backend)
@@ -30,57 +27,11 @@ public sealed class TrustTests
_backend = backend;
}
/// <summary>一个已经建立了信任根的操作员:自己的用户、passkey、会话,以及创世指纹。</summary>
private sealed record Operator(HttpClient Session, SoftwarePasskey Passkey, string Genesis);
private Task<Operator> OperatorAsync() => SharedOperator.GetAsync(_backend);
private async Task<Operator> OperatorAsync()
{
await s_gate.WaitAsync();
try
{
if (s_operators.TryGetValue(_backend, out Operator? existing))
{
return existing;
}
private static string Base64UrlOf(string statement) => SharedOperator.Base64UrlOf(statement);
// 操作员:有 passkey、用 passkey 登录的会话
(HttpClient session, SoftwarePasskey passkey, _) = await _backend.CreateStrongSessionAsync("operator");
// 创世:用 passkey 对集合本身签名(节点据此验链)
JsonNode state = JsonNode.Parse(await session.GetStringAsync("/api/admin/trust"))!;
Assert.False(state["initialized"]!.GetValue<bool>());
string statement = state["proposal"]!["statement"]!.GetValue<string>();
Assert.Equal(Base64UrlOf(statement), state["proposal"]!["challenge"]!.GetValue<string>());
using HttpResponseMessage genesis = await session.PostAsync("/api/admin/trust", BackendFixture.Json(Sign(passkey, statement)));
Assert.True(genesis.StatusCode == HttpStatusCode.Created, await genesis.Content.ReadAsStringAsync());
string fingerprint = JsonNode.Parse(await genesis.Content.ReadAsStringAsync())!["genesis"]!.GetValue<string>();
Assert.Equal(OperatorTrust.Fingerprint(statement), fingerprint);
var made = new Operator(session, passkey, fingerprint);
s_operators.Add(_backend, made);
return made;
}
finally
{
s_gate.Release();
}
}
private static string Base64UrlOf(string statement) => Fleet.Core.Base64Url.Encode(SHA256.HashData(Encoding.UTF8.GetBytes(statement)));
/// <summary>passkey 对语句签名(挑战是语句的 SHA-256),拼成接口要的断言对象。</summary>
private static JsonObject Sign(SoftwarePasskey passkey, string statement)
{
JsonObject assertion = passkey.Assert(Base64UrlOf(statement));
return new JsonObject
{
["statement"] = statement,
["credential_id"] = assertion["credential_id"]!.DeepClone(),
["authenticator_data"] = assertion["authenticator_data"]!.DeepClone(),
["client_data_json"] = assertion["client_data_json"]!.DeepClone(),
["signature"] = assertion["signature"]!.DeepClone(),
};
}
private static JsonObject Sign(SoftwarePasskey passkey, string statement) => SharedOperator.Sign(passkey, statement);
/// <summary>创建一个加入码(普通 API key 就行,不需要 passkey),返回响应里的码和三条安装命令。</summary>
private async Task<(string Code, JsonNode Commands, HttpStatusCode Status)> CreateCodeAsync(string role = "probe", string[]? controllers = null)