using System.Net; using System.Text.Json.Nodes; namespace MoShiro.Backend.Tests; /// 一行安装:Agent 和 Probe 共用同一份脚本,角色、网关、镜像由一次性代码决定,节点名字默认取主机名。 [Collection("probe-e2e")] public sealed class InstallTests { private readonly BackendFixture _backend; public InstallTests(BackendFixture backend) { _backend = backend; } private async Task CreateAsync(string role) { using HttpResponseMessage response = await _backend.Admin.PostAsync("/api/admin/join-tokens", BackendFixture.Json(new JsonObject { ["role"] = role })); return JsonNode.Parse(await response.Content.ReadAsStringAsync())!; } [Theory] [InlineData("agent")] [InlineData("probe")] public async Task Commands_AreOneLiners_WithoutAPlaceholder_AndTheScriptIsFilledInForTheRole(string role) { JsonNode created = await CreateAsync(role); string code = created["plaintext"]!.GetValue(); JsonNode commands = created["install_commands"]!; string linux = commands["linux"]!.GetValue(); string macos = commands["macos"]!.GetValue(); string windows = commands["windows"]!.GetValue(); Assert.StartsWith("curl -fsSL '", linux, StringComparison.Ordinal); Assert.EndsWith("/install?code=" + code + "' | bash", linux, StringComparison.Ordinal); Assert.EndsWith("/install?code=" + code + "' | zsh", macos, StringComparison.Ordinal); Assert.StartsWith("irm '", windows, StringComparison.Ordinal); Assert.EndsWith("/install.ps1?code=" + code + "' | iex", windows, StringComparison.Ordinal); Assert.DoesNotContain("<", linux + macos + windows, StringComparison.Ordinal); // 没有要用户自己替换的占位符 // 脚本不需要登录(拿代码就是授权),不带缓存 using HttpResponseMessage sh = await _backend.Anonymous.GetAsync("/install?code=" + code); string script = await sh.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, sh.StatusCode); Assert.Equal("no-store", sh.Headers.CacheControl?.ToString()); Assert.Contains($"ROLE='{role}'", script, StringComparison.Ordinal); Assert.Contains($"CODE='{code}'", script, StringComparison.Ordinal); Assert.Contains($"/moshiro-{role}:latest'", script, StringComparison.Ordinal); Assert.Contains("SERVER='http", script, StringComparison.Ordinal); Assert.DoesNotContain("@@", script, StringComparison.Ordinal); using HttpResponseMessage ps = await _backend.Anonymous.GetAsync("/install.ps1?code=" + code); string powershell = await ps.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, ps.StatusCode); Assert.Contains($"$role = '{role}'", powershell, StringComparison.Ordinal); Assert.DoesNotContain("@@", powershell, StringComparison.Ordinal); } [Fact] public async Task Codes_RecordWhoCreatedThem() { JsonNode created = await CreateAsync("agent"); Assert.StartsWith("key:", created["token"]!["created_by"]!.GetValue(), StringComparison.Ordinal); // Admin 客户端是 API key } [Fact] public async Task Script_IsNotServedForUnknownRevokedOrUsedUpCodes() { foreach (string path in new[] { "/install", "/install?code=", "/install?code=jt_nope", "/install.ps1?code=jt_nope", "/install.sh?code=jt_nope" }) { using HttpResponseMessage response = await _backend.Anonymous.GetAsync(path); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } JsonNode revoked = await CreateAsync("agent"); using HttpResponseMessage delete = await _backend.Admin.DeleteAsync("/api/admin/join-tokens/" + revoked["token"]!["id"]!.GetValue()); Assert.True(delete.IsSuccessStatusCode); using HttpResponseMessage gone = await _backend.Anonymous.GetAsync("/install?code=" + revoked["plaintext"]!.GetValue()); Assert.Equal(HttpStatusCode.NotFound, gone.StatusCode); // 用完了(一次性令牌已经注册过一个节点) JsonNode used = await CreateAsync("probe"); string code = used["plaintext"]!.GetValue(); using HttpResponseMessage before = await _backend.Anonymous.GetAsync("/install?code=" + code); Assert.Equal(HttpStatusCode.OK, before.StatusCode); await using (await MoShiro.TestSupport.AgentSimulator.EnrollAsync(_backend.Anonymous, code, _backend.UniqueName("used"))) { } using HttpResponseMessage after = await _backend.Anonymous.GetAsync("/install?code=" + code); Assert.Equal(HttpStatusCode.NotFound, after.StatusCode); } }