acdream/tests/AcDream.Headless.Tests/HeadlessStaticStateAuditTests.cs
Erik f7a6f46ba0 fix(chat): consolidated-review fixes — retail /help Detail extraction, seam wiring test
SHOULD-FIX 1: RetailClientCommandCatalog's ~45 catalog leaf verbs were
showing acdream-authored Summary text for /help <verb> instead of
retail's own Detail_HelpType(2) text. Byte-swept every Help* handler
against the PDB-paired acclient.exe (verified MATCH), confirmed each
Detail/Summary branch by reading the actual decompiled if/else shape
(address order and string length both proved unreliable alone), and
fixed a sweep_weenie_strings.py 800-char truncation bug that silently
dropped several longer Detail branches. Resolved every ambiguous
CmdHashData-registered verb (hor/hr/hom/hoa/alh/ah/friends_add/
friends_remove/squelch/unsquelch) by reading for Binary Ninja's
nullptr-4th-arg decompiler artifact instead of trusting it. Coverage:
42 of 47 distinct catalog Definitions verbatim-extracted, 4
confirmed-null (index/clist/on/off register with a genuinely null help
pointer — DoHelp falls to UnknownCommand for these, now reproduced),
1 honest UNVERIFIED (messagetypes builds its text from a runtime enum
table, not a static string). ChatCommandRouter now prefers retail
Detail text over the catalog summary; RetailCommandHelpTable's class
doc no longer overclaims its own scope.

SHOULD-FIX 2: extracted the a5a7eb4f-class OnInterfaceText wiring into
a testable CreateChatViewModel method and added
ComposedChatViewModelWiresOnInterfaceTextToSpewBox, which the prior
FakeFactory-based test suite could never exercise.

SHOULD-FIX 3: retires register row AP-113. DoLifestone/DoMarketplace
print their own 0x1A refusal text (byte-recovered, UTF-16LE) instead
of falling through to the generic 0x26 fallback; ChatCommandRouter's
comment corrected to state the fallback's real scope.

SHOULD-FIX 4: corrected the divergence register's stale AP section
header sentence about AP-190's opacity default (refuted by cc582899).

NITs: (a) HeadlessStaticStateAudit routes through the injected
HeadlessDiagnosticWriter instead of Console.WriteLine; (b) a bounded
300-pump liveness diagnostic on the IsQuiescent conductor gate (no
retry, no behavior change); (c) fixed the #365 hydration test's doc
comment contradiction against diagnosis §8; (d) the 0x26 fallback
dispatches on WeenieErrorMessages' own Type instead of hardcoding
ClientLocal.

Full Release suite: 12,553 passed / 4 skipped / 0 failed (baseline
03404b71: 12,542/4/0; net +11 tests, zero regressions).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:22:36 +02:00

91 lines
3.1 KiB
C#

using AcDream.Core.Physics;
using AcDream.Headless.Configuration;
using AcDream.Headless.Diagnostics;
using AcDream.Headless.Hosting;
namespace AcDream.Headless.Tests;
/// <summary>
/// #365 Step 1: the audit's refusal rationale is multi-root attribution
/// ambiguity, which does not hold for a process that owns exactly one
/// session. These tests mutate <see cref="PhysicsDiagnostics"/> process-
/// global probe flags, so they run in their own non-parallel collection —
/// see <see cref="HeadlessStaticStateAuditCollection"/>. Consolidated-
/// review round (2026-08-10), NIT (a): the single-session log line now
/// routes through the injected <see cref="HeadlessDiagnosticWriter"/>
/// instead of <see cref="Console.Out"/>, so these tests capture its
/// backing <see cref="TextWriter"/> directly rather than swapping the
/// process-global console.
/// </summary>
[CollectionDefinition(
HeadlessStaticStateAuditCollection.Name,
DisableParallelization = true)]
public sealed class HeadlessStaticStateAuditCollection
{
public const string Name = "Headless static-state audit";
}
[Collection(HeadlessStaticStateAuditCollection.Name)]
public sealed class HeadlessStaticStateAuditTests : IDisposable
{
public HeadlessStaticStateAuditTests() => PhysicsDiagnostics.ResetForTest();
public void Dispose() => PhysicsDiagnostics.ResetForTest();
[Fact]
public void SingleSessionWithProbeEnabledIsAllowedAndLoggedLoudly()
{
PhysicsDiagnostics.ProbeParkEnabled = true;
using var captured = new StringWriter();
var diagnostics = new HeadlessDiagnosticWriter(captured);
HeadlessStaticStateAudit.ValidateProcessIsolation(
sessionCount: 1, diagnostics);
Assert.Contains(
nameof(PhysicsDiagnostics.ProbeParkEnabled),
captured.ToString(),
StringComparison.Ordinal);
}
[Fact]
public void SingleSessionWithNoProbesEnabledIsSilent()
{
using var captured = new StringWriter();
var diagnostics = new HeadlessDiagnosticWriter(captured);
HeadlessStaticStateAudit.ValidateProcessIsolation(
sessionCount: 1, diagnostics);
Assert.Equal(string.Empty, captured.ToString());
}
[Fact]
public void MultiSessionWithProbeEnabledStillThrowsNamingTheProbe()
{
PhysicsDiagnostics.ProbeParkEnabled = true;
var diagnostics = new HeadlessDiagnosticWriter(TextWriter.Null);
HeadlessConfigurationException exception = Assert.Throws<
HeadlessConfigurationException>(
() => HeadlessStaticStateAudit.ValidateProcessIsolation(
sessionCount: 2, diagnostics));
Assert.Contains(
nameof(PhysicsDiagnostics.ProbeParkEnabled),
exception.Message,
StringComparison.Ordinal);
}
[Fact]
public void MultiSessionWithNoProbesEnabledIsAllowed()
{
var diagnostics = new HeadlessDiagnosticWriter(TextWriter.Null);
Exception? exception = Record.Exception(
() => HeadlessStaticStateAudit.ValidateProcessIsolation(
sessionCount: 3, diagnostics));
Assert.Null(exception);
}
}