126 lines
5.9 KiB
C#
126 lines
5.9 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
using PrivaPub.Domain.Statistics;
|
||
|
|
using PrivaPub.Infrastructure.Statistics;
|
||
|
|
using PrivaPub.Models.Jobs;
|
||
|
|
using PrivaPub.Tests.Support;
|
||
|
|
using PrivaPub.Tests.Support.Host;
|
||
|
|
|
||
|
|
using System.Net;
|
||
|
|
using System.Text.Json.Nodes;
|
||
|
|
|
||
|
|
namespace PrivaPub.Tests.Statistics
|
||
|
|
{
|
||
|
|
public class StatisticsQueriesTests
|
||
|
|
{
|
||
|
|
[Fact]
|
||
|
|
public void Percentiles_come_from_the_latency_buckets()
|
||
|
|
{
|
||
|
|
var latency = new Dictionary<string, long> { ["out:le50ms"] = 50, ["out:le250ms"] = 45, ["out:le5000ms"] = 4, ["out:inf"] = 1, ["in:le50ms"] = 9 };
|
||
|
|
|
||
|
|
Assert.Equal(50, StatisticsQueries.Percentile(latency, "out", 0.5));
|
||
|
|
Assert.Equal(250, StatisticsQueries.Percentile(latency, "out", 0.95));
|
||
|
|
Assert.Equal(5000, StatisticsQueries.Percentile(latency, "out", 0.99));
|
||
|
|
Assert.Equal(30001, StatisticsQueries.Percentile(latency, "out", 1));
|
||
|
|
Assert.Null(StatisticsQueries.Percentile(latency, "http:actor", 0.5));
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void Outcomes_add_up_per_channel()
|
||
|
|
{
|
||
|
|
var counters = new Dictionary<string, long>
|
||
|
|
{
|
||
|
|
["out:Create:Note:ok"] = 3, ["out:Create:Note:retry:503"] = 2, ["out:Like:-:ok"] = 1, ["in:Create:Note:accepted:stored"] = 4, ["recv:Create:202:queued"] = 9
|
||
|
|
};
|
||
|
|
|
||
|
|
Assert.Equal(new Dictionary<string, long> { ["ok"] = 4, ["retry"] = 2 }, StatisticsQueries.Outcomes(counters, "out"));
|
||
|
|
Assert.Equal(new Dictionary<string, long> { ["accepted"] = 4 }, StatisticsQueries.Outcomes(counters, "in"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Trait("Category", "Integration")]
|
||
|
|
public sealed class AdminStatisticsTests : IAsyncLifetime
|
||
|
|
{
|
||
|
|
PrivaPubHost _host;
|
||
|
|
Peer _peer;
|
||
|
|
|
||
|
|
public async ValueTask InitializeAsync()
|
||
|
|
{
|
||
|
|
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
|
||
|
|
_host = await PrivaPubHost.Shared();
|
||
|
|
_peer = await Peer.Start();
|
||
|
|
}
|
||
|
|
|
||
|
|
public async ValueTask DisposeAsync()
|
||
|
|
{
|
||
|
|
if (_peer != default)
|
||
|
|
await _peer.DisposeAsync();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Only_the_admin_reads_the_statistics()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var plain = await _host.SignUp();
|
||
|
|
using var anonymous = _host.Client();
|
||
|
|
using var user = _host.As(plain.Jwt);
|
||
|
|
|
||
|
|
Assert.Equal(HttpStatusCode.Unauthorized, (await anonymous.GetAsync("/clientapi/admin/statistics/overview", token)).StatusCode);
|
||
|
|
Assert.Equal(HttpStatusCode.Forbidden, (await user.GetAsync("/clientapi/admin/statistics/overview", token)).StatusCode);
|
||
|
|
Assert.Equal(HttpStatusCode.Forbidden, (await user.PostAsync("/clientapi/admin/statistics/rollups/2001-01-01", default, token)).StatusCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task The_admin_sees_a_server_its_events_and_the_overview()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var admin = await _host.Admin();
|
||
|
|
var persona = await _host.Persona(await _host.SignUp(), "stats");
|
||
|
|
var bob = new RemoteActor(_peer, "bob");
|
||
|
|
var noteId = $"{_peer.A}/notes/{Guid.NewGuid():N}";
|
||
|
|
var to = new JsonArray($"{PrivaPubHost.Base}/peasants/{persona.UserName}");
|
||
|
|
using (var client = _host.Client())
|
||
|
|
Assert.Equal(HttpStatusCode.Accepted, (await client.SendAsync(bob.SignedPost($"/peasants/{persona.UserName}/mouth", new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = noteId + "/activity", ["type"] = "Create", ["actor"] = bob.Id, ["to"] = to.DeepClone(),
|
||
|
|
["object"] = new JsonObject { ["id"] = noteId, ["type"] = "Note", ["attributedTo"] = bob.Id, ["to"] = to.DeepClone(), ["content"] = "hi" }
|
||
|
|
}), token)).StatusCode);
|
||
|
|
await _host.RunInbox(noteId + "/activity", token);
|
||
|
|
await _host.Get<InteractionLedger>().Flush(token);
|
||
|
|
using var adminClient = _host.As(admin.Jwt);
|
||
|
|
|
||
|
|
var host = JsonNode.Parse(await adminClient.GetStringAsync("/clientapi/admin/statistics/hosts/127.0.0.1?days=1", token))!;
|
||
|
|
var overview = JsonNode.Parse(await adminClient.GetStringAsync("/clientapi/admin/statistics/overview?days=1", token))!;
|
||
|
|
var hosts = JsonNode.Parse(await adminClient.GetStringAsync("/clientapi/admin/statistics/hosts?days=1&sort=host", token))!;
|
||
|
|
var events = JsonNode.Parse(await adminClient.GetStringAsync("/clientapi/admin/statistics/events?host=127.0.0.1&channel=in&limit=5", token))!;
|
||
|
|
|
||
|
|
var today = host["days"]!.AsArray().Last()!;
|
||
|
|
Assert.True(today["live"]!.GetValue<bool>());
|
||
|
|
Assert.True(today["counters"]!["in:Create:Note:accepted:stored"]!.GetValue<long>() >= 1);
|
||
|
|
Assert.Contains("DB-IP", host["attribution"]!.GetValue<string>());
|
||
|
|
Assert.True(overview["channels"]!["in"]!.GetValue<long>() >= 1);
|
||
|
|
Assert.Contains(hosts["hosts"]!.AsArray(), h => h!["host"]!.GetValue<string>() == "127.0.0.1");
|
||
|
|
Assert.NotEmpty(events.AsArray());
|
||
|
|
Assert.DoesNotContain(persona.UserName, host.ToJsonString());
|
||
|
|
Assert.DoesNotContain(bob.Id, host.ToJsonString());
|
||
|
|
Assert.Equal(HttpStatusCode.NotFound, (await adminClient.GetAsync($"/clientapi/admin/statistics/hosts/never{Guid.NewGuid():N}.example", token)).StatusCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task The_admin_can_refold_a_past_day_and_describe_a_server_again()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var admin = await _host.Admin();
|
||
|
|
using var client = _host.As(admin.Jwt);
|
||
|
|
var describe = $"described{Guid.NewGuid():N}.example";
|
||
|
|
|
||
|
|
Assert.Equal(HttpStatusCode.BadRequest, (await client.PostAsync($"/clientapi/admin/statistics/rollups/{DateTime.UtcNow:yyyy-MM-dd}", default, token)).StatusCode);
|
||
|
|
Assert.Equal(HttpStatusCode.BadRequest, (await client.PostAsync("/clientapi/admin/statistics/rollups/yesterday", default, token)).StatusCode);
|
||
|
|
Assert.Equal(HttpStatusCode.Accepted, (await client.PostAsync("/clientapi/admin/statistics/rollups/2001-01-05", default, token)).StatusCode);
|
||
|
|
Assert.Equal(HttpStatusCode.Accepted, (await client.PostAsync($"/clientapi/admin/statistics/hosts/{describe}/describe", default, token)).StatusCode);
|
||
|
|
|
||
|
|
Assert.True(await DB.Default.Find<Job>().Match(j => j.Kind == JobKind.RollupDay && j.Payload == "2001-01-05" && j.DedupeKey.StartsWith("rollup|2001-01-05|rerun|")).ExecuteAnyAsync(token));
|
||
|
|
Assert.True(await DB.Default.Find<Job>().Match(j => j.Kind == JobKind.DescribeInstance && j.Payload == describe).ExecuteAnyAsync(token));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|