openac-mosswartmassacre/tests/OpenAC.MosswartMassacre.Tests/Backend/BackendSessionTests.cs
Erik 5dbb294a96 Skeleton: plugin root, backend socket, /mm router, settings, test harness
The socket connects with the shared-secret header, registers first, reassembles
incoming frames of any size (the original dropped anything over 4 KiB) and
reconnects two seconds after a failure. Incoming share_* frames go to the
vital-sharing handlers; any other frame is a chat-box command for this
character, run one per tick through the chat bar as if typed. Frames are
written with Newtonsoft.Json defaults, the original's serializer, so the
backend sees the same bytes. The secret lives in the character's settings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:03:00 +02:00

107 lines
3.3 KiB
C#

using Newtonsoft.Json.Linq;
namespace OpenAC.MosswartMassacre.Tests.Backend;
public sealed class BackendSessionTests
{
[Fact]
public void A_command_for_this_character_runs_as_typed_on_the_next_tick()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Receive("{\"player_name\":\"mossy\",\"command\":\"/a hello\"}");
Assert.Empty(harness.Host.Automation.Chat.Submitted);
harness.Tick(0.01);
Assert.Equal(["/a hello"], harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void A_command_for_another_character_is_ignored()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Receive("{\"player_name\":\"Horan\",\"command\":\"/a hello\"}");
harness.Tick(0.01);
Assert.Empty(harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void Commands_run_one_per_tick_in_arrival_order()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Receive("{\"player_name\":\"Mossy\",\"command\":\"/one\"}");
harness.Receive("{\"player_name\":\"Mossy\",\"command\":\"/two\"}");
harness.Tick(0.01);
Assert.Equal(["/one"], harness.Host.Automation.Chat.Submitted);
harness.Tick(0.01);
Assert.Equal(["/one", "/two"], harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void Radar_commands_toggle_the_radar_instead_of_chat()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
var toggles = new List<bool>();
harness.Session.RadarToggle = on => toggles.Add(on);
harness.Receive("{\"player_name\":\"Mossy\",\"command\":\"start_radar\"}");
harness.Receive("{\"player_name\":\"Mossy\",\"command\":\"stop_radar\"}");
harness.Tick(0.01);
harness.Tick(0.01);
Assert.Equal([true, false], toggles);
Assert.Empty(harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void Share_frames_go_to_the_share_handlers_whoever_they_name()
{
using var harness = new PluginHarness("Mossy");
var seen = new List<string>();
harness.Session.AddShareHandler(new RecordingShareHandler(seen));
harness.Login();
harness.Receive("{\"type\":\"share_vital_update\",\"character_name\":\"Horan\"}");
Assert.Equal(["share_vital_update"], seen);
harness.Tick(0.01);
Assert.Empty(harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void Malformed_frames_are_skipped()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Receive("not json");
harness.Receive("[1,2,3]");
harness.Tick(0.01);
Assert.Empty(harness.Host.Automation.Chat.Submitted);
}
[Fact]
public void Connecting_sets_the_per_connection_session_id()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Session.OnConnected(new DateTime(2026, 9, 25, 8, 5, 9, DateTimeKind.Utc));
Assert.Equal("Mossy-20260925-080509", harness.Context.ConnectionSessionId);
}
private sealed class RecordingShareHandler(List<string> seen) : IShareFrameHandler
{
public void HandleShareFrame(string type, JObject frame) => seen.Add(type);
}
}