Add managed channel administration and moderation
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
using VoiceCat.Server.Data;
|
||||
using Voicecat.V1;
|
||||
using static VoiceCat.Tests.ServerTests;
|
||||
|
||||
namespace VoiceCat.Tests;
|
||||
|
||||
public class ChannelManagementTests
|
||||
{
|
||||
internal static async Task<Client> AdminAsync(ServerFixture fixture)
|
||||
{
|
||||
using (var store = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db")))
|
||||
await store.CreateAccountAsync("Admin", "secret", true);
|
||||
Client client = await fixture.ConnectAsync();
|
||||
client.Send(new() { ClientHello = new() { ProtoVersion = 2 } });
|
||||
await client.ReadUntilAsync(e => e.ServerHello is not null);
|
||||
client.Send(new() { AuthRequest = new() { Password = new() { Username = "Admin", Password = "secret" } } });
|
||||
Assert.True((await client.ReadUntilAsync(e => e.AuthResult is not null)).AuthResult.Ok);
|
||||
await client.ReadUntilAsync(e => e.ServerState is not null);
|
||||
return client;
|
||||
}
|
||||
|
||||
private static Channel Room(string name = "Protected") => new()
|
||||
{
|
||||
Name = name, MaxUsers = 1,
|
||||
Audio = new() { SampleRate = 48000, BitrateBps = 24000, FrameMs = 20, Complexity = 5, Fec = true }
|
||||
};
|
||||
|
||||
internal static async Task<bool> ResultAsync(Client client, Envelope request)
|
||||
{
|
||||
request.RequestId = 42;
|
||||
client.Send(request);
|
||||
Envelope result = await client.ReadUntilAsync(e => e.GenericResult is not null);
|
||||
Assert.Equal(42UL, result.RequestId);
|
||||
return result.GenericResult.Ok;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProtectedChannelCrudEnforcesPasswordCapacityAndMovesMembersToLobby()
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
await using var guest = await fixture.ConnectAsync();
|
||||
User user = await guest.LoginAsync("Guest");
|
||||
await using var admin = await AdminAsync(fixture);
|
||||
Assert.False(await ResultAsync(guest, new() { CreateChannel = new() { Channel = Room() } }));
|
||||
Assert.True(await ResultAsync(admin, new() { CreateChannel = new() { Channel = Room(), Password = "pāssword" } }));
|
||||
Channel room = (await guest.ReadUntilAsync(e => e.ChannelEvent?.Kind == ChannelEvent.Types.Kind.Created)).ChannelEvent.Channel;
|
||||
Assert.True(room.PasswordProtected);
|
||||
foreach (string password in new[] { "", "wrong", "pāssword" })
|
||||
{
|
||||
guest.Send(new() { RequestId = 7, JoinChannel = new() { ChannelId = room.Id, Password = password } });
|
||||
Envelope result = await guest.ReadUntilAsync(e => e.JoinChannelResult is not null);
|
||||
Assert.Equal(7UL, result.RequestId);
|
||||
Assert.Equal(password == "pāssword", result.JoinChannelResult.Ok);
|
||||
}
|
||||
admin.Send(new() { JoinChannel = new() { ChannelId = room.Id, Password = "pāssword" } });
|
||||
Assert.False((await admin.ReadUntilAsync(e => e.JoinChannelResult is not null)).JoinChannelResult.Ok);
|
||||
room.Name = "Renamed";
|
||||
Assert.False(await ResultAsync(guest, new() { EditChannel = new() { Channel = room } }));
|
||||
Assert.True(await ResultAsync(admin, new() { EditChannel = new() { Channel = room } }));
|
||||
Assert.Equal("Renamed", (await guest.ReadUntilAsync(e => e.ChannelEvent is not null)).ChannelEvent.Channel.Name);
|
||||
using (var store = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db")))
|
||||
{
|
||||
Assert.Equal("Renamed", store.LoadChannels().Single(c => c.Id == room.Id).Name);
|
||||
Assert.True(store.CheckChannelPassword(room.Id, "pāssword"));
|
||||
Assert.False(store.CheckChannelPassword(room.Id, "wrong"));
|
||||
}
|
||||
Assert.True(await ResultAsync(admin, new() { DeleteChannel = new() { ChannelId = room.Id } }));
|
||||
Assert.Equal(room.Id, (await guest.ReadUntilAsync(e => e.ChannelEvent?.Kind == ChannelEvent.Types.Kind.Deleted)).ChannelEvent.DeletedId);
|
||||
guest.Send(new() { Subscribe = new() });
|
||||
var snapshot = (await guest.ReadUntilAsync(e => e.ServerState is not null)).ServerState;
|
||||
Assert.Equal(1U, snapshot.Users.Single(u => u.Id == user.Id).ChannelId);
|
||||
Assert.DoesNotContain(snapshot.Channels, c => c.Id == room.Id);
|
||||
using var reopened = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db"));
|
||||
Assert.DoesNotContain(reopened.LoadChannels(), c => c.Id == room.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvalidChangesCannotCorruptChannelTreeOrLobby()
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
await using var admin = await AdminAsync(fixture);
|
||||
Assert.True(await ResultAsync(admin, new() { CreateChannel = new() { Channel = Room("Parent") } }));
|
||||
using var store = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db"));
|
||||
Channel parent = store.LoadChannels().Single(c => c.Name == "Parent");
|
||||
Channel child = Room("Child"); child.ParentId = parent.Id;
|
||||
Assert.True(await ResultAsync(admin, new() { CreateChannel = new() { Channel = child } }));
|
||||
child = store.LoadChannels().Single(c => c.Name == "Child");
|
||||
parent.ParentId = child.Id;
|
||||
Assert.False(await ResultAsync(admin, new() { EditChannel = new() { Channel = parent } }));
|
||||
Assert.False(await ResultAsync(admin, new() { DeleteChannel = new() { ChannelId = parent.Id } }));
|
||||
Assert.False(await ResultAsync(admin, new() { DeleteChannel = new() { ChannelId = 1 } }));
|
||||
Channel lobby = store.LoadChannels().Single(c => c.Id == 1);
|
||||
Assert.False(await ResultAsync(admin, new() { EditChannel = new() { Channel = lobby, Password = "lockout" } }));
|
||||
Assert.False(await ResultAsync(admin, new() { CreateChannel = new() { Channel = Room("Child") } }));
|
||||
var invalid = Room("Invalid"); invalid.Audio.SampleRate = 123;
|
||||
Assert.False(await ResultAsync(admin, new() { CreateChannel = new() { Channel = invalid } }));
|
||||
Assert.False(await ResultAsync(admin, new() { CreateChannel = new() }));
|
||||
Assert.Equal(4, store.LoadChannels().Count);
|
||||
Assert.True(await ResultAsync(admin, new() { DeleteChannel = new() { ChannelId = child.Id } }));
|
||||
Assert.True(await ResultAsync(admin, new() { DeleteChannel = new() { ChannelId = parent.Id } }));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user