mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
157 lines
5.7 KiB
C#
157 lines
5.7 KiB
C#
using RadicadeServer.RecNet.Enums.Moderation;
|
|
using RadicadeServer.RecNet.Moderation;
|
|
using Xunit;
|
|
|
|
namespace RadicadeServer.Tests;
|
|
|
|
/// <summary>
|
|
/// Unit tests for the reputation system spec:
|
|
/// - every player starts at ReputationScore = 10
|
|
/// - Noteriety starts at 0 and grows +0.5 per cheer / subscriber received
|
|
/// - bans drop reputation: -0.5 per ban, -1 per cheating ban, -4 per permanent ban (floored at 0)
|
|
/// - Trust label: Trusted (8-10), Neutral (6-7), Not Trusted (0-5)
|
|
/// - reports increment ReportCount, which never feeds Noteriety (so reports can't lock
|
|
/// the client's room-comment / club-creation features)
|
|
/// </summary>
|
|
public class ReputationTests
|
|
{
|
|
[Fact]
|
|
public void CheerCategory_MatchesTheClientsLadder()
|
|
{
|
|
// Copied off the client's own enum in dump.cs (and matching the archived 2023
|
|
// servers FemRec and Rubidium). Numbering them sequentially silently broke every
|
|
// cheer except General: none of 10/20/30/40 matched a switch arm.
|
|
Assert.Equal(0, (int)CheerCategory.General);
|
|
Assert.Equal(10, (int)CheerCategory.Helpful);
|
|
Assert.Equal(20, (int)CheerCategory.Sportsman);
|
|
Assert.Equal(30, (int)CheerCategory.GreatHost);
|
|
Assert.Equal(40, (int)CheerCategory.Creative);
|
|
// The badge only developers can equip. The client asks for it by this value, so
|
|
// leaving it out of the enum made the request look like an unknown category.
|
|
Assert.Equal(9000, (int)CheerCategory.RecRoomDeveloper);
|
|
}
|
|
|
|
[Fact]
|
|
public void NewReputation_StartsAtTen()
|
|
{
|
|
var rep = new Reputation { AccountId = 42 };
|
|
Assert.Equal(Reputation.DefaultReputation, rep.ReputationScore);
|
|
Assert.Equal(10.0, rep.ReputationScore);
|
|
Assert.Equal(0, rep.Noteriety);
|
|
Assert.Equal(0, rep.ReportCount);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(10.0, "Trusted")]
|
|
[InlineData(9.5, "Trusted")]
|
|
[InlineData(8.0, "Trusted")]
|
|
[InlineData(7.99, "Neutral")]
|
|
[InlineData(7.0, "Neutral")]
|
|
[InlineData(6.0, "Neutral")]
|
|
[InlineData(5.99, "Not Trusted")]
|
|
[InlineData(5.0, "Not Trusted")]
|
|
[InlineData(1.0, "Not Trusted")]
|
|
[InlineData(0.0, "Not Trusted")]
|
|
public void Trust_FollowsTheScoreBands(double score, string expected)
|
|
{
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = score };
|
|
Assert.Equal(expected, rep.Trust);
|
|
}
|
|
|
|
[Fact]
|
|
public void NormalBan_DropsReputationByHalf()
|
|
{
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = 10 };
|
|
rep.ApplyBanPenalty(permanent: false, cheating: false);
|
|
Assert.Equal(9.5, rep.ReputationScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void CheatingBan_DropsReputationByOne()
|
|
{
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = 10 };
|
|
rep.ApplyBanPenalty(permanent: false, cheating: true);
|
|
Assert.Equal(9.0, rep.ReputationScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void PermanentBan_DropsReputationByFour()
|
|
{
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = 10 };
|
|
rep.ApplyBanPenalty(permanent: true, cheating: false);
|
|
Assert.Equal(6.0, rep.ReputationScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void CheatingPermanentBan_AppliesTheLargerPenalty()
|
|
{
|
|
// A cheating offense that escalates to permanent takes the permanent (-4) penalty.
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = 10 };
|
|
rep.ApplyBanPenalty(permanent: true, cheating: true);
|
|
Assert.Equal(6.0, rep.ReputationScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void RepeatedBans_NeverPushReputationNegative()
|
|
{
|
|
var rep = new Reputation { AccountId = 1, ReputationScore = 1.0 };
|
|
rep.ApplyBanPenalty(permanent: true, cheating: true); // -4
|
|
rep.ApplyBanPenalty(permanent: false, cheating: false); // -0.5
|
|
Assert.Equal(0.0, rep.ReputationScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddCheer_GrowsNoterietyByHalfPerCheer()
|
|
{
|
|
var rep = new Reputation { AccountId = 1 };
|
|
rep.AddCheer(CheerCategory.General);
|
|
rep.AddCheer(CheerCategory.Creative);
|
|
Assert.Equal(1.0, rep.Noteriety); // 2 x 0.5
|
|
Assert.Equal(1, rep.CheerGeneral);
|
|
Assert.Equal(1, rep.CheerCreative);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSubscriber_GrowsNoterietyByHalf()
|
|
{
|
|
var rep = new Reputation { AccountId = 1 };
|
|
rep.AddSubscriber();
|
|
rep.AddSubscriber();
|
|
Assert.Equal(1.0, rep.Noteriety);
|
|
}
|
|
|
|
[Fact]
|
|
public void Reports_NeverTouchNoteriety()
|
|
{
|
|
// Simulates the report flow: ReportCount goes up, Noteriety stays a fame score.
|
|
var rep = new Reputation { AccountId = 1 };
|
|
for (var i = 0; i < 20; i++) rep.ReportCount += 1;
|
|
Assert.Equal(20, rep.ReportCount);
|
|
Assert.Equal(0.0, rep.Noteriety);
|
|
}
|
|
|
|
[Fact]
|
|
public void ForClient_PassesThroughScoreTrustAndReportCount()
|
|
{
|
|
var rep = new Reputation { AccountId = 7, ReputationScore = 6.5, ReportCount = 3, Noteriety = 1.0 };
|
|
var client = rep.ForClient();
|
|
|
|
Assert.Equal(7, client.AccountId);
|
|
Assert.Equal(6.5, client.ReputationScore);
|
|
Assert.Equal("Neutral", client.Trust);
|
|
Assert.Equal(3, client.ReportCount);
|
|
// Noteriety is normalized to a tiny 0-1 fraction so the client's moderation
|
|
// gates (room comments / club creation) never trip on it.
|
|
Assert.Equal(Math.Min(0.05, rep.Noteriety / 100.0), client.Noteriety);
|
|
Assert.True(client.Noteriety <= 0.05);
|
|
}
|
|
|
|
[Fact]
|
|
public void ForClient_NormalizesNoterietyButNeverAboveTheCeiling()
|
|
{
|
|
var rep = new Reputation { AccountId = 1, Noteriety = 5000 };
|
|
var client = rep.ForClient();
|
|
Assert.Equal(0.05, client.Noteriety);
|
|
}
|
|
}
|