using AcDream.UI.Abstractions.Panels.Chat; using Xunit; namespace AcDream.UI.Abstractions.Tests.Panels.Chat; /// /// Campaign CH user-gate round 2, item 3 (2026-08-09): pins a sample of /// 's extracted strings byte-exact /// against the values recovered from the PDB-paired /// C:\Users\erikn\Downloads\acclient.exe via /// tools/pdb-extract/sweep_weenie_strings.py --ascii-only (generalized /// this round for narrow PStringBase<char> literals — see the /// script's own docstring). Covers all four COMPLETE group listings /// (death/status/text/allegiances — /help death was the user's own /// example of the fabricated meta-message this round replaces), the three /// PARTIAL groups' honest UNVERIFIED fallback, and a sample of the newly /// verbatim channel one-liners. /// public sealed class RetailCommandHelpTableTests { [Fact] public void DeathGroup_IsCompleteVerbatimListing_NoLeadingSummaryLine() { // acclient_2013_pseudo_c.txt:391365-391436 (HelpDeathGroup Detail // branch) -- straight-line concatenation, no "@help death - ..." // prefix (that string is exclusive to the Summary_HelpType branch). Assert.Equal( "@permit - Commands to give or revoke permission for others to loot your corpse.\n" + "@consent - Commands to help you manage the corpse-looting permissions that others give you.\n" + "@corpse - Displays the location of your last outdoor death.\n" + "@die - Kills your character and leaves a corpse, returning you to your lifestone.\n" + "@lifestone - Returns you to the last lifestone you used without killing you.\n" + "@marketplace - Teleports you to the Marketplace of Dereth.\n" + "@pkarena - Teleports you to the PK Arena. You must be PK to use this command.\n" + "@pklarena - Teleports you to the PKL Arena. You must be PKL to use this command.\n", RetailCommandHelpTable.DeathGroupDetail); Assert.True(RetailCommandHelpTable.TryGetHelpText("death", out string viaLookup)); Assert.Equal(RetailCommandHelpTable.DeathGroupDetail, viaLookup); Assert.DoesNotContain("has not yet extracted", viaLookup); } [Fact] public void StatusGroup_IsCompleteVerbatimListing_PreservesRetailTrailingSpace() { // The trailing space before "\n" on the @day line is retail's own // byte content (data_0x7de280) -- not an acdream typo. Assert.Equal( "@age - Displays your total gameplay time.\n" + "@birth - Displays when your character was created.\n" + "@day - A toggle that lightens the outdoor landscape. Note that this command may take several seconds to take effect. \n" + "@endurance - Explains how endurance affects your character.\n" + "@framerate - Toggles the framerate display.\n" + "@loc - Displays your current position.\n" + "@pklite - Sets your status to Player Killer Lite. Type @help pklite for more details.\n" + "@version - Tells you what version of the software you are using.\n", RetailCommandHelpTable.StatusGroupDetail); } [Fact] public void TextGroup_IsCompleteVerbatimListing() { Assert.Equal( "@clear - Clears the chat box of all text.\n" + "@filter - Commands to filter out incoming messages.\n" + "@unfilter - Commands to remove filters from incoming messages.\n" + "@loadfile - Reads in the given text file and executes each line in the chat entry field.\n" + "@log - Commands to echo chat text to a logfile.\n" + "@title - Sets the title of the popup chat window.\n", RetailCommandHelpTable.TextGroupDetail); } [Fact] public void AllegiancesGroup_IsCompleteVerbatimListing_TwoLines() { Assert.Equal( "@allegiance - Commands to help manage your allegiance.\n" + "@allegiance motd - Displays or sets the message of the day for your allegiance, see @help motd for more information.\n", RetailCommandHelpTable.AllegiancesGroupDetail); } [Theory] [InlineData("channels")] [InlineData("commands")] public void UnresolvedGroups_ContainVerbatimSummaryPlusHonestUnverifiedNote_NoMetaFabrication( string verb) { Assert.True(RetailCommandHelpTable.TryGetHelpText(verb, out string text)); // The old sentinel this round retired -- must never come back. Assert.DoesNotContain("has not yet extracted", text); Assert.Contains("UNVERIFIED", text); Assert.Contains("HelpStupidChannelHack @0x0056f290", text); } [Fact] public void ChannelsGroup_SummaryLineIsVerbatim() { Assert.True(RetailCommandHelpTable.TryGetHelpText("channels", out string text)); Assert.StartsWith( "@help channels - How to communicate with people in your allegiance or fellowship.", text); } [Fact] public void ChattingGroup_SevenOfEightEntriesAreVerbatim_FifthEntryMarkedUnverified() { Assert.True(RetailCommandHelpTable.TryGetHelpText("chatting", out string text)); Assert.Contains( "@chat - Sets whether or not you receive normal chat.\n", text); Assert.Contains( "@notell - Sets whether or not you receive @tell's.\n", text); Assert.Contains( "@reply - Sends some text to the last person who @tell'd you.\n", text); Assert.Contains( "@retell - Sends some text to the last person you @tell'd.\n", text); Assert.Contains( "@say - Says some text to everyone around you.\n", text); Assert.Contains( "@tell - Sends a private message to another character.\n", text); Assert.Contains( "@afk - Set your away-from-keyboard status.\n", text); Assert.Contains("HelpStupidChannelHack @0x0056f290", text); } // Campaign CH user-gate round 2, item 3: 7 of the ~35 channel // one-liners are now verbatim retail text (HelpTurbineChat_Xxx, // acclient_2013_pseudo_c.txt:387294-387411, addresses // 0x577620-0x577850) -- the "Also: @alias" suffix is retail's own text. [Theory] [InlineData("a", "@a - Sends a message to your Allegiance. Also: @guild, @gu")] [InlineData("guild", "@a - Sends a message to your Allegiance. Also: @guild, @gu")] [InlineData("general", "@general - Sends a message to the global General chat channel. Also: @cg")] [InlineData("trade", "@trade - Sends a message to the global Trade chat channel. Also: @ct")] [InlineData("lfg", "@lfg - Sends a message to the global Looking For Group (LFG) chat channel. Also: @clfg")] [InlineData("roleplay", "@roleplay - Sends a message to the global Roleplay chat channel. Also: @crp")] [InlineData("society", "@society - Sends a message to the your Society chat channel. Also: @soc")] [InlineData("olthoi", "@olthoi - If you are an Olthoi, sends a message to the global Olthoi chat channel. Also: @o")] public void ChannelVerb_VerbatimOneLiner_MatchesExactly(string verb, string expected) { Assert.True(RetailCommandHelpTable.TryGetHelpText(verb, out string text)); Assert.Equal(expected, text); } // The fellowship/monarch/patron/vassals/covassal family still routes // through the unresolved HelpStupidChannelHack pooled-string mechanism // (the "fvpca" BN artifact) and remains an honest acdream summary, not // a claimed-verbatim string. Pinned so a future accidental "upgrade" to // a fabricated retail-looking string doesn't slip in unnoticed. [Theory] [InlineData("f", "Sends text to your Fellowship channel.")] [InlineData("monarch", "Sends text to your Monarch.")] [InlineData("patron", "Sends text to your Patron.")] [InlineData("vassal", "Sends text to your Vassals.")] [InlineData("covassal", "Sends text to your Co-vassals.")] public void ChannelVerb_StillAcdreamSummary_UnresolvedChannelHackFamily( string verb, string expected) { Assert.True(RetailCommandHelpTable.TryGetHelpText(verb, out string text)); Assert.Equal(expected, text); } // ── Campaign CH user-gate round 3 (2026-08-10) ────────────────────── // The round 2 pass extracted the individual STRINGS byte-exact; this // round traces DoHelp's complete PRINT SEQUENCE — see the class // remarks. These pin the newly-swept literals byte-exact against // tools/pdb-extract/sweep_weenie_strings.py's output. [Fact] public void HelpPrefixNote_HasRetailsLeadingAndTrailingBlankLines() { // acclient_2013_pseudo_c.txt:394980 (data_0x7e11e0) -- the original // round-2 extraction dropped the leading "\n" and the trailing // "\n\n" that are part of retail's own literal. Assert.Equal( "\nNote: You may substitute a forward slash (/) for the at symbol (@).\n\n", RetailCommandHelpTable.HelpPrefixNote); } [Fact] public void ForMoreInformationPrefix_KeepsRetailsUnsubstitutedPlaceholderVerbatim() { // acclient_2013_pseudo_c.txt:395087 (data_0x7e1178) -- "" // is retail's own literal text, not a format placeholder acdream // failed to substitute; the decomp shows no sprintf/substitution // call between this literal and its use. Assert.Equal( "For more information, type @help .\n", RetailCommandHelpTable.ForMoreInformationPrefix); } [Fact] public void UnknownCommand_MatchesRetailsExactFallbackText() { Assert.Equal("Unknown command", RetailCommandHelpTable.UnknownCommand); } [Fact] public void AvailableHelpListing_MatchesDoHelpsCompleteThirteenItemSequence() { // acclient_2013_pseudo_c.txt:395091-395249 (DoHelp bare-arg "else" // branch), swept whole via sweep_weenie_strings.py --range // 0x57f9e0 0x57fe7e --ascii-only against the PDB-paired // C:\Users\erikn\Downloads\acclient.exe (verified MATCH). Exact // source order: header, then allegiances/channels/chatting/death/ // emote/fillcomps/friends/house/squelch/status/text/commands. Assert.Equal( "Available help:\n" + "@help allegiances - Commands to help you deal with your Allegiance.\n" + "@help channels - How to communicate with people in your allegiance or fellowship.\n" + "@help chatting - How to chat publically and privately.\n" + "@help death - Commands for making, finding, and looting corpses.\n" + "@help emote - How to perform text and action emotes.\n" + "@help fillcomps - A command to help you buy components in bulk.\n" + "@help friends - Commands to help you manage your friends list.\n" + "@help house - Commands that help you manage your house, including guest and storage management.\n" + "@help squelch - Commands that let you block out messages from other players.\n" + "@help status - Commands that display useful information.\n" + "@help text - Commands that help you manage your text window.\n" + "@help commands - Lists all commands.\n", RetailCommandHelpTable.AvailableHelpListing); } [Fact] public void DeathGroup_ThroughRouter_PrintsRetailsCompleteTwoEntryShape() { // Campaign CH user-gate round 3, finding (c): "/help death"'s // CONTENT was already byte-exact (round 2); what was still wrong // was the SHAPE. Retail's DoHelp prints two scroll entries for any // resolved verb: HelpPrefixNote, then ForMoreInformationPrefix // concatenated directly onto the verb's own Detail text (no blank // line, no third entry). Exercised at the router level (not just // the table) so this is the actual /help death user experience, // not merely the table's stored string. var log = new AcDream.Core.Chat.ChatLog(); var vm = new ChatVM(log, displayLimit: 50); var bus = new RecordingCommandBus(); var outcome = ChatCommandRouter.Submit( "/help death", vm, bus, ChatChannelKind.Say); Assert.Equal(SubmitOutcome.ClientHandled, outcome); var entries = log.Snapshot(); Assert.Equal(2, entries.Length); Assert.Equal(RetailCommandHelpTable.HelpPrefixNote, entries[0].Text); Assert.Equal( RetailCommandHelpTable.ForMoreInformationPrefix + RetailCommandHelpTable.DeathGroupDetail, entries[1].Text); } private sealed class RecordingCommandBus : ICommandBus { public List Published { get; } = new(); public void Publish(T command) where T : notnull => Published.Add(command); } }