Review fixes: logoff inventory, socket stop, connect order, relog leftovers
F2: the logoff inventory is skipped when the host already let go of the items (it may reset the session before reporting the logoff): an empty list would have emptied the backend's inventory and overwritten the saved copy. F3/F4: each socket start owns its run; Stop no longer blocks the game thread and gives the run up to ten seconds to send what was queued (a large logoff inventory) before closing, flushing that run's own send loop; a relog never hears the previous run's frames. F7: on connect, share_subscribe goes out before the first telemetry frame, as the original sent them. F8: a rare announcement still waiting at logoff is dropped rather than sent as the next character. F9: each dungeon map is sent once per login, as the original (reloaded per login) did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
71f14473c6
commit
aec0ddb8d7
8 changed files with 223 additions and 82 deletions
|
|
@ -93,6 +93,44 @@ public sealed class BackendClientTests
|
|||
Assert.Equal(1, server.Connections);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Stop_sends_what_was_queued_before_it_closes_without_blocking()
|
||||
{
|
||||
using var server = new TestSocketServer();
|
||||
using var client = new BackendClient(new RecordingLogger());
|
||||
client.Start(server.Uri, "", "Mossy", () => Connected);
|
||||
server.NextFrame();
|
||||
Assert.True(await WaitAsync(() => client.IsConnected));
|
||||
|
||||
string big = "{\"type\":\"full_inventory\",\"pad\":\"" + new string('x', 400_000) + "\"}";
|
||||
client.Send(big);
|
||||
client.Send("{\"type\":\"share_unsubscribe\"}");
|
||||
var watch = System.Diagnostics.Stopwatch.StartNew();
|
||||
client.Stop();
|
||||
Assert.True(watch.ElapsedMilliseconds < 200, $"Stop blocked for {watch.ElapsedMilliseconds} ms");
|
||||
|
||||
Assert.Equal(big, server.NextFrame());
|
||||
Assert.Equal("{\"type\":\"share_unsubscribe\"}", server.NextFrame());
|
||||
await client.LastStop.WaitAsync(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_new_start_right_after_stop_hears_only_its_own_connection()
|
||||
{
|
||||
using var server = new TestSocketServer();
|
||||
using var client = new BackendClient(new RecordingLogger());
|
||||
client.Start(server.Uri, "", "Mossy", () => Connected);
|
||||
server.NextFrame();
|
||||
Assert.True(await WaitAsync(() => client.IsConnected));
|
||||
client.Stop();
|
||||
|
||||
client.Start(server.Uri, "", "Horan", () => Connected);
|
||||
|
||||
Assert.Equal("{\"type\":\"register\",\"player_name\":\"Horan\"}", server.NextFrame(8000));
|
||||
Assert.True(await WaitAsync(() => client.TryTakeConnection(out _)));
|
||||
Assert.False(client.TryTakeConnection(out _));
|
||||
}
|
||||
|
||||
internal static async Task<bool> WaitAsync(Func<bool> condition, int timeoutMs = 5000)
|
||||
{
|
||||
for (int waited = 0; waited < timeoutMs; waited += 20)
|
||||
|
|
|
|||
|
|
@ -223,6 +223,29 @@ public sealed class InventoryFeatureTests
|
|||
Assert.Single(harness.FramesOfTypeRaw("full_inventory"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Logoff_after_the_host_let_go_of_the_items_sends_nothing_and_keeps_the_saved_copy()
|
||||
{
|
||||
// The host may reset the session before it reports the logoff; an
|
||||
// empty list then would wipe the backend's inventory.
|
||||
using var harness = new PluginHarness("Mossy", arrange: host => Own(host, Tapers()));
|
||||
LoginAndSettle(harness);
|
||||
harness.Tick(0.1);
|
||||
harness.Tick(0.1);
|
||||
harness.Plugin.OnLogoff();
|
||||
string? saved = harness.Host.Storage.ReadText("inventory.json");
|
||||
Assert.NotNull(saved);
|
||||
harness.Plugin.OnLogin();
|
||||
LoginAndSettle(harness);
|
||||
harness.Frames.Clear();
|
||||
harness.Host.Automation.Items.Owned.Clear();
|
||||
|
||||
harness.Plugin.OnLogoff();
|
||||
|
||||
Assert.Empty(harness.FramesOfTypeRaw("full_inventory"));
|
||||
Assert.Equal(saved, harness.Host.Storage.ReadText("inventory.json"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_taper_count_follows_the_stacks_in_the_packs()
|
||||
{
|
||||
|
|
|
|||
37
tests/OpenAC.MosswartMassacre.Tests/ReviewFixTests.cs
Normal file
37
tests/OpenAC.MosswartMassacre.Tests/ReviewFixTests.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
namespace OpenAC.MosswartMassacre.Tests;
|
||||
|
||||
/// <summary>Pins for the fixes a review of the port asked for.</summary>
|
||||
public sealed class ReviewFixTests
|
||||
{
|
||||
[Fact]
|
||||
public void On_connect_the_sharing_subscribe_goes_out_before_the_first_telemetry()
|
||||
{
|
||||
using var harness = new PluginHarness("Mossy");
|
||||
harness.Login();
|
||||
harness.Context.Settings.Current.VitalSharingEnabled = true;
|
||||
harness.Frames.Clear();
|
||||
|
||||
harness.SimulateConnected();
|
||||
|
||||
string[] types = [.. harness.Frames.Select(f => (string)Newtonsoft.Json.Linq.JObject.Parse(f)["type"]!)];
|
||||
Assert.Equal(["share_subscribe", "telemetry"], types.Take(2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_rare_announcement_still_waiting_at_logoff_is_dropped()
|
||||
{
|
||||
using var harness = new PluginHarness("Mossy");
|
||||
harness.Login();
|
||||
harness.Host.Automation.Chat.Deliver("Mossy has discovered the Pearl of Blood Drinking!", kind: 4);
|
||||
harness.Host.Automation.Chat.Submitted.Clear();
|
||||
|
||||
harness.Plugin.OnLogoff();
|
||||
harness.Host.Automation.Character.Name = "Horan";
|
||||
harness.Plugin.OnLogin();
|
||||
harness.Host.Automation.Chat.Submitted.Clear();
|
||||
harness.Run(4);
|
||||
|
||||
Assert.DoesNotContain(harness.Host.Automation.Chat.Submitted, line => line.StartsWith("/a ", StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue