using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.DependencyInjection; using MongoDB.Entities; using PrivaPub.Federation.Inbox; using PrivaPub.Infrastructure.Jobs; using PrivaPub.Models; using PrivaPub.Models.Jobs; using PrivaPub.Models.Post; using PrivaPub.Tests.Support; using PrivaPub.Tests.Support.Host; using System.Net; using System.Text.Json.Nodes; namespace PrivaPub.Tests.Http { [Trait("Category", "Integration")] public sealed class HostBootTests : IAsyncLifetime { PrivaPubHost _host; public async ValueTask InitializeAsync() { Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip); _host = await PrivaPubHost.Shared(); } public ValueTask DisposeAsync() => ValueTask.CompletedTask; [Fact] public async Task The_host_shares_the_fixture_database() { Assert.True(await DB.Default.Find().ExecuteAnyAsync(TestContext.Current.CancellationToken)); using var client = _host.Client(); Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/build.json", TestContext.Current.CancellationToken)).StatusCode); } [Fact] public async Task The_harness_handles_the_same_activities_as_the_server() { await using var harness = await Harness.Start(); var registered = _host.Services.GetServices().Select(h => h.GetType()).OrderBy(t => t.Name); var harnessed = harness.Handlers.Select(h => h.GetType()).OrderBy(t => t.Name); Assert.Equal(registered, harnessed); } [Fact] public void Every_job_kind_has_exactly_one_handler() { var handlers = _host.Services.GetServices().GroupBy(h => h.Kind).ToDictionary(g => g.Key, g => g.Count()); foreach (var kind in Enum.GetValues()) Assert.True(handlers.GetValueOrDefault(kind) == 1, $"{kind} has {handlers.GetValueOrDefault(kind)} handlers"); } [Fact] public void Every_controller_and_page_model_can_be_made() { using var scope = _host.Services.CreateScope(); var controllers = _host.Get().ActionDescriptors.Items .OfType() .Select(d => d.ControllerTypeInfo.AsType()) .Distinct() .ToList(); var pages = typeof(Program).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(PageModel)) && !t.IsAbstract).ToList(); Assert.NotEmpty(controllers); Assert.NotEmpty(pages); foreach (var type in controllers.Concat(pages)) Assert.NotNull(ActivatorUtilities.CreateInstance(scope.ServiceProvider, type)); } [Fact] public void The_service_graph_is_valid() { var services = new ServiceCollection(); foreach (var descriptor in _host.Registered) ((ICollection)services).Add(descriptor); using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true }); } [Fact] public async Task Swagger_is_not_served_outside_development() { using var client = _host.Client(); Assert.Equal(HttpStatusCode.NotFound, (await client.GetAsync("/swagger/index.html", TestContext.Current.CancellationToken)).StatusCode); } [Fact] public async Task A_persona_gets_a_mastodon_token_through_the_real_code_flow() { var root = await _host.SignUp(); var persona = await _host.Persona(root, "boot"); var token = await _host.MastodonToken(persona); using var client = _host.As(token); var account = await client.GetStringAsync("/api/v1/accounts/verify_credentials", TestContext.Current.CancellationToken); Assert.Contains($"\"id\":\"{persona.Id}\"", account); Assert.DoesNotContain(root.Id, account); Assert.DoesNotContain(root.UserName, account); } [Fact] public async Task A_signed_delivery_through_the_real_route_is_queued_and_stored() { var token = TestContext.Current.CancellationToken; await using var peer = await Peer.Start(); var persona = await _host.Persona(await _host.SignUp(), "mouth"); var bob = new RemoteActor(peer, "bob"); var noteId = $"{peer.A}/notes/{Guid.NewGuid():N}"; var activity = new JsonObject { ["id"] = noteId + "/activity", ["type"] = "Create", ["actor"] = bob.Id, ["to"] = new JsonArray($"{PrivaPubHost.Base}/peasants/{persona.UserName}"), ["object"] = new JsonObject { ["id"] = noteId, ["type"] = "Note", ["attributedTo"] = bob.Id, ["to"] = new JsonArray($"{PrivaPubHost.Base}/peasants/{persona.UserName}"), ["content"] = "

hello through the front door

", ["published"] = DateTime.UtcNow.ToString("O") } }; using var client = _host.Client(); var response = await client.SendAsync(bob.SignedPost($"/peasants/{persona.UserName}/mouth", activity), token); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal(1, await _host.RunInbox(noteId + "/activity", token)); var stored = await DB.Default.Find().Match(p => p.ObjectURI == noteId).ExecuteFirstAsync(token); Assert.Equal(PostVisibility.Direct, stored.Visibility); Assert.Contains("front door", stored.ContentHtml); } } }