using System.Net.Http.Json; using System.Text.Json.Nodes; using decePubClient.E2E.Support; using Microsoft.Playwright; namespace decePubClient.E2E.Actions { // Administration as the pasture's administrator (pastureroot, whom the scenarios make): a server blocked for everyone // and unblocked, the federation statistics, and the reports moderators handle; each change checked on /clientapi public sealed class AdministrationTests(Browser browser) { const string Root = "pastureroot"; const string Password = "Pasture-Pass-1!"; static readonly Pasture.Persona Admin = new("alice", "alice", Root, Password, null, null, null); static async Task AsAdmin(string path) { using var http = Pasture.Http(); var login = await http.PostAsJsonAsync($"{Pasture.Api}/clientapi/user/login", new { userName = Root, password = Password }); login.EnsureSuccessStatusCode(); var jwt = (await login.Content.ReadFromJsonAsync())!["token"]!.GetValue(); using var request = new HttpRequestMessage(HttpMethod.Get, Pasture.Api + path); request.Headers.Authorization = new("Bearer", jwt); using var response = await http.SendAsync(request); response.EnsureSuccessStatusCode(); return JsonNode.Parse(await response.Content.ReadAsStringAsync()); } static async Task Open(IPage page, string section) { await page.GotoAsync($"{Pasture.Site}/administration"); var header = page.Locator(".collapse-title", new() { HasText = section }).First; await header.ClickAsync(); return header.Locator("xpath=.."); } [Fact] public async Task A_server_is_blocked_for_everyone_and_unblocked() { const string server = "instance-block-by-e2e.example"; await using var session = await SignIn.As(browser, Admin); var page = session.Page; await Results.Cell("client.admin.server-block", "privapub", Root, async () => { var panel = await Open(page, "Blocked servers"); var blocks = panel.Locator("[data-server-blocks]"); await blocks.WaitForAsync(new() { Timeout = 30000 }); await blocks.Locator("[data-server-block-domain]").FillAsync(server); await blocks.GetByRole(AriaRole.Button, new() { Name = "Block server" }).ClickAsync(); var row = blocks.Locator($"[data-server-block='{server}']"); await Assertions.Expect(row).ToContainTextAsync("Suspended", new() { Timeout = 15000 }); Assert.Contains((await AsAdmin("/clientapi/admin/domainblocks/list"))!.AsArray(), b => b!["domain"]!.GetValue() == server); await row.GetByRole(AriaRole.Button, new() { Name = "Unblock" }).ClickAsync(); await Assertions.Expect(row).ToHaveCountAsync(0, new() { Timeout = 15000 }); Assert.DoesNotContain((await AsAdmin("/clientapi/admin/domainblocks/list"))!.AsArray(), b => b!["domain"]!.GetValue() == server); await session.AssertGuards(); }, page); } [Fact] public async Task The_federation_statistics_and_the_reports_are_shown() { await using var session = await SignIn.As(browser, Admin); var page = session.Page; await Results.Cell("client.admin.statistics", "privapub", Root, async () => { var panel = await Open(page, "Statistics"); await Assertions.Expect(panel.Locator("[data-statistics-overview]")).ToBeVisibleAsync(new() { Timeout = 30000 }); var expected = (await AsAdmin("/clientapi/admin/statistics/hosts?days=30&sort=traffic&page=1&limit=50"))!["hosts"]!.AsArray() .Select(h => h!["host"]!.GetValue()).Take(5).ToList(); Assert.NotEmpty(expected); foreach (var host in expected) await Assertions.Expect(panel.Locator($"[data-statistics-host='{host}']")).ToHaveCountAsync(1, new() { Timeout = 15000 }); var reports = await Open(page, "Reports"); await Assertions.Expect(reports.Locator("[data-reports]")).ToBeVisibleAsync(new() { Timeout = 30000 }); var open = (await AsAdmin("/clientapi/moderator/reports?resolved=false"))!.AsArray().Count; await Assertions.Expect(reports.Locator("[data-report-id]")).ToHaveCountAsync(open, new() { Timeout = 15000 }); await session.AssertGuards(); }, page); } } }