using AcDream.Core.Chat; using AcDream.Runtime.Chat; using AcDream.Runtime.Gameplay; namespace AcDream.Runtime.Tests.Chat; /// /// Owner direction 2026-09-07 (verbatim): "Unknown commands like /vt or /// stuff from plugins shall now go to the SpewBox. They should go to the /// chatbox." Register row AD-124 records the deviation from retail's own /// ClientLocal (0x1A) typing for exactly these two families. This file pins /// the half of that change at the Runtime /// layer — bound to a real /// — since the existing router /// coverage in AcDream.UI.Abstractions.Tests only exercises the /// ChatVM feedback implementation. The plugin-text half is pinned at /// the App layer (AppAutomationSurfaceTests.PostSystemMessage_RoutesToChatLog_NeverSpewBox), /// since AppAutomationSurface is the App-layer production /// implementation of IPluginChat. /// public sealed class ChatCommandRouterFeedbackRoutingTests { [Fact] public void DegeneratePrefix_UnknownCommandRefusal_RoutesToChatLog_NeverSpewBox() { // "/" alone (no letter verb) is the degenerate-prefix guard's // "Unknown command: {verb}." refusal — retail itself types this // 0x1A (ClientLocal / SpewBox-only); the owner override moves it to // the chat scroll (Default/0x00) instead. using var communication = new RuntimeCommunicationState(); var feedback = new RuntimeChatCommandFeedback(communication); SubmitOutcome outcome = ChatCommandRouter.Submit( "/", feedback, NullCommandBus.Instance, ChatChannelKind.Say); Assert.Equal(SubmitOutcome.UnknownCommand, outcome); ChatEntry entry = Assert.Single(communication.Chat.Snapshot()); Assert.Contains("Unknown command:", entry.Text); Assert.Equal((uint)RetailLogTextType.Default, entry.LogTextType); communication.SpewBox.Tick(0d); Assert.Equal(0, communication.SpewBox.Count); } [Fact] public void HelpUnresolvedVerb_UnknownCommandText_RoutesToChatLog_NeverSpewBox() { // "/help nonsenseverb" hits EmitVerbHelp's final unresolved-verb // fallback (RetailCommandHelpTable.UnknownCommand), the exact // existing retail-swept text — only the destination changes. using var communication = new RuntimeCommunicationState(); var feedback = new RuntimeChatCommandFeedback(communication); SubmitOutcome outcome = ChatCommandRouter.Submit( "/help nonsenseverb", feedback, NullCommandBus.Instance, ChatChannelKind.Say); Assert.Equal(SubmitOutcome.ClientHandled, outcome); ChatEntry entry = Assert.Single(communication.Chat.Snapshot()); Assert.Equal(RetailCommandHelpTable.UnknownCommand, entry.Text); Assert.Equal((uint)RetailLogTextType.Default, entry.LogTextType); communication.SpewBox.Tick(0d); Assert.Equal(0, communication.SpewBox.Count); } [Fact] public void HelpConfirmedNullVerb_UnknownCommandText_RoutesToChatLog_NeverSpewBox() { // "index" is one of the four catalog verbs retail registers with a // genuinely NULL help pointer (RetailCommandHelpTable. // CatalogVerbsWithNoRetailHelp) — EmitVerbHelp's OTHER "Unknown // command" call site, distinct from the unresolved-verb fallback // above. using var communication = new RuntimeCommunicationState(); var feedback = new RuntimeChatCommandFeedback(communication); SubmitOutcome outcome = ChatCommandRouter.Submit( "/help index", feedback, NullCommandBus.Instance, ChatChannelKind.Say); Assert.Equal(SubmitOutcome.ClientHandled, outcome); ChatEntry entry = Assert.Single(communication.Chat.Snapshot()); Assert.Equal(RetailCommandHelpTable.UnknownCommand, entry.Text); Assert.Equal((uint)RetailLogTextType.Default, entry.LogTextType); communication.SpewBox.Tick(0d); Assert.Equal(0, communication.SpewBox.Count); } [Fact] public void RealCommandBadArguments_StillRoutesToSpewBox_NeverChatLog() { // Boundary pin: AP-183's bad-argument refusals of REAL retail // commands are UNCHANGED by the owner's 2026-09-07 direction, which // named only unknown commands and plugin text. "/ls now" (Lifestone // with bad args) must still land in the SpewBox exclusively. using var communication = new RuntimeCommunicationState(); var feedback = new RuntimeChatCommandFeedback(communication); SubmitOutcome outcome = ChatCommandRouter.Submit( "/ls now", feedback, NullCommandBus.Instance, ChatChannelKind.Say); Assert.Equal(SubmitOutcome.ClientHandled, outcome); Assert.Empty(communication.Chat.Snapshot()); communication.SpewBox.Tick(0d); Assert.Equal(1, communication.SpewBox.Count); Assert.Equal( "Please see @help lifestone for more information on how to use this command.", communication.SpewBox.Snapshot()[0].Text); } }