Show the backend socket's status and errors in chat, as the original did
Live, /mm ws enable printed nothing about the connection: the socket's connecting / CONNECTED / REGISTERED / error / reconnect / DISABLED lines went only to the host log, which the windowed client does not show. The original printed every one of them to chat. BackendClient now queues each status and error line (original wording) and still logs it; BackendSession drains the queue to chat on the tick, and right after a start or stop on the tick thread so "[WebSocket] connecting…" and "[WebSocket] DISABLED" come before the command's own reply, as in the original. The socket thread never writes to chat. The original's telemetry-loop and cleanup lines are emitted at the matching points, "Starting telemetry loop" comes from the telemetry listener, and "Vital sharing re-subscribed" is no longer verbose-only. No frame on the wire changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
336c5cfdf5
commit
28794299cb
7 changed files with 226 additions and 21 deletions
|
|
@ -0,0 +1,122 @@
|
|||
using OpenAC.MosswartMassacre.Tests.Fakes;
|
||||
|
||||
namespace OpenAC.MosswartMassacre.Tests.Backend;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>Every chat line from <paramref name="from"/> on was written on <paramref name="threadId"/>, the thread that ran the tick.</summary>
|
||||
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))];
|
||||
}
|
||||
|
|
@ -431,7 +431,14 @@ public sealed class RecordingChat : IPluginChat
|
|||
return message;
|
||||
}
|
||||
|
||||
public void PostSystemMessage(string text) => Posted.Add(text);
|
||||
/// <summary>The managed thread each <see cref="PostSystemMessage"/> ran on.</summary>
|
||||
public List<int> PostedThreadIds { get; } = [];
|
||||
|
||||
public void PostSystemMessage(string text)
|
||||
{
|
||||
Posted.Add(text);
|
||||
PostedThreadIds.Add(Environment.CurrentManagedThreadId);
|
||||
}
|
||||
|
||||
public void PostMessage(string text, int logTextType)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -295,12 +295,25 @@ public sealed class RecordingLogger : IPluginLogger
|
|||
{
|
||||
public List<string> Messages { get; } = [];
|
||||
|
||||
public void Info(string message) => Messages.Add("info: " + message);
|
||||
public void Info(string message) => Add("info: " + message);
|
||||
|
||||
public void Warn(string message) => Messages.Add("warn: " + message);
|
||||
public void Warn(string message) => Add("warn: " + message);
|
||||
|
||||
public void Error(string message, Exception? exception = null)
|
||||
=> Messages.Add("error: " + message);
|
||||
=> Add("error: " + message);
|
||||
|
||||
/// <summary>True when a logged line matches; safe while a background thread logs.</summary>
|
||||
public bool Any(Func<string, bool> match)
|
||||
{
|
||||
lock (Messages)
|
||||
return Messages.Any(match);
|
||||
}
|
||||
|
||||
private void Add(string line)
|
||||
{
|
||||
lock (Messages)
|
||||
Messages.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FakeGameState : IGameState
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue