fix(launcher): close Campaign LA LA1 review findings

This commit is contained in:
Erik 2026-08-14 17:00:09 +02:00
parent 4edc122085
commit d511e4c348
12 changed files with 413 additions and 65 deletions

View file

@ -62,6 +62,73 @@ public sealed class HeadlessSessionHostTests
Assert.DoesNotContain("AcDream.App", diagnostics);
}
/// <summary>
/// Campaign LA LA1 review fixes F3/F6: reconnect is a visible lifecycle
/// replacement, so the retiring connection must publish disconnected
/// before the new connection publishes connected. Its reason is distinct
/// from the final host stop and from the terminal process outcome.
/// </summary>
[Fact]
public void ReconnectPublishesDisconnectedBeforeTheSecondConnectedEdge()
{
string statusPath = Path.Combine(
Path.GetTempPath(),
$"acdream-headless-reconnect-status-{Guid.NewGuid():N}.jsonl");
try
{
var operations = new FixtureSessionOperations();
using var diagnosticsOutput = new StringWriter();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(statusFile: statusPath),
credential,
new HeadlessDiagnosticWriter(diagnosticsOutput),
operations);
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Reconnect().Status);
host.Dispose();
JsonElement[] events = File.ReadAllLines(statusPath)
.Select(static line => JsonDocument.Parse(line).RootElement.Clone())
.ToArray();
Assert.Equal(
[
"started", "connected", "characterList", "enteredWorld",
"disconnected", "connected", "characterList", "enteredWorld",
"disconnected", "exited",
],
events.Select(static item => item.GetProperty("e").GetString()));
JsonElement[] disconnected = events
.Where(static item =>
item.GetProperty("e").GetString() == "disconnected")
.ToArray();
Assert.Equal(2, disconnected.Length);
Assert.Equal(
"reconnect",
disconnected[0].GetProperty("reason").GetString());
Assert.Equal(
"stopped",
disconnected[1].GetProperty("reason").GetString());
JsonElement exited = events[^1];
Assert.Equal(0, exited.GetProperty("code").GetInt32());
Assert.Equal("graceful", exited.GetProperty("reason").GetString());
}
finally
{
if (File.Exists(statusPath))
File.Delete(statusPath);
}
}
/// <summary>
/// Campaign LA slice LA1: proves the status-event writer fires the
/// pinned lifecycle vocabulary — started/connected/characterList/