test(headless): S3 end-to-end HeadlessProcessHost console proof
Existing coverage exercises HeadlessSessionHost.SubmitConsoleLine directly (bypassing the background reader thread and the scheduler) or HeadlessConsoleController in isolation (a fake submit callback, no real session). Add one test that drives the actual production wiring: a plain StringReader feeds "hello" and "/quit" through the real HeadlessProcessHost constructor (background reader thread -> per-tick ConsolePump -> ChatCommandRouter.Submit -> the wire), asserting the outbound Talk action reaches the fixture AND that /quit ends RunAsync with HeadlessExitCode.Success -- the same graceful path an external cancellation takes. Mutation: removed the session.ConsolePump assignment in HeadlessProcessHost's constructor (never wiring the drain+pump delegate). The test failed with a TimeoutException -- the queued console lines were never drained, so /quit's cancellation never fired and RunAsync ran until the test's own 10s WaitAsync bound. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
314cb5f4a5
commit
70e86c180e
1 changed files with 45 additions and 0 deletions
|
|
@ -524,6 +524,51 @@ public sealed class HeadlessConsoleTests
|
|||
|
||||
// ── HeadlessProcessHost: end-to-end console wiring ───────────────────
|
||||
|
||||
/// <summary>
|
||||
/// S3: an end-to-end proof that a console line, read from a plain
|
||||
/// <see cref="StringReader"/>, reaches the real session's
|
||||
/// <c>SubmitConsoleLine</c> pipeline through the actual
|
||||
/// <see cref="HeadlessProcessHost"/> wiring (background reader thread →
|
||||
/// per-tick <c>ConsolePump</c> → <c>ChatCommandRouter.Submit</c> → the
|
||||
/// wire), and that <c>/quit</c> ends <see cref="HeadlessProcessHost.RunAsync"/>
|
||||
/// through the SAME graceful path an external cancellation takes —
|
||||
/// <see cref="HeadlessExitCode.Success"/>, not an error code.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task ConsoleLineReachesTheSessionAndQuitEndsTheProcessGracefully()
|
||||
{
|
||||
var captured = new List<byte[]>();
|
||||
var operations = new FixtureSessionOperations
|
||||
{
|
||||
GameActionCapture = body => captured.Add(body),
|
||||
};
|
||||
var configuration = new HeadlessConfiguration
|
||||
{
|
||||
Version = 1,
|
||||
Sessions = [Descriptor()],
|
||||
};
|
||||
using var diagnostics = new StringWriter();
|
||||
using var input = new System.IO.StringReader(
|
||||
"hello" + Environment.NewLine + "/quit" + Environment.NewLine);
|
||||
using var host = new HeadlessProcessHost(
|
||||
configuration,
|
||||
HeadlessPathSet.Resolve(new HeadlessPathOverrides()),
|
||||
input,
|
||||
diagnostics,
|
||||
operations,
|
||||
new FakeTimeProvider(),
|
||||
directCredentials: new HeadlessDirectCredentials("account", "password"),
|
||||
consoleEnabled: true);
|
||||
|
||||
HeadlessExitCode exitCode = await host.RunAsync(CancellationToken.None)
|
||||
.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.Equal(HeadlessExitCode.Success, exitCode);
|
||||
byte[] body = Assert.Single(captured);
|
||||
Assert.Equal(ChatRequests.TalkOpcode, ActionOpcode(body));
|
||||
Assert.Equal("hello", TalkText(body));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// S6: <c>standardOutputIsTerminal</c> is threaded in as a constructor
|
||||
/// parameter, not read from the real <c>System.Console</c> inside
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue