Files
Radicade/ServerTests/AdminPanelFractionalReputationTests.cs
T
2026-10-16 23:33:13 -07:00

111 lines
3.4 KiB
C#

using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RadicadeServer.Admin;
using RadicadeServer.DB;
using RadicadeServer.RecNet.Auth;
using RadicadeServer.RecNet.Moderation;
using Xunit;
namespace RadicadeServer.Tests;
/// <summary>
/// PATCH admin/panel/players/{id} with the values the panel echoes back from its own GET.
///
/// Noteriety and ReputationScore are doubles on Reputation (NoterietyPerCheer is 0.5), so an account
/// that has been cheered carries a fractional value. The endpoint used to read them with GetInt32,
/// which throws System.FormatException on a fractional JSON number - the whole edit answered 500 and
/// saved nothing. Because only accounts with a fractional score hit it, it looked account-specific.
/// </summary>
public class AdminPanelFractionalReputationTests
{
private const string Secret = "test-bot-secret";
private const long Player = 3;
private static RecDBContext NewDb() => new(
TestDbOptions.Create()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options);
private static async Task<RecDBContext> WithPlayer()
{
var db = NewDb();
db.Profiles.Add(new Profile
{
accountId = Player,
username = "fractional",
displayName = "Fractional Player",
profileImage = ""
});
db.Reputations.Add(new Reputation
{
AccountId = Player,
Noteriety = 0.5,
ReputationScore = 7.5
});
await db.SaveChangesAsync();
return db;
}
private static async Task<IActionResult> Patch(RecDBContext db, string json)
{
var payload = JsonDocument.Parse(json).RootElement.Clone();
var api = new AdminPanelAPI(db);
var original = DBConfig.AdminApiKey;
DBConfig.AdminApiKey = Secret;
try
{
return await api.Update("players", Player, Secret, payload);
}
finally
{
DBConfig.AdminApiKey = original;
}
}
[Fact]
public async Task AFractionalNoterietyIsAcceptedAndStoredAsADouble()
{
using var db = await WithPlayer();
// Exactly what the panel sends for an account whose score is not a whole number.
var result = await Patch(db, """
{
"display_name": "Fractional Player",
"reputation": 9.5,
"noteriety": 1.5,
"cheer_general": 3
}
""");
Assert.IsType<OkObjectResult>(result);
var rep = await db.Reputations.SingleAsync(x => x.AccountId == Player);
Assert.Equal(1.5, rep.Noteriety);
Assert.Equal(9.5, rep.ReputationScore);
Assert.Equal(3, rep.CheerGeneral);
}
[Fact]
public async Task AnOutOfRangeOrNumericStringIntegerDoesNotThrow()
{
using var db = await WithPlayer();
// A value Int32 cannot hold, and an integer sent as a string, must both land (clamped /
// parsed) rather than 500 the edit.
var result = await Patch(db, """
{
"noteriety": 0.5,
"cheer_helpful": "4",
"cheer_creative": 9999999999999999999
}
""");
Assert.IsType<OkObjectResult>(result);
var rep = await db.Reputations.SingleAsync(x => x.AccountId == Player);
Assert.Equal(4, rep.CheerHelpful);
Assert.Equal(int.MaxValue, rep.CheerCreative);
}
}