mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.Admin;
|
||||
using RadicadeServer.API.Avatars.V2;
|
||||
using RadicadeServer.API.Storefront;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Avatar;
|
||||
using RadicadeServer.RecNet.Enums.Store;
|
||||
using Xunit;
|
||||
|
||||
namespace RadicadeServer.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// The Gift Player page in the admin panel posts one <c>gift_log</c> row per account to
|
||||
/// <c>POST admin/panel/gift_log</c>, and AddGiftLogRows turns each row into a real gift box on
|
||||
/// that account. The box carries its sender as RecNet's Gift.FromPlayerId, and that is what the
|
||||
/// player reads off the box - so the panel's "Gift From" field has to land there, not only on the
|
||||
/// panel's own log row.
|
||||
///
|
||||
/// The panel sends ids (both player_id and from_player_id) as digits in a JSON string, because a
|
||||
/// platform id is seventeen digits and a JSON number cannot hold that many exactly.
|
||||
///
|
||||
/// These tests drive the real route and then read the box back the way a client does -
|
||||
/// GET api/avatar/v2/gifts - rather than inspecting the row the route answered with, so a sender
|
||||
/// stored in the log but not on the box fails here.
|
||||
public class AdminGiftFromTests
|
||||
{
|
||||
private const string Secret = "test-bot-secret";
|
||||
|
||||
private static RecDBContext NewDb() => new(
|
||||
TestDbOptions.Create()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||
.Options);
|
||||
|
||||
/// <summary>Posts one gift_log row through the route, and returns the row the route answered with.</summary>
|
||||
private static async Task<JsonObject> GiveAsync(RecDBContext db, string rowJson)
|
||||
{
|
||||
var api = new AdminPanelAPI(db);
|
||||
var originalKey = DBConfig.AdminApiKey;
|
||||
var originalLogs = DBConfig.PersistentLogs;
|
||||
DBConfig.AdminApiKey = Secret;
|
||||
DBConfig.PersistentLogs = Path.Join(Path.GetTempPath(), "radicade-admin-tests-" + Guid.NewGuid().ToString("N"));
|
||||
try
|
||||
{
|
||||
var payload = JsonDocument.Parse(rowJson).RootElement;
|
||||
var result = await api.Insert("gift_log", Secret, payload);
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
return Assert.IsType<JsonObject>(ok.Value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DBConfig.AdminApiKey = originalKey;
|
||||
DBConfig.PersistentLogs = originalLogs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The box as the recipient's client reads it: 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();
|
||||
}
|
||||
|
||||
/// <summary>The JSON a client actually receives for one box, with the server's own naming.</summary>
|
||||
private static JsonElement SerializeAsClient(Gift gift)
|
||||
=> JsonDocument.Parse(JsonSerializer.Serialize(gift)).RootElement;
|
||||
|
||||
private const long Recipient = 114903603133502227;
|
||||
private const long Sender = 733901112234567890;
|
||||
|
||||
[Fact]
|
||||
public async Task TheGiftFromBecomesTheBoxSenderTheClientReads()
|
||||
{
|
||||
var db = NewDb();
|
||||
|
||||
var row = await GiveAsync(db, $$"""
|
||||
{
|
||||
"player_id": "{{Recipient}}",
|
||||
"from_player_id": "{{Sender}}",
|
||||
"message": "Thanks for the help!",
|
||||
"context": "Game_Drop",
|
||||
"rarity": "Rare",
|
||||
"avatar": "8aa79563-ace1-4ba7-ad0c-f3210a78142f,"
|
||||
}
|
||||
""");
|
||||
|
||||
// The route's answer is the panel's record of what it did.
|
||||
Assert.Equal(true, row["sent"]?.GetValue<bool>());
|
||||
Assert.Equal(Sender, row["from_player_id"]!.GetValue<long>());
|
||||
|
||||
// The box itself, stored against the recipient.
|
||||
var stored = Assert.Single(await StorefrontShared.GetPendingGiftsAsync(db, Recipient));
|
||||
Assert.Equal(Sender, stored.FromPlayerId);
|
||||
|
||||
// And what the client reads off that stored box.
|
||||
var read = Assert.Single(await ClientGiftsAsync(db, Recipient));
|
||||
Assert.Equal(Sender, read.FromPlayerId);
|
||||
|
||||
var wire = SerializeAsClient(read);
|
||||
Assert.Equal(Sender, wire.GetProperty("FromPlayerId").GetInt64());
|
||||
Assert.Equal(Rarity.Rare, read.Rarity);
|
||||
Assert.Equal(GiftContext.Game_Drop, read.Context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ABlankGiftFromSendsItFromTheRadicadeTeam()
|
||||
{
|
||||
var db = NewDb();
|
||||
|
||||
// The panel leaves from_player_id out entirely when the field is blank; the box then
|
||||
// comes from the Radicade account, the same sender the server's own admin gifts use.
|
||||
var row = await GiveAsync(db, $$"""
|
||||
{
|
||||
"player_id": "{{Recipient}}",
|
||||
"context": "Game_Drop",
|
||||
"rarity": "Common",
|
||||
"cons": "00000000-0000-0000-0000-000000000000,"
|
||||
}
|
||||
""");
|
||||
|
||||
Assert.Equal(true, row["sent"]?.GetValue<bool>());
|
||||
Assert.Equal(1L, row["from_player_id"]!.GetValue<long>());
|
||||
|
||||
var read = Assert.Single(await ClientGiftsAsync(db, Recipient));
|
||||
Assert.Equal(1L, read.FromPlayerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ASeventeenDigitSenderIsNotRoundedOrOverflowed()
|
||||
{
|
||||
var db = NewDb();
|
||||
|
||||
// Both ids are past int.MaxValue, which is why the panel posts them as text.
|
||||
Assert.True(Sender > int.MaxValue);
|
||||
Assert.True(Recipient > int.MaxValue);
|
||||
|
||||
await GiveAsync(db, $$"""
|
||||
{
|
||||
"player_id": "{{Recipient}}",
|
||||
"from_player_id": "{{Sender}}",
|
||||
"context": "Game_Drop",
|
||||
"rarity": "Common",
|
||||
"equip": "Sword"
|
||||
}
|
||||
""");
|
||||
|
||||
var read = Assert.Single(await ClientGiftsAsync(db, Recipient));
|
||||
Assert.Equal(Sender, read.FromPlayerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ASenderThePanelDidNotSendDoesNotClearTheTeamDefault()
|
||||
{
|
||||
var db = NewDb();
|
||||
|
||||
// Nonsense in the field is refused by the panel before it posts; if it ever reaches the
|
||||
// route anyway the box still has a sender rather than a bare zero.
|
||||
await GiveAsync(db, $$"""
|
||||
{
|
||||
"player_id": "{{Recipient}}",
|
||||
"from_player_id": "not-a-player",
|
||||
"context": "Game_Drop",
|
||||
"rarity": "Common",
|
||||
"cons": "00000000-0000-0000-0000-000000000000,"
|
||||
}
|
||||
""");
|
||||
|
||||
var read = Assert.Single(await ClientGiftsAsync(db, Recipient));
|
||||
Assert.Equal(1L, read.FromPlayerId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user