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,160 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using RadicadeServer.API.Notifications;
|
||||
using RadicadeServer.Clubs;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Enums.Messages;
|
||||
using RadicadeServer.RecNet.Notifications;
|
||||
using Xunit;
|
||||
|
||||
namespace RadicadeServer.Tests;
|
||||
|
||||
public class WatchWireCompatibilityTests
|
||||
{
|
||||
// Notifs and NotificationType carry the same values as the client's own enum
|
||||
// (RelationshipChanged = 1, MessageReceived = 2), so both must reach the wire the same way.
|
||||
[Theory]
|
||||
[InlineData(Notifs.RelationshipChanged, "1")]
|
||||
[InlineData(Notifs.MessageReceived, "2")]
|
||||
public void FriendNotificationUsesNumericStringIdWithNumericPayload(Notifs eventType, string expectedId)
|
||||
{
|
||||
var wire = new Notification
|
||||
{
|
||||
Id = eventType,
|
||||
Msg = new { Type = MessageType.FriendInvite, AccountId = 2 }
|
||||
}.ToJson();
|
||||
using var json = JsonDocument.Parse(wire);
|
||||
Assert.Equal(expectedId, json.RootElement.GetProperty("Id").GetString());
|
||||
Assert.Equal(4, json.RootElement.GetProperty("Msg").GetProperty("Type").GetInt32());
|
||||
Assert.Equal(2, json.RootElement.GetProperty("Msg").GetProperty("AccountId").GetInt32());
|
||||
}
|
||||
|
||||
// The 2023 client requires a STRING at the notification Id field AND dispatches on the
|
||||
// number inside it. A quoted number is the only form that satisfies both: a bare number
|
||||
// throws in the client's deserializer ("expected:'String Begin Token', actual:'1'"), and
|
||||
// the enum's name parses but matches no registered handler, so the frame is ignored.
|
||||
[Theory]
|
||||
[InlineData(NotificationType.GiftPackageReceivedImmediate, 31)]
|
||||
[InlineData(NotificationType.SubscriptionUpdatePresence, 12)]
|
||||
[InlineData(NotificationType.ModerationKick, 22)]
|
||||
[InlineData(NotificationType.MessageReceived, 2)]
|
||||
[InlineData(NotificationType.RefreshLogin, 5)]
|
||||
public void NotificationIdIsTheClientEnumNumberAsAString(NotificationType eventType, int expectedId)
|
||||
{
|
||||
using var json = JsonDocument.Parse(new Notification { Id = eventType, Msg = new { AccountId = 2 } }.ToJson());
|
||||
var id = json.RootElement.GetProperty("Id");
|
||||
Assert.Equal(JsonValueKind.String, id.ValueKind);
|
||||
Assert.Equal(expectedId.ToString(CultureInfo.InvariantCulture), id.GetString());
|
||||
Assert.Equal((int)eventType, expectedId);
|
||||
Assert.Equal(2, json.RootElement.GetProperty("Msg").GetProperty("AccountId").GetInt32());
|
||||
}
|
||||
|
||||
// Both halves of the contract, stated as the client would experience them: the name is
|
||||
// never what goes out, and the number is never sent bare.
|
||||
[Theory]
|
||||
[InlineData(NotificationType.MessageReceived, "2", "MessageReceived")]
|
||||
[InlineData(NotificationType.ModerationKick, "22", "ModerationKick")]
|
||||
public void NotificationIdIsNeitherTheNameNorABareNumber(NotificationType eventType, string expectedNumber, string name)
|
||||
{
|
||||
var notification = new Notification { Id = eventType, Msg = new { } };
|
||||
var wire = notification.ToJson();
|
||||
using var json = JsonDocument.Parse(wire);
|
||||
Assert.NotEqual(JsonValueKind.Number, json.RootElement.GetProperty("Id").ValueKind);
|
||||
// Through the property, exactly as the push log does it: the log has to show the token
|
||||
// the client will parse, not what a boxed enum's ToString() happens to be.
|
||||
Assert.Equal($"\"{expectedNumber}\"", Notify.WireId(notification.Id));
|
||||
Assert.DoesNotContain($"\"{name}\"", wire);
|
||||
}
|
||||
|
||||
// An explicit string id set by a caller is passed through untouched.
|
||||
[Fact]
|
||||
public void ExplicitStringNotificationIdIsUnchanged()
|
||||
{
|
||||
using var json = JsonDocument.Parse(new Notification { Id = "ReputationUpdate", Msg = new { } }.ToJson());
|
||||
Assert.Equal("ReputationUpdate", json.RootElement.GetProperty("Id").GetString());
|
||||
}
|
||||
|
||||
// These ids are NOT members of the client's enum, so they are named events and must keep
|
||||
// travelling as strings (the reference server sends exactly these spellings).
|
||||
[Theory]
|
||||
[InlineData("PresenceUpdate")]
|
||||
[InlineData("AccountUpdate")]
|
||||
[InlineData("SelfAccountUpdate")]
|
||||
[InlineData("ReputationUpdate")]
|
||||
public void NonEnumNotificationIdStaysAString(string eventName)
|
||||
{
|
||||
using var json = JsonDocument.Parse(new Notification { Id = eventName, Msg = new { AccountId = 2 } }.ToJson());
|
||||
Assert.Equal(JsonValueKind.String, json.RootElement.GetProperty("Id").ValueKind);
|
||||
Assert.Equal(eventName, json.RootElement.GetProperty("Id").GetString());
|
||||
}
|
||||
|
||||
// The push log prints the id with the same token the client will parse, so the server log
|
||||
// shows the frame's real shape. A boxed enum prints its NAME through ToString() no matter
|
||||
// how the frame was serialized, which is exactly why a log line reading "ModerationKick"
|
||||
// looked healthy while every frame went out unparseable.
|
||||
[Theory]
|
||||
[InlineData(NotificationType.ModerationKick, "22")]
|
||||
[InlineData(NotificationType.MessageReceived, "2")]
|
||||
[InlineData(NotificationType.RefreshLogin, "5")]
|
||||
public void PushLogShowsTheWireId(NotificationType eventType, string expected)
|
||||
=> Assert.Equal(expected, Notify.WireId(eventType));
|
||||
|
||||
[Theory]
|
||||
[InlineData("PresenceUpdate")]
|
||||
[InlineData("AccountUpdate")]
|
||||
[InlineData("SelfAccountUpdate")]
|
||||
public void PushLogQuotesANamedId(string eventName)
|
||||
=> Assert.Equal($"\"{eventName}\"", Notify.WireId(eventName));
|
||||
|
||||
// The diagnostic A/B (/diag/notify) has to be able to produce all three forms, because the
|
||||
// point of it is to show the live client accepting one and rejecting the other two.
|
||||
[Fact]
|
||||
public void IdFormJsonProducesTheCanonicalQuotedNumberByDefault()
|
||||
{
|
||||
Assert.Equal("\"22\"", Notify.IdFormJson(null, NotificationType.ModerationKick));
|
||||
Assert.Equal("\"22\"", Notify.IdFormJson("string", NotificationType.ModerationKick));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("number", "22")]
|
||||
[InlineData("name", "\"ModerationKick\"")]
|
||||
public void IdFormJsonCanProduceTheFormsTheClientRejects(string form, string expected)
|
||||
=> Assert.Equal(expected, Notify.IdFormJson(form, NotificationType.ModerationKick));
|
||||
|
||||
// The A/B envelope keeps the real Msg bytes and only swaps the Id token, so what the client
|
||||
// is reacting to is genuinely just the id's form.
|
||||
[Fact]
|
||||
public void DiagnosticEnvelopeSwapsOnlyTheIdToken()
|
||||
{
|
||||
var message = new Notification
|
||||
{
|
||||
Id = NotificationType.MessageReceived,
|
||||
Msg = new { Type = MessageType.CoachMessage, Data = "hello" }
|
||||
};
|
||||
|
||||
using var json = JsonDocument.Parse(Notify.EnvelopeWithIdForm(message, "\"2\""));
|
||||
Assert.Equal("2", json.RootElement.GetProperty("Id").GetString());
|
||||
Assert.Equal(100, json.RootElement.GetProperty("Msg").GetProperty("Type").GetInt32());
|
||||
Assert.Equal("hello", json.RootElement.GetProperty("Msg").GetProperty("Data").GetString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(2, 100)]
|
||||
[InlineData(3, 10)]
|
||||
[InlineData(0, 0)]
|
||||
public void ClubMembershipUsesClientEnumValues(long viewerId, int expected)
|
||||
{
|
||||
var club = new ClubRecord
|
||||
{
|
||||
clubId = 1,
|
||||
ownerPlayerId = 2,
|
||||
members = viewerId == 3
|
||||
? [new ClubMemberRecord { accountId = viewerId, membershipType = ClubMembershipTypes.Member }]
|
||||
: []
|
||||
};
|
||||
using var details = JsonDocument.Parse(JsonSerializer.Serialize(ClubStore.BuildClubDetails(club, viewerId)));
|
||||
using var membership = JsonDocument.Parse(JsonSerializer.Serialize(ClubStore.BuildMembership(club, viewerId)));
|
||||
Assert.Equal(expected, details.RootElement.GetProperty("MyMembershipType").GetInt32());
|
||||
Assert.Equal(expected, membership.RootElement.GetProperty("membershipType").GetInt32());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user