mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 15:39:01 -07:00
263 lines
13 KiB
C#
263 lines
13 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.RecNet.CDN;
|
|
using RadicadeServer.RecNet.Config;
|
|
using RadicadeServer.RecNet.Enums;
|
|
using RadicadeServer.RecNet.Enums.Rooms;
|
|
using RadicadeServer.RecNet.Events;
|
|
using RadicadeServer.RecNet.Leaderboards;
|
|
using RadicadeServer.RecNet.Messages;
|
|
using RadicadeServer.RecNet.Rooms;
|
|
using RadicadeServer.Utils;
|
|
using Xunit;
|
|
|
|
namespace RadicadeServer.Tests;
|
|
|
|
/// <summary>
|
|
/// Showdown belongs in the shipped catalog, but it used to be built by an importer of its own: it
|
|
/// never got the image images.txt ships for it (it kept default.png), and because it sat outside
|
|
/// <see cref="DefaultRooms.All"/> nothing that rebuilt the catalog would have brought it back.
|
|
/// These pin both halves of the repair - the catalog entry itself, and the one-time reset that
|
|
/// deletes every room in the table so the catalog can seed the shipped set from nothing.
|
|
///
|
|
/// The reset is destructive by design, so the tests say what it takes: every room, the RecCenter
|
|
/// and every player's dorm included, with the rows that hang off them. What they check hardest is
|
|
/// that the database is left *usable* - the dorm template every player's own dorm is cloned from is
|
|
/// put back on the same boot, before anyone can join.
|
|
/// </summary>
|
|
public class DefaultRoomsResetTests
|
|
{
|
|
private const long CoachId = 1;
|
|
private const long PlayerId = 2;
|
|
private const long DriftedShowdownId = 32;
|
|
private const long DriftedBowlingId = 12;
|
|
private const long RecCenterId = 3;
|
|
private const long DormId = 1;
|
|
private const long PlayerRoomId = 40;
|
|
|
|
private const string DriftedShowdownTags = "rro,2|games,0|beta,1";
|
|
|
|
private static RecDBContext NewDb() =>
|
|
new(TestDbOptions.Create()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options);
|
|
|
|
private static RoomData Room(
|
|
long id, string name, long creator, bool isDorm = false, string tags = "",
|
|
string image = "default.png", int maxPlayers = 10,
|
|
Accessibility accessibility = Accessibility.Public, bool cloningAllowed = false) => new()
|
|
{
|
|
RoomId = id,
|
|
Name = name,
|
|
Description = name,
|
|
ImageName = image,
|
|
Tags = tags,
|
|
CreatorAccountId = creator,
|
|
IsDorm = isDorm,
|
|
MaxPlayers = maxPlayers,
|
|
Accessibility = accessibility,
|
|
CloningAllowed = cloningAllowed,
|
|
State = RoomState.Active,
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
|
|
private static SubRoom Sub(
|
|
long id, long roomId, string name = "Home", string scene = "old-scene",
|
|
string save = "", string bundle = "") => new()
|
|
{
|
|
SubRoomId = id,
|
|
RoomId = roomId,
|
|
Name = name,
|
|
DataBlob = save,
|
|
UnityAssetId = bundle,
|
|
UnitySceneId = scene,
|
|
MaxPlayers = 10,
|
|
Accessibility = Accessibility.Public,
|
|
SavedByAccountId = CoachId,
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
|
|
/// <summary>
|
|
/// The shape the live database was in: the importer's Showdown with the default picture, a
|
|
/// catalog room that drifted, the RecCenter, the dorm template, a room a player made, and one
|
|
/// row in every table that hangs off a room - so the cleanup is checked table by table rather
|
|
/// than only for the room row. The alignment marker is already set, which is the trap: without
|
|
/// the reset clearing it, the catalog would decide this database was fine and do nothing.
|
|
/// </summary>
|
|
private static void SeedDriftedState(RecDBContext db)
|
|
{
|
|
db.Rooms.AddRange(
|
|
Room(DormId, "DormRoom", CoachId, isDorm: true, tags: "dormroom,0",
|
|
accessibility: Accessibility.Private, cloningAllowed: true),
|
|
Room(RecCenterId, "RecCenter", CoachId, tags: "rro,2", image: "RecCenter", maxPlayers: 12),
|
|
Room(DriftedShowdownId, "Showdown", CoachId, tags: DriftedShowdownTags, maxPlayers: 16, cloningAllowed: true),
|
|
Room(DriftedBowlingId, "Bowling", CoachId, tags: "rro,2|sports,0", maxPlayers: 99),
|
|
Room(PlayerRoomId, "MyCoolRoom", PlayerId, image: "myimage"));
|
|
|
|
db.SubRooms.AddRange(
|
|
Sub(1, DormId, scene: SceneLocations.Dorm),
|
|
Sub(3, RecCenterId),
|
|
Sub(DriftedShowdownId, DriftedShowdownId, scene: SceneLocations.MakerRoom,
|
|
save: "Showdown.room", bundle: "Showdown_0_1.assetbundle"),
|
|
Sub(DriftedBowlingId, DriftedBowlingId),
|
|
Sub(PlayerRoomId, PlayerRoomId));
|
|
|
|
db.RoomStats.AddRange(
|
|
new RoomStats { RoomId = DormId },
|
|
new RoomStats { RoomId = RecCenterId },
|
|
new RoomStats { RoomId = DriftedShowdownId },
|
|
new RoomStats { RoomId = PlayerRoomId });
|
|
|
|
db.RoomRoles.AddRange(
|
|
new RoomRole { RoomId = DriftedShowdownId, AccountId = CoachId, Role = RoleType.Creator },
|
|
new RoomRole { RoomId = PlayerRoomId, AccountId = PlayerId, Role = RoleType.Creator });
|
|
|
|
db.RoomKeys.Add(new RoomKey { RoomId = DriftedShowdownId, ReplicationId = "key", Name = "Key", Description = "" });
|
|
db.RoomBans.Add(new RoomBan { roomId = DriftedShowdownId, actorId = CoachId, bannedPlayerId = PlayerId });
|
|
db.RoomComments.Add(new RoomComment { RoomId = DriftedShowdownId, CreatorAccountId = PlayerId, Comment = "on the wrong room" });
|
|
db.RoomComments.Add(new RoomComment { RoomId = PlayerRoomId, CreatorAccountId = PlayerId, Comment = "kept for nobody" });
|
|
db.RoomInteractions.Add(new PlayerInteractionDB { PlayerId = PlayerId, RoomId = DriftedShowdownId, Visited = true });
|
|
db.RoomInteractions.Add(new PlayerInteractionDB { PlayerId = PlayerId, RoomId = PlayerRoomId, Visited = true });
|
|
db.RoomInstances.Add(new RoomInstance { roomId = DriftedShowdownId, subRoomId = DriftedShowdownId });
|
|
db.LeaderboardScores.Add(new LeaderboardScore { RoomId = DriftedShowdownId, PlayerId = PlayerId, Score = 12 });
|
|
db.SubRoomPermissions.Add(new SubRoomPermission { subRoomId = DriftedShowdownId, permission = "AnyoneCanSave" });
|
|
db.SubRoomHistory.Add(new RestorableSubRoom { RoomId = DriftedShowdownId, SubRoomId = DriftedShowdownId, DataBlob = "old" });
|
|
|
|
db.PlayerEvents.Add(new PlayerEventDB
|
|
{
|
|
PlayerEventId = 7,
|
|
Name = "Event in a room that is about to go",
|
|
CreatorPlayerId = PlayerId,
|
|
RoomId = DriftedShowdownId,
|
|
StartTime = DateTime.UtcNow,
|
|
EndTime = DateTime.UtcNow,
|
|
});
|
|
db.PlayerEventResponses.Add(new PlayerEventResponse { PlayerEventId = 7, PlayerId = PlayerId });
|
|
|
|
// These three only point at a room, so their rows are kept and the reference cleared.
|
|
db.Images.Add(new ImageDB { Name = "photo-in-showdown", OwnerId = PlayerId, RoomId = DriftedShowdownId });
|
|
db.Messages.Add(new Message { ToPlayerId = PlayerId, FromPlayerId = CoachId, RoomId = DriftedShowdownId, SentTime = DateTime.UtcNow });
|
|
|
|
// Already aligned: the trap the reset has to clear.
|
|
db.GameConfig.Add(new ConfigVal("DefaultRooms.AlignmentVersion", "1"));
|
|
|
|
db.SaveChanges();
|
|
}
|
|
|
|
[Fact]
|
|
public void TheCatalogShipsShowdownWithItsOwnImageAndRoomFile()
|
|
{
|
|
var showdown = DefaultRooms.All.Single(room => room.Name == "Showdown");
|
|
|
|
Assert.Equal("Showdown", showdown.ImageName);
|
|
Assert.Equal(16, showdown.MaxPlayers);
|
|
Assert.True(showdown.CloningAllowed);
|
|
Assert.True(showdown.IsRRO);
|
|
Assert.Equal(Accessibility.Public, showdown.Accessibility);
|
|
Assert.Equal(SceneLocations.MakerRoom, showdown.SceneId);
|
|
Assert.Equal("Showdown.room", showdown.SaveName);
|
|
Assert.Equal("Showdown_0_1.assetbundle", showdown.BundleName);
|
|
// Spelled out rather than through RroWith, because "beta" is stored as an Auto tag.
|
|
Assert.Equal(DriftedShowdownTags, StringTools.CreateTagString(showdown.Tags.ToList()));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TheResetEmptiesTheTableAndPutsBackTheDormTemplateAndTheShippedRooms()
|
|
{
|
|
var db = NewDb();
|
|
SeedDriftedState(db);
|
|
|
|
await DefaultRooms.EnsureAsync(db);
|
|
db.ChangeTracker.Clear();
|
|
|
|
// Every room went, with no exception list - the old ids are all gone and nothing is left
|
|
// in the tables that hung off them.
|
|
foreach (var oldId in new[] { DormId, RecCenterId, DriftedShowdownId, DriftedBowlingId, PlayerRoomId })
|
|
Assert.False(await db.Rooms.AnyAsync(room => room.RoomId == oldId));
|
|
|
|
Assert.Empty(await db.RoomComments.ToListAsync());
|
|
Assert.Empty(await db.RoomInteractions.ToListAsync());
|
|
Assert.Empty(await db.RoomInstances.ToListAsync());
|
|
Assert.Empty(await db.LeaderboardScores.ToListAsync());
|
|
Assert.Empty(await db.RoomKeys.ToListAsync());
|
|
Assert.Empty(await db.RoomBans.ToListAsync());
|
|
Assert.Empty(await db.SubRoomPermissions.ToListAsync());
|
|
Assert.Empty(await db.SubRoomHistory.ToListAsync());
|
|
Assert.Empty(await db.PlayerEvents.ToListAsync());
|
|
Assert.Empty(await db.PlayerEventResponses.ToListAsync());
|
|
|
|
// A photo, a message and a moderation record outlive the room, without the dead reference.
|
|
Assert.Null((await db.Images.SingleAsync(image => image.Name == "photo-in-showdown")).RoomId);
|
|
Assert.Null((await db.Messages.SingleAsync()).RoomId);
|
|
|
|
// The dorm template is back before anyone can join - it is the row every player's own dorm
|
|
// is cloned from, so a server without it gives out no dorms at all.
|
|
var dorm = await db.Rooms.SingleAsync(room => room.IsDorm);
|
|
Assert.Equal("DormRoom", dorm.Name);
|
|
Assert.Equal("Dorm", dorm.ImageName);
|
|
Assert.Equal(CoachId, dorm.CreatorAccountId);
|
|
Assert.True(dorm.CloningAllowed);
|
|
var dormSub = await db.SubRooms.SingleAsync(sub => sub.RoomId == dorm.RoomId);
|
|
Assert.Equal("Home", dormSub.Name);
|
|
Assert.Equal(SceneLocations.Dorm, dormSub.UnitySceneId);
|
|
Assert.True(dormSub.IsSandbox);
|
|
Assert.True(await db.RoomStats.AnyAsync(stats => stats.RoomId == dorm.RoomId));
|
|
|
|
// ...and the whole shipped set was built from the catalog on the same boot, Showdown with
|
|
// its own picture and its room file attached.
|
|
var showdown = await db.Rooms.SingleAsync(room => room.Name == "Showdown");
|
|
Assert.NotEqual(DriftedShowdownId, showdown.RoomId);
|
|
Assert.Equal("Showdown", showdown.ImageName);
|
|
Assert.Equal(16, showdown.MaxPlayers);
|
|
Assert.Equal(DriftedShowdownTags, showdown.Tags);
|
|
Assert.True(showdown.CloningAllowed);
|
|
Assert.True(showdown.IsRRO);
|
|
Assert.Equal(Accessibility.Public, showdown.Accessibility);
|
|
Assert.Equal(RoomState.Active, showdown.State);
|
|
Assert.Equal(CoachId, showdown.CreatorAccountId);
|
|
|
|
var sub = await db.SubRooms.SingleAsync(room => room.RoomId == showdown.RoomId);
|
|
Assert.Equal("Home", sub.Name);
|
|
Assert.Equal(SceneLocations.MakerRoom, sub.UnitySceneId);
|
|
Assert.Equal("Showdown.room", sub.DataBlob);
|
|
Assert.Equal("Showdown_0_1.assetbundle", sub.UnityAssetId);
|
|
Assert.Equal(16, sub.MaxPlayers);
|
|
Assert.False(sub.IsSandbox);
|
|
Assert.True(await db.RoomStats.AnyAsync(stats => stats.RoomId == showdown.RoomId));
|
|
Assert.True(await db.RoomRoles.AnyAsync(role => role.RoomId == showdown.RoomId && role.AccountId == CoachId));
|
|
|
|
var recCenter = await db.Rooms.SingleAsync(room => room.Name == "RecCenter");
|
|
Assert.NotEqual(RecCenterId, recCenter.RoomId);
|
|
Assert.Equal("RecCenter", recCenter.ImageName);
|
|
var bowling = await db.Rooms.SingleAsync(room => room.Name == "Bowling");
|
|
Assert.Equal("Bowling", bowling.ImageName);
|
|
Assert.Equal(8, bowling.MaxPlayers);
|
|
Assert.True(await db.Rooms.AnyAsync(room => room.Name == "Orientation"));
|
|
|
|
// Exactly the shipped set, plus the one dorm template.
|
|
Assert.Equal(DefaultRooms.All.Count + 1, await db.Rooms.CountAsync());
|
|
Assert.Equal("1", (await db.GameConfig.SingleAsync(config => config.Key == "DefaultRooms.AlignmentVersion")).Value);
|
|
Assert.Equal("3", (await db.GameConfig.SingleAsync(config => config.Key == "DefaultRooms.ShippedRoomResetVersion")).Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TheResetRunsOnceAndDoesNotWipeTheRoomsItJustSeeded()
|
|
{
|
|
var db = NewDb();
|
|
SeedDriftedState(db);
|
|
|
|
await DefaultRooms.EnsureAsync(db);
|
|
var seeded = await db.Rooms.SingleAsync(room => room.Name == "Showdown");
|
|
var dorm = await db.Rooms.SingleAsync(room => room.IsDorm);
|
|
|
|
// A second boot: the marker stops the wipe, so the rooms the first one built are still
|
|
// there - not deleted and rebuilt every time the server starts.
|
|
await DefaultRooms.EnsureAsync(db);
|
|
db.ChangeTracker.Clear();
|
|
|
|
Assert.Equal(seeded.RoomId, (await db.Rooms.SingleAsync(room => room.Name == "Showdown")).RoomId);
|
|
Assert.Equal(dorm.RoomId, (await db.Rooms.SingleAsync(room => room.IsDorm)).RoomId);
|
|
Assert.Equal(DefaultRooms.All.Count + 1, await db.Rooms.CountAsync());
|
|
}
|
|
}
|