mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
169 lines
8.5 KiB
C#
169 lines
8.5 KiB
C#
using System.Security.Claims;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.API.Rooms;
|
|
using RadicadeServer.API.Rooms.Currency.V1;
|
|
using RadicadeServer.API.Rooms.Keys.V1;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.RecNet.Accounts;
|
|
using RadicadeServer.RecNet.Rooms;
|
|
using Xunit;
|
|
|
|
namespace RadicadeServer.Tests;
|
|
|
|
public class RoomEconomyPersistenceTests
|
|
{
|
|
[Fact]
|
|
public async Task CurrencyAndConsumableCreationPersistForTheCurrentRoom()
|
|
{
|
|
var options = TestDbOptions.Create()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
await using var db = new RecDBContext(options);
|
|
const long accountId = 71;
|
|
const long roomId = 902;
|
|
var instance = new RoomInstance { roomId = roomId };
|
|
db.RoomInstances.Add(instance);
|
|
db.Presences.Add(new RealPresence
|
|
{
|
|
playerId = accountId,
|
|
isOnline = true,
|
|
roomInstance = instance.roomInstanceId,
|
|
});
|
|
await db.SaveChangesAsync();
|
|
|
|
var currencyApi = new RoomCurrencyAPI(db);
|
|
SetUserAndJsonBody(currencyApi, accountId, "{\"Name\":\"Test currency\",\"Description\":\"A test currency\",\"Color\":\"#112233\"}");
|
|
var currencyResult = Assert.IsType<OkObjectResult>(await currencyApi.CreateCurrency());
|
|
var currency = Assert.IsType<Dictionary<string, object?>>(currencyResult.Value);
|
|
Assert.Equal(roomId, currency["RoomId"]);
|
|
Assert.Equal("#112233", currency["Color"]);
|
|
|
|
var consumableApi = new RoomConsumablesAPI(db);
|
|
SetUserAndJsonBody(consumableApi, accountId, "{\"Name\":\"Test consumable\",\"Description\":\"A test consumable\"}");
|
|
var consumableResult = Assert.IsType<OkObjectResult>(await consumableApi.CreateRoomConsumable());
|
|
var consumable = Assert.IsType<Dictionary<string, object?>>(consumableResult.Value);
|
|
Assert.Equal(roomId, consumable["RoomId"]);
|
|
|
|
var listedCurrency = Assert.IsType<OkObjectResult>(await currencyApi.GetRoomCurrencies(roomId));
|
|
var listedCurrencies = Assert.IsType<List<Dictionary<string, object?>>>(listedCurrency.Value);
|
|
Assert.Single(listedCurrencies);
|
|
Assert.Equal(currency["CurrencyId"], listedCurrencies[0]["CurrencyId"]);
|
|
|
|
var listedConsumable = Assert.IsType<OkObjectResult>(await consumableApi.GetRoomConsumablesInRoom(roomId));
|
|
var listedConsumables = Assert.IsType<List<Dictionary<string, object?>>>(listedConsumable.Value);
|
|
Assert.Single(listedConsumables);
|
|
Assert.Equal(consumable["ConsumableId"], listedConsumables[0]["ConsumableId"]);
|
|
|
|
var storedConfig = await db.ServerConfig.SingleAsync(config => config.Key == "RoomAssets");
|
|
Assert.Contains("Test currency", storedConfig.Value);
|
|
Assert.Contains("Test consumable", storedConfig.Value);
|
|
|
|
// The list routes load records from persisted JSON, not the original request
|
|
// dictionaries; nested JSON values must remain intact after deserialization.
|
|
await using var restartedDb = new RecDBContext(options);
|
|
var reloadedCurrencies = Assert.IsType<OkObjectResult>(
|
|
await new RoomCurrencyAPI(restartedDb).GetRoomCurrencies(roomId));
|
|
var reloadedCurrency = Assert.Single(
|
|
Assert.IsAssignableFrom<IEnumerable<Dictionary<string, object?>>>(reloadedCurrencies.Value));
|
|
Assert.Equal("#112233", reloadedCurrency["Color"]?.ToString());
|
|
|
|
var reloadedConsumables = Assert.IsType<OkObjectResult>(
|
|
await new RoomConsumablesAPI(restartedDb).GetRoomConsumablesInRoom(roomId));
|
|
Assert.Equal("Test consumable",
|
|
Assert.Single(Assert.IsAssignableFrom<IEnumerable<Dictionary<string, object?>>>(reloadedConsumables.Value))["Name"]?.ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RoomKeyCreationUsesTheCurrentRoomAndPersistsTheKey()
|
|
{
|
|
var options = TestDbOptions.Create()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
await using var db = new RecDBContext(options);
|
|
const long accountId = 81;
|
|
const long roomId = 903;
|
|
var instance = new RoomInstance { roomId = roomId };
|
|
db.RoomInstances.Add(instance);
|
|
db.Presences.Add(new RealPresence
|
|
{
|
|
playerId = accountId,
|
|
isOnline = true,
|
|
roomInstance = instance.roomInstanceId,
|
|
});
|
|
await db.SaveChangesAsync();
|
|
|
|
var api = new RoomKeysAPI(db);
|
|
SetUserAndJsonBody(api, accountId, "{\"Name\":\"Test key\",\"Description\":\"A test key\",\"Price\":5}");
|
|
|
|
var created = Assert.IsType<OkObjectResult>(await api.CreateRoomKey());
|
|
var key = Assert.IsType<RoomKey>(created.Value);
|
|
Assert.Equal(roomId, key.RoomId);
|
|
Assert.Equal("Test key", key.Name);
|
|
Assert.Equal(key.RoomKeyId, Assert.Single(db.RoomKeys).RoomKeyId);
|
|
var listed = Assert.IsType<OkObjectResult>(api.GetRoomKeysForRoom(roomId));
|
|
Assert.Equal(key.RoomKeyId, Assert.Single(Assert.IsAssignableFrom<IEnumerable<RoomKey>>(listed.Value)).RoomKeyId);
|
|
var missingRoomId = Assert.IsType<OkObjectResult>(api.GetRoomKeysForRoom(null));
|
|
Assert.Empty(Assert.IsAssignableFrom<IEnumerable<RoomKey>>(missingRoomId.Value));
|
|
|
|
// The client endpoint takes the RoomKeyId in its path and answers a bare bool.
|
|
// Creating a room key doesn't purchase it; model ownership explicitly.
|
|
db.Unlocks.Add(new PlayerUnlocks
|
|
{
|
|
AccountId = accountId,
|
|
RoomKeys = JsonSerializer.Serialize(new[] { key.RoomKeyId })
|
|
});
|
|
await db.SaveChangesAsync();
|
|
SetUserAndJsonBody(api, accountId, string.Empty);
|
|
Assert.True(Assert.IsType<bool>(Assert.IsType<OkObjectResult>(await api.IsRoomKeyPurchased(key.RoomKeyId)).Value));
|
|
Assert.False(Assert.IsType<bool>(Assert.IsType<OkObjectResult>(await api.IsRoomKeyPurchased(key.RoomKeyId + 1000)).Value));
|
|
Assert.False(Assert.IsType<bool>(Assert.IsType<OkObjectResult>(await api.IsRoomKeyPurchased(key.RoomKeyId + 1)).Value));
|
|
}
|
|
|
|
[Fact]
|
|
public void BalanceLookupsAnswerWithAZeroBalanceRatherThanNotFound()
|
|
{
|
|
// The 2023 client asks getBalance right after getAllBalances on every room join and
|
|
// retries an answer it cannot use, so a 404 here is a join that spends a fresh TLS
|
|
// connection and a round trip (~100 ms RTT) per retry before the room finishes
|
|
// loading. This server tracks no balances, so the answer is a zero balance.
|
|
var options = TestDbOptions.Create()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
using var db = new RecDBContext(options);
|
|
|
|
var api = new RoomCurrencyAPI(db);
|
|
SetUserAndJsonBody(api, 91, "{}");
|
|
|
|
var result = Assert.IsType<OkObjectResult>(api.GetBalance("d0618354-d68a-4364-a693-b8cf09879973", 91, 25));
|
|
var balance = JsonDocument.Parse(JsonSerializer.Serialize(result.Value)).RootElement;
|
|
Assert.Equal("d0618354-d68a-4364-a693-b8cf09879973", balance.GetProperty("CurrencyId").GetString());
|
|
Assert.Equal(91, balance.GetProperty("AccountId").GetInt64());
|
|
Assert.Equal(25, balance.GetProperty("RoomId").GetInt64());
|
|
Assert.Equal(0, balance.GetProperty("Balance").GetInt32());
|
|
|
|
// The client does not always send the account it is asking about; the caller's own
|
|
// id is the only sensible substitute.
|
|
var anonymous = new RoomCurrencyAPI(db);
|
|
SetUserAndJsonBody(anonymous, 92, "{}");
|
|
var inferred = Assert.IsType<OkObjectResult>(anonymous.GetBalance(null, null, null));
|
|
var inferredBalance = JsonDocument.Parse(JsonSerializer.Serialize(inferred.Value)).RootElement;
|
|
Assert.Equal(92, inferredBalance.GetProperty("AccountId").GetInt64());
|
|
Assert.Equal(string.Empty, inferredBalance.GetProperty("CurrencyId").GetString());
|
|
}
|
|
|
|
private static void SetUserAndJsonBody(Controller controller, long accountId, string json)
|
|
{
|
|
var identity = new ClaimsIdentity("TestAuth");
|
|
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, accountId.ToString()));
|
|
var request = new DefaultHttpContext { User = new ClaimsPrincipal(identity) }.Request;
|
|
request.ContentType = "application/json";
|
|
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json));
|
|
request.ContentLength = request.Body.Length;
|
|
controller.ControllerContext = new ControllerContext { HttpContext = request.HttpContext };
|
|
}
|
|
}
|