132 lines
7.3 KiB
C#
132 lines
7.3 KiB
C#
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 } }));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DredAudioSettingsRoundTripThroughEditAndDatabaseRestart()
|
|
{
|
|
await using var fixture = new ServerFixture();
|
|
await using var admin = await AdminAsync(fixture);
|
|
using (var store = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db")))
|
|
{
|
|
Channel music = store.LoadChannels().Single(c => c.Name == "Music Room");
|
|
music.Audio.SampleRate = 48_000;
|
|
music.Audio.BitrateBps = 128_000;
|
|
music.Audio.ExpectedPacketLoss = 15;
|
|
music.Audio.Dtx = true;
|
|
music.Audio.Fec = true;
|
|
music.Audio.Dred = true;
|
|
music.Audio.Complexity = 10;
|
|
Assert.True(await ResultAsync(admin, new() { EditChannel = new() { Channel = music } }));
|
|
}
|
|
|
|
using var reopened = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db"));
|
|
AudioConfig audio = reopened.LoadChannels().Single(c => c.Name == "Music Room").Audio;
|
|
Assert.Equal(48_000U, audio.SampleRate);
|
|
Assert.Equal(128_000U, audio.BitrateBps);
|
|
Assert.Equal(15U, audio.ExpectedPacketLoss);
|
|
Assert.True(audio.Dtx);
|
|
Assert.True(audio.Fec);
|
|
Assert.True(audio.Dred);
|
|
Assert.Equal(10U, audio.Complexity);
|
|
}
|
|
}
|