Update .gitignore to exclude TypeScript build info and add dist directory. Expand README with project overview, technology stack, prerequisites, and instructions for running and testing the application.
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
namespace HSchool.Protocol.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// The wire format is a contract with the browser client. Round-trips prove the C# side is
|
||||
/// self-consistent; the explicit byte-layout tests are what keeps
|
||||
/// <c>src/HSchool.Client/src/net/protocol.ts</c> honest.
|
||||
/// </summary>
|
||||
public class ProtocolCodecTests
|
||||
{
|
||||
[Fact]
|
||||
public void Hello_RoundTrips()
|
||||
{
|
||||
var message = new ClientHelloMessage(ProtocolConstants.Version, "ada");
|
||||
Span<byte> buffer = stackalloc byte[64];
|
||||
|
||||
var length = ProtocolCodec.WriteHello(buffer, message);
|
||||
|
||||
Assert.Equal(message, ProtocolCodec.ReadHello(buffer[..length]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Input_RoundTrips()
|
||||
{
|
||||
var message = new ClientInputMessage(0x01020304, InputButtons.Up | InputButtons.Right);
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
|
||||
var length = ProtocolCodec.WriteInput(buffer, message);
|
||||
|
||||
Assert.Equal(6, length);
|
||||
Assert.Equal(message, ProtocolCodec.ReadInput(buffer[..length]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ping_RoundTrips()
|
||||
{
|
||||
var message = new ClientPingMessage(1_700_000_000_123);
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
|
||||
var length = ProtocolCodec.WritePing(buffer, message);
|
||||
|
||||
Assert.Equal(9, length);
|
||||
Assert.Equal(message, ProtocolCodec.ReadPing(buffer[..length]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Welcome_RoundTripsAndIsFifteenBytes()
|
||||
{
|
||||
var message = new ServerWelcomeMessage(ProtocolConstants.Version, 42, 20, 1600f, 900f);
|
||||
Span<byte> buffer = stackalloc byte[32];
|
||||
|
||||
var length = ProtocolCodec.WriteWelcome(buffer, message);
|
||||
|
||||
Assert.Equal(15, length);
|
||||
Assert.Equal(message, ProtocolCodec.ReadWelcome(buffer[..length]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pong_RoundTrips()
|
||||
{
|
||||
var message = new ServerPongMessage(5, 99);
|
||||
Span<byte> buffer = stackalloc byte[32];
|
||||
|
||||
var length = ProtocolCodec.WritePong(buffer, message);
|
||||
|
||||
Assert.Equal(13, length);
|
||||
Assert.Equal(message, ProtocolCodec.ReadPong(buffer[..length]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Snapshot_RoundTripsEveryEntityField()
|
||||
{
|
||||
ReadOnlySpan<EntitySnapshot> entities =
|
||||
[
|
||||
new EntitySnapshot(7, EntityKind.Player, 100f, 200f, 18f, 0x4CC9F0),
|
||||
new EntitySnapshot(8, EntityKind.Obstacle, 800f, 450f, 70f, 0x3A4553),
|
||||
];
|
||||
|
||||
var buffer = new byte[ProtocolCodec.SnapshotSize(entities.Length)];
|
||||
var length = ProtocolCodec.WriteSnapshot(buffer, 1234, entities);
|
||||
|
||||
Assert.Equal(buffer.Length, length);
|
||||
|
||||
var decoded = new EntitySnapshot[entities.Length];
|
||||
var count = ProtocolCodec.ReadSnapshot(buffer, decoded, out var tick);
|
||||
|
||||
Assert.Equal(entities.Length, count);
|
||||
Assert.Equal(1234u, tick);
|
||||
Assert.Equal(entities[0], decoded[0]);
|
||||
Assert.Equal(entities[1], decoded[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SnapshotSize_MatchesTheLayoutTheClientAssumes()
|
||||
{
|
||||
// 1 type + 4 tick + 2 count, then 21 bytes per entity.
|
||||
Assert.Equal(7, ProtocolCodec.SnapshotSize(0));
|
||||
Assert.Equal(7 + 21, ProtocolCodec.SnapshotSize(1));
|
||||
Assert.Equal(21, ProtocolConstants.EntitySnapshotSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numbers_AreLittleEndian()
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
var length = ProtocolCodec.WriteInput(buffer, new ClientInputMessage(0x01020304, InputButtons.None));
|
||||
|
||||
Assert.Equal((byte)MessageType.ClientInput, buffer[0]);
|
||||
Assert.Equal(new byte[] { 0x04, 0x03, 0x02, 0x01 }, buffer[1..5].ToArray());
|
||||
Assert.Equal(6, length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PeekMessageType_ReadsTheFirstByte()
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
ProtocolCodec.WritePing(buffer, new ClientPingMessage(1));
|
||||
|
||||
Assert.Equal(MessageType.ClientPing, ProtocolCodec.PeekMessageType(buffer));
|
||||
Assert.Equal(MessageType.None, ProtocolCodec.PeekMessageType([]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TruncatedFrame_Throws()
|
||||
{
|
||||
byte[] frame = [(byte)MessageType.ServerWelcome, ProtocolConstants.Version];
|
||||
|
||||
Assert.Throws<ProtocolException>(() => ProtocolCodec.ReadWelcome(frame));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WrongMessageId_Throws()
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
var length = ProtocolCodec.WritePing(buffer, new ClientPingMessage(1));
|
||||
var frame = buffer[..length].ToArray();
|
||||
|
||||
Assert.Throws<ProtocolException>(() => ProtocolCodec.ReadInput(frame));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OversizedName_Throws()
|
||||
{
|
||||
var message = new ClientHelloMessage(ProtocolConstants.Version, new string('x', 100));
|
||||
var buffer = new byte[256];
|
||||
|
||||
Assert.Throws<ProtocolException>(() => ProtocolCodec.WriteHello(buffer, message));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UndersizedBuffer_Throws()
|
||||
{
|
||||
var message = new ServerWelcomeMessage(ProtocolConstants.Version, 1, 20, 1f, 1f);
|
||||
var buffer = new byte[4];
|
||||
|
||||
Assert.Throws<ProtocolException>(() => ProtocolCodec.WriteWelcome(buffer, message));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user