Files
SocialPub/PrivaPub.Tests/Http/ServerSetupTests.cs
T

156 lines
6.1 KiB
C#
Raw Permalink Normal View History

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using MongoDB.Entities;
using PrivaPub.ClientModels;
using PrivaPub.Infrastructure;
using PrivaPub.Infrastructure.Cli;
using PrivaPub.Models;
using PrivaPub.Tests.Support;
using PrivaPub.Tests.Support.Host;
using System.Net;
using System.Net.Http.Headers;
namespace PrivaPub.Tests.Http
{
// Setup is server-wide, so each test opens it again by removing the seal, and seals it once done.
[Xunit.Collection(nameof(Exclusive))]
[Trait("Category", "Integration")]
public sealed class ServerSetupTests : IAsyncLifetime
{
PrivaPubHost _host;
WebApplicationFactory<Program> _closed;
public async ValueTask InitializeAsync()
{
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
_host = await PrivaPubHost.Shared();
// production's mode: sign-up closed, invitations only
_closed = _host.WithWebHostBuilder(builder => builder.UseSetting("Registrations:Mode", nameof(RegistrationMode.Invitations)));
await DB.Default.DeleteAsync<ServerSetup>(ServerSetup.Name);
}
public async ValueTask DisposeAsync()
{
if (_closed == default)
return;
await ServerSetup.Seal("tests", default);
await _closed.DisposeAsync();
}
HttpClient Client(string jwt = default)
{
var client = _closed.CreateClient(new WebApplicationFactoryClientOptions { BaseAddress = new Uri(PrivaPubHost.Base + "/"), AllowAutoRedirect = false });
client.DefaultRequestHeaders.Remove(PrivaPubHost.ClientHeader);
client.DefaultRequestHeaders.Add(PrivaPubHost.ClientHeader, $"10.{Random.Shared.Next(256)}.{Random.Shared.Next(256)}.{Random.Shared.Next(1, 255)}");
if (jwt != default)
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
return client;
}
static string Name(string prefix) => $"{prefix}{Guid.NewGuid():N}"[..20];
static object Form(string userName, string avatarUserName, string avatarName = default) =>
new { userName, password = Accounts.Password, avatarUserName, avatarName };
async Task<bool> Pending()
{
using var client = Client();
return (await client.Get("/clientapi/user/setup")).Ok().Body.Flag("isPending");
}
static async Task<string> Message(HttpResponseMessage response)
{
var body = await response.JsonBody();
return body["errorMessage"]?.GetValue<string>() ?? body["title"]?.GetValue<string>();
}
[Fact]
public async Task Setup_makes_the_first_login_the_administrator_with_its_persona_and_closes()
{
var token = TestContext.Current.CancellationToken;
var userName = Name("first");
var avatarUserName = Name("founder");
Assert.True(await Pending());
using var client = Client();
var response = await client.PostJson("/clientapi/user/setup", Form(userName, avatarUserName, "The Founder"));
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var jwt = await response.JsonBody();
var policies = jwt["policies"]!.AsArray().Select(p => p!.GetValue<string>()).Order();
Assert.Equal(new[] { Policies.IsAdmin, Policies.IsModerator, Policies.IsUser }.Order(), policies);
using var admin = Client(jwt["token"]!.GetValue<string>());
Assert.Equal(HttpStatusCode.OK, (await admin.GetAsync("/clientapi/admin/domainblocks/list", token)).StatusCode);
var persona = Assert.Single(await (await admin.GetAsync("/clientapi/avatar/private/list", token)).JsonItems())!;
Assert.Equal(avatarUserName, persona["userName"]!.GetValue<string>());
Assert.Equal("The Founder", persona["name"]!.GetValue<string>());
var seal = await DB.Default.Find<ServerSetup>().OneAsync(ServerSetup.Name, token);
Assert.Equal(jwt["userId"]!.GetValue<string>(), seal.AdminId);
Assert.False(await Pending());
var again = await client.PostJson("/clientapi/user/setup", Form(Name("second"), Name("latecomer")));
Assert.Equal(HttpStatusCode.Forbidden, again.StatusCode);
Assert.Contains("already set up", await Message(again));
}
[Fact]
public async Task Two_setups_at_once_make_one_administrator()
{
using var first = Client();
using var second = Client();
var answers = await Task.WhenAll(
first.PostJson("/clientapi/user/setup", Form(Name("racea"), Name("racera"))),
second.PostJson("/clientapi/user/setup", Form(Name("raceb"), Name("racerb"))));
Assert.Equal(new[] { HttpStatusCode.OK, HttpStatusCode.Forbidden }, answers.Select(a => a.StatusCode).Order());
Assert.False(await Pending());
}
[Fact]
public async Task A_setup_that_fails_leaves_the_server_to_set_up()
{
var taken = await _host.SignUp("taken");
var userName = Name("same");
using var client = Client();
var samePersona = await client.PostJson("/clientapi/user/setup", Form(userName, userName));
var noPersona = await client.PostJson("/clientapi/user/setup", new { userName = Name("bare"), password = Accounts.Password });
var takenLogin = await client.PostJson("/clientapi/user/setup", Form(taken.UserName, Name("takenp")));
Assert.Equal(HttpStatusCode.BadRequest, samePersona.StatusCode);
Assert.Contains("must differ", await Message(samePersona));
Assert.Equal(HttpStatusCode.BadRequest, noPersona.StatusCode);
Assert.Equal(HttpStatusCode.BadRequest, takenLogin.StatusCode);
Assert.Contains("already taken", await Message(takenLogin));
Assert.True(await Pending());
}
[Fact]
public async Task While_setup_waits_sign_up_stays_closed()
{
using var client = Client();
var signUp = await client.PostJson("/clientapi/user/signup", new { userName = Name("outsider"), password = Accounts.Password });
Assert.Equal(HttpStatusCode.Forbidden, signUp.StatusCode);
Assert.True(await Pending());
Assert.False((await client.Get("/nodeinfo/2.1")).Ok().Body.Flag("openRegistrations"));
}
[Fact]
public async Task Promoting_an_administrator_with_the_cli_closes_setup()
{
var root = await _host.SignUp("promoted");
Assert.True(await Pending());
Assert.Equal(0, await AdminCommands.Run(new[] { "promote", root.UserName }, _host.Services));
Assert.False(await Pending());
Assert.Equal(root.Id, (await DB.Default.Find<ServerSetup>().OneAsync(ServerSetup.Name, TestContext.Current.CancellationToken)).AdminId);
}
}
}