using OpenAC.MosswartMassacre.Tests.Fakes;
namespace OpenAC.MosswartMassacre.Tests.Backend;
///
/// The socket's status and error lines reach the player's chat, worded as the
/// original worded them, and only through the tick: the socket thread never
/// writes to chat itself.
///
public sealed class BackendChatStatusTests
{
private const string P = "[Mosswart Massacre] ";
[Fact]
public async Task Enable_shows_connecting_then_connected_and_registered_on_the_next_tick()
{
using var server = new TestSocketServer();
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Context.Settings.Current.BackendUrl = server.Uri.ToString();
harness.Context.Commands.Dispatch("ws enable");
Assert.Equal([P + "[WebSocket] connecting…", P + "WS streaming ENABLED."], Tail(harness, 2));
server.NextFrame();
Assert.True(await BackendClientTests.WaitAsync(() => harness.Context.Backend.IsConnected));
Assert.DoesNotContain(P + "[WebSocket] CONNECTED", harness.ChatLines);
int before = harness.ChatLines.Count;
int tickThread = Environment.CurrentManagedThreadId;
harness.Tick(0.01);
Assert.Equal(
[P + "[WebSocket] CONNECTED", P + "[WebSocket] REGISTERED", P + "[WebSocket] Starting telemetry loop"],
Tail(harness, 3));
AssertPostedOn(harness, before, tickThread);
}
[Fact]
public async Task Streaming_saved_on_connects_at_login_and_says_so()
{
using var server = new TestSocketServer();
using var harness = new PluginHarness("Mossy");
harness.Context.Settings.Current.WebSocketEnabled = true;
harness.Context.Settings.Current.BackendUrl = server.Uri.ToString();
harness.Context.Settings.Save();
harness.Host.Automation.Character.IsInWorld = true;
harness.Plugin.OnLogin();
Assert.Contains(P + "[WebSocket] connecting…", harness.ChatLines);
Assert.Equal("{\"type\":\"register\",\"player_name\":\"Mossy\"}", server.NextFrame());
Assert.True(await BackendClientTests.WaitAsync(() => harness.Context.Backend.IsConnected));
harness.Tick(0.01);
Assert.Contains(P + "[WebSocket] REGISTERED", harness.ChatLines);
}
[Fact]
public async Task A_failed_connect_shows_the_error_cleanup_and_reconnect_on_the_next_tick()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
// Nothing listens on port 1: the connect is refused.
harness.Context.Settings.Current.BackendUrl = "ws://127.0.0.1:1/websocket/";
var log = (RecordingLogger)harness.Host.Log;
harness.Context.Commands.Dispatch("ws enable");
Assert.True(await BackendClientTests.WaitAsync(
() => log.Any(line => line.Contains("[WebSocket] Reconnecting in 2 seconds...")), 15000));
Assert.DoesNotContain(harness.ChatLines, line => line.Contains("Connection error"));
int before = harness.ChatLines.Count;
int tickThread = Environment.CurrentManagedThreadId;
harness.Tick(0.01);
string[] tail = Tail(harness, 3);
Assert.StartsWith(P + "[WebSocket] Connection error: ", tail[0]);
Assert.StartsWith(P + "[WebSocket] Cleaning up connection - Final state: ", tail[1]);
Assert.Equal(P + "[WebSocket] Reconnecting in 2 seconds...", tail[2]);
AssertPostedOn(harness, before, tickThread);
}
[Fact]
public async Task Disable_shows_disabled_before_the_command_reply()
{
using var server = new TestSocketServer();
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Context.Settings.Current.BackendUrl = server.Uri.ToString();
harness.Context.Commands.Dispatch("ws enable");
server.NextFrame();
Assert.True(await BackendClientTests.WaitAsync(() => harness.Context.Backend.IsConnected));
harness.Tick(0.01);
harness.Context.Commands.Dispatch("ws disable");
Assert.Equal([P + "[WebSocket] DISABLED", P + "WS streaming DISABLED."], Tail(harness, 2));
}
[Fact]
public void A_reconnect_with_sharing_on_says_it_re_subscribed_even_without_verbose_logging()
{
using var harness = new PluginHarness("Mossy");
harness.Login();
harness.Context.Settings.Current.VitalSharingEnabled = true;
harness.Context.Settings.Current.VerboseLogging = false;
harness.SimulateConnected();
Assert.Equal(
[P + "[WebSocket] Vital sharing re-subscribed", P + "[WebSocket] Starting telemetry loop"],
Tail(harness, 2));
}
/// Every chat line from on was written on , the thread that ran the tick.
private static void AssertPostedOn(PluginHarness harness, int from, int threadId) =>
Assert.All(harness.Host.Automation.Chat.PostedThreadIds.Skip(from), id => Assert.Equal(threadId, id));
private static string[] Tail(PluginHarness harness, int count) =>
[.. harness.ChatLines.Skip(Math.Max(0, harness.ChatLines.Count - count))];
}