mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
401 lines
15 KiB
C#
401 lines
15 KiB
C#
using System.Security.Claims;
|
|
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.Admin;
|
|
using RadicadeServer.API.Avatars.V2;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.RecNet.Avatar;
|
|
using RadicadeServer.RecNet.Rooms;
|
|
using RadicadeServer.Utils;
|
|
using Xunit;
|
|
|
|
namespace RadicadeServer.Tests;
|
|
|
|
/// <summary>
|
|
/// A room hands players one or more boxes when they join. The boxes live in RoomJoinGifts, one row
|
|
/// each, and the panel edits them as a list behind Room Details.
|
|
///
|
|
/// A room saved before that list existed still describes one box on its own Rooms row; it keeps
|
|
/// handing that out, which is what every room did before the list existed. A room with a list is
|
|
/// read from the list alone, and each box is claimed on its own, so editing one box re-issues that
|
|
/// box without silencing the others.
|
|
///
|
|
/// These tests drive the real admin route and then read the boxes the way a client does -
|
|
/// GET api/avatar/v2/gifts - rather than inspecting the rows the route answered with.
|
|
[Collection(DbFactoryCollection.Name)]
|
|
public class RoomJoinGiftListTests
|
|
{
|
|
private const string Secret = "room-join-gift-list-key";
|
|
private const long RoomId = 911;
|
|
private const long Joiner = 114903603133502227;
|
|
private const long Sender = 733901112234567890;
|
|
|
|
private static RecDBContext NewDb() => new(
|
|
TestDbOptions.Create()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options);
|
|
|
|
private static async Task<RecDBContext> NewRoomAsync(string name = "RecCenter")
|
|
{
|
|
var db = NewDb();
|
|
db.Rooms.Add(new RoomData
|
|
{
|
|
RoomId = RoomId,
|
|
Name = name,
|
|
CreatorAccountId = 1
|
|
});
|
|
await db.SaveChangesAsync();
|
|
return db;
|
|
}
|
|
|
|
/// <summary>Patches the room the way the panel does, and returns the answer.</summary>
|
|
private static async Task<IActionResult> SaveRoomAsync(RecDBContext db, string payloadJson)
|
|
{
|
|
var api = new AdminPanelAPI(db);
|
|
var originalKey = DBConfig.AdminApiKey;
|
|
DBConfig.AdminApiKey = Secret;
|
|
try
|
|
{
|
|
var payload = JsonDocument.Parse(payloadJson).RootElement;
|
|
return await api.Update("rooms", RoomId, Secret, payload);
|
|
}
|
|
finally
|
|
{
|
|
DBConfig.AdminApiKey = originalKey;
|
|
}
|
|
}
|
|
|
|
private static Task<IActionResult> SaveGiftsAsync(RecDBContext db, params object[] gifts)
|
|
{
|
|
var payload = JsonSerializer.Serialize(new { join_gifts = gifts });
|
|
return SaveRoomAsync(db, payload);
|
|
}
|
|
|
|
/// <summary>One box as the panel sends it.</summary>
|
|
private static object Gift(long id, string title, int currency, bool enabled = true,
|
|
bool oncePerPlayer = true, string fromPlayerId = "", string message = "")
|
|
{
|
|
return new
|
|
{
|
|
id,
|
|
enabled,
|
|
once_per_player = oncePerPlayer,
|
|
title,
|
|
message,
|
|
from_player_id = fromPlayerId,
|
|
avatar_item = "",
|
|
avatar_item_type = "Outfit",
|
|
equipment_item = "",
|
|
equipment_modification_guid = "",
|
|
consumable_item = "",
|
|
currency_type = "RecCenterTokens",
|
|
currency_amount = currency,
|
|
xp = 0,
|
|
context = "Game_Drop",
|
|
rarity = "Common"
|
|
};
|
|
}
|
|
|
|
/// <summary>The boxes as the recipient's client reads them: GET api/avatar/v2/gifts.</summary>
|
|
private static async Task<List<Gift>> ClientGiftsAsync(RecDBContext db, long playerId)
|
|
{
|
|
var api = new AvatarV2(db)
|
|
{
|
|
ControllerContext = new ControllerContext
|
|
{
|
|
HttpContext = new DefaultHttpContext
|
|
{
|
|
User = new ClaimsPrincipal(new ClaimsIdentity(
|
|
[new Claim(ClaimTypes.NameIdentifier, playerId.ToString())], "test"))
|
|
}
|
|
}
|
|
};
|
|
|
|
var result = await api.GetGifts();
|
|
var ok = Assert.IsType<OkObjectResult>(result);
|
|
return Assert.IsAssignableFrom<IEnumerable<Gift>>(ok.Value).ToList();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TwoBoxesAreBothHandedToTheJoiner()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
Assert.IsType<OkObjectResult>(await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25),
|
|
Gift(0, "Thanks!", 5)));
|
|
|
|
Assert.True(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
|
|
var boxes = await ClientGiftsAsync(db, Joiner);
|
|
Assert.Equal(2, boxes.Count);
|
|
Assert.Equal(new[] { "Welcome!", "Thanks!" }, boxes.Select(x => x.FriendlyName).OrderBy(x => x == "Welcome!" ? 0 : 1));
|
|
Assert.Equal(2, boxes.Select(x => x.GiftDropId).Distinct().Count());
|
|
Assert.Equal([25, 5], boxes.Select(x => x.Currency).OrderByDescending(x => x));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AnOrderedListIsHandedOutInTheOrderThePanelShowsIt()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db,
|
|
Gift(0, "First", 1),
|
|
Gift(0, "Second", 2),
|
|
Gift(0, "Third", 3));
|
|
|
|
await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId);
|
|
|
|
// Each box names itself, so the client's list can be read in the order the room stores.
|
|
var stored = await db.RoomJoinGifts.Where(x => x.RoomId == RoomId)
|
|
.OrderBy(x => x.SortOrder).ToListAsync();
|
|
Assert.Equal(new[] { "First", "Second", "Third" }, stored.Select(x => x.Title));
|
|
Assert.Equal(new[] { 0, 1, 2 }, stored.Select(x => x.SortOrder));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EachBoxIsClaimedOnItsOwn()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db, Gift(0, "Welcome!", 25), Gift(0, "Thanks!", 5));
|
|
|
|
Assert.True(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
// Nothing changed, so the second visit is silent.
|
|
Assert.False(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
|
|
Assert.Equal(2, (await ClientGiftsAsync(db, Joiner)).Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AChangedBoxIsHandedOutAgainWithoutReissuingTheOther()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db, Gift(0, "Welcome!", 25), Gift(0, "Thanks!", 5));
|
|
await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId);
|
|
|
|
var saved = (await db.RoomJoinGifts.Where(x => x.RoomId == RoomId)
|
|
.OrderBy(x => x.SortOrder).ToListAsync());
|
|
var firstId = saved[0].RoomJoinGiftId;
|
|
var secondId = saved[1].RoomJoinGiftId;
|
|
|
|
// The panel posts the whole list every save: only the second box's amount changed.
|
|
await SaveGiftsAsync(db,
|
|
Gift(firstId, "Welcome!", 25),
|
|
Gift(secondId, "Thanks!", 50));
|
|
|
|
Assert.True(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
|
|
var boxes = await ClientGiftsAsync(db, Joiner);
|
|
Assert.Equal(3, boxes.Count);
|
|
Assert.Equal(3, boxes.Select(x => x.GiftDropId).Distinct().Count());
|
|
Assert.Single(boxes, x => x.Currency == 50);
|
|
|
|
// The box that did not change was not handed out a second time.
|
|
var unchanged = await db.RoomJoinGifts.SingleAsync(x => x.RoomJoinGiftId == firstId);
|
|
Assert.Equal(1, unchanged.Version);
|
|
Assert.Equal(2, (await db.RoomJoinGifts.SingleAsync(x => x.RoomJoinGiftId == secondId)).Version);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ABoxLeftOutOfTheListIsNoLongerHandedOut()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db, Gift(0, "Welcome!", 25), Gift(0, "Thanks!", 5));
|
|
var first = (await db.RoomJoinGifts.OrderBy(x => x.SortOrder).FirstAsync()).RoomJoinGiftId;
|
|
|
|
await SaveGiftsAsync(db, Gift(first, "Welcome!", 25));
|
|
|
|
Assert.Single(await db.RoomJoinGifts.Where(x => x.RoomId == RoomId).ToListAsync());
|
|
await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId);
|
|
var boxes = Assert.Single(await ClientGiftsAsync(db, Joiner));
|
|
Assert.Equal("Welcome!", boxes.FriendlyName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ADisabledBoxIsSkippedAndTheRestStillArrive()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25),
|
|
Gift(0, "Thanks!", 5, enabled: false));
|
|
|
|
Assert.True(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
|
|
var boxes = Assert.Single(await ClientGiftsAsync(db, Joiner));
|
|
Assert.Equal("Welcome!", boxes.FriendlyName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ABoxThatGivesNothingIsNotAHandout()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
// The editor always keeps at least one card, and a blank one gives nothing at all.
|
|
await SaveGiftsAsync(db, Gift(0, "Room Visit Gift", 0));
|
|
|
|
Assert.False(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
Assert.Empty(await ClientGiftsAsync(db, Joiner));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ARoomSavedBeforeTheListStillHandsOutItsOwnBox()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
var room = await db.Rooms.SingleAsync(x => x.RoomId == RoomId);
|
|
room.JoinGiftEnabled = true;
|
|
room.JoinGiftOncePerPlayer = true;
|
|
room.JoinGiftTitle = "Room Visit Gift";
|
|
room.JoinGiftCurrencyAmount = 25;
|
|
await db.SaveChangesAsync();
|
|
|
|
Assert.True(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
|
|
var box = Assert.Single(await ClientGiftsAsync(db, Joiner));
|
|
Assert.Equal(25, box.Currency);
|
|
Assert.Empty(await db.RoomJoinGifts.ToListAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TheRoomsOwnFieldsDescribeTheFirstBox()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
Assert.IsType<OkObjectResult>(await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25, fromPlayerId: Sender.ToString()),
|
|
Gift(0, "Thanks!", 5, enabled: false)));
|
|
|
|
var room = await db.Rooms.SingleAsync(x => x.RoomId == RoomId);
|
|
Assert.Equal("Welcome!", room.JoinGiftTitle);
|
|
Assert.Equal(25, room.JoinGiftCurrencyAmount);
|
|
Assert.Equal(Sender, room.JoinGiftFromPlayerId);
|
|
// The room is still handing boxes out even though only its first one is enabled.
|
|
Assert.True(room.JoinGiftEnabled);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AListWithEveryBoxDisabledClosesTheRoom()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25, enabled: false),
|
|
Gift(0, "Thanks!", 5, enabled: false));
|
|
|
|
var room = await db.Rooms.SingleAsync(x => x.RoomId == RoomId);
|
|
Assert.False(room.JoinGiftEnabled);
|
|
Assert.False(await RoomJoinGiftUtils.TryDeliverAsync(db, Joiner, RoomId));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ThePanelReadsTheWholeListBackWithSendersAsDigits()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25, fromPlayerId: Sender.ToString()),
|
|
Gift(0, "Thanks!", 5));
|
|
|
|
var api = new AdminPanelAPI(db);
|
|
var originalKey = DBConfig.AdminApiKey;
|
|
DBConfig.AdminApiKey = Secret;
|
|
string? firstSender;
|
|
int count;
|
|
try
|
|
{
|
|
var details = Assert.IsType<OkObjectResult>(await api.RoomPanelDetails(RoomId, Secret));
|
|
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(details.Value));
|
|
var gifts = doc.RootElement.GetProperty("room").GetProperty("join_gifts");
|
|
count = gifts.GetArrayLength();
|
|
var sender = gifts[0].GetProperty("from_player_id");
|
|
Assert.Equal(JsonValueKind.String, sender.ValueKind);
|
|
firstSender = sender.GetString();
|
|
}
|
|
finally
|
|
{
|
|
DBConfig.AdminApiKey = originalKey;
|
|
}
|
|
|
|
Assert.Equal(2, count);
|
|
Assert.Equal(Sender.ToString(), firstSender);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TheRoomsListThePanelLoadsCarriesEveryBox()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db, Gift(0, "Welcome!", 25), Gift(0, "Thanks!", 5));
|
|
|
|
// The panel's room table is what fills the editor, so the list has to travel with it - the
|
|
// details route is not what opens the modal.
|
|
var api = new AdminPanelAPI(db);
|
|
var originalKey = DBConfig.AdminApiKey;
|
|
DBConfig.AdminApiKey = Secret;
|
|
JsonElement room;
|
|
try
|
|
{
|
|
var result = Assert.IsType<OkObjectResult>(await api.List("rooms", Secret, null, null, null));
|
|
using var doc = JsonDocument.Parse(JsonSerializer.Serialize(result.Value));
|
|
room = doc.RootElement.EnumerateArray()
|
|
.Single(x => x.GetProperty("id").GetInt64() == RoomId)
|
|
.Clone();
|
|
}
|
|
finally
|
|
{
|
|
DBConfig.AdminApiKey = originalKey;
|
|
}
|
|
|
|
var gifts = room.GetProperty("join_gifts");
|
|
Assert.Equal(2, gifts.GetArrayLength());
|
|
Assert.Equal("Welcome!", gifts[0].GetProperty("title").GetString());
|
|
Assert.Equal(25, gifts[0].GetProperty("currency_amount").GetInt32());
|
|
Assert.Equal("Thanks!", gifts[1].GetProperty("title").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ASenderThatIsNotAnAccountIdRefusesTheWholeList()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
|
|
var result = await SaveGiftsAsync(db,
|
|
Gift(0, "Welcome!", 25),
|
|
Gift(0, "Thanks!", 5, fromPlayerId: "not-a-player"));
|
|
|
|
var bad = Assert.IsType<BadRequestObjectResult>(result);
|
|
Assert.Contains("join_gifts[1].from_player_id", bad.Value?.ToString());
|
|
// Nothing was written: a save the panel could not make sense of leaves no half-list behind.
|
|
Assert.Empty(await db.RoomJoinGifts.ToListAsync());
|
|
Assert.Equal(0, (await db.Rooms.SingleAsync(x => x.RoomId == RoomId)).JoinGiftCurrencyAmount);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ABoxIdFromAnotherRoomIsRefused()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
db.RoomJoinGifts.Add(new RoomJoinGift
|
|
{
|
|
RoomId = 4242,
|
|
SortOrder = 0,
|
|
Title = "Someone else's",
|
|
CurrencyAmount = 1
|
|
});
|
|
await db.SaveChangesAsync();
|
|
var foreignId = (await db.RoomJoinGifts.SingleAsync()).RoomJoinGiftId;
|
|
|
|
var result = await SaveGiftsAsync(db, Gift(foreignId, "Welcome!", 25));
|
|
|
|
Assert.IsType<BadRequestObjectResult>(result);
|
|
Assert.Empty(await db.RoomJoinGifts.Where(x => x.RoomId == RoomId).ToListAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReorderingTheListReissuesNothing()
|
|
{
|
|
var db = await NewRoomAsync();
|
|
await SaveGiftsAsync(db, Gift(0, "First", 1), Gift(0, "Second", 2));
|
|
var ids = (await db.RoomJoinGifts.OrderBy(x => x.SortOrder).ToListAsync())
|
|
.Select(x => x.RoomJoinGiftId).ToArray();
|
|
|
|
await SaveGiftsAsync(db, Gift(ids[1], "Second", 2), Gift(ids[0], "First", 1));
|
|
|
|
var stored = await db.RoomJoinGifts.OrderBy(x => x.SortOrder).ToListAsync();
|
|
Assert.Equal(new[] { ids[1], ids[0] }, stored.Select(x => x.RoomJoinGiftId));
|
|
Assert.All(stored, x => Assert.Equal(1, x.Version));
|
|
}
|
|
}
|