using AcDream.Core.Chat; using AcDream.UI.Abstractions.Panels.Chat; namespace AcDream.UI.Abstractions.Tests; public sealed class ChatVMTests { [Fact] public void RecentLines_ReturnsEmpty_ForEmptyLog() { var log = new ChatLog(); var vm = new ChatVM(log); Assert.Empty(vm.RecentLines()); } [Fact] public void RecentLines_ReturnsAllEntries_WhenBelowLimit() { var log = new ChatLog(); log.OnLocalSpeech(sender: "Caith", text: "hello", senderGuid: 0x5000_0001u, isRanged: false); log.OnLocalSpeech(sender: "Regal", text: "world", senderGuid: 0x5000_0002u, isRanged: false); var vm = new ChatVM(log, displayLimit: 20); var lines = vm.RecentLines(); Assert.Equal(2, lines.Count); Assert.Equal("Caith says, \"hello\"", lines[0]); Assert.Equal("Regal says, \"world\"", lines[1]); } [Fact] public void RecentLines_ReturnsTail_WhenAboveLimit_InOldestFirstOrder() { var log = new ChatLog(); for (int i = 0; i < 30; i++) log.OnLocalSpeech(sender: "A", text: $"msg{i}", senderGuid: 0x5000_0001u, isRanged: false); var vm = new ChatVM(log, displayLimit: 5); var lines = vm.RecentLines(); // Tail = msg25..msg29 (5 entries, oldest first). Assert.Equal(5, lines.Count); Assert.Equal("A says, \"msg25\"", lines[0]); Assert.Equal("A says, \"msg29\"", lines[4]); } [Fact] public void FormatEntry_LocalSpeech_RetailStyleSays() { // Retail format: "Name says, \"text\"" for someone else; // "You say, \"text\"" for our own /say (sender == "" or "You"). var incoming = new ChatEntry(ChatKind.LocalSpeech, "Caith", "hello", 0x5000_0001u, 0); Assert.Equal("Caith says, \"hello\"", ChatVM.FormatEntry(incoming)); var ownEcho = new ChatEntry(ChatKind.LocalSpeech, "", "hi there", 0, 0); Assert.Equal("You say, \"hi there\"", ChatVM.FormatEntry(ownEcho)); var ownEchoSubst = new ChatEntry(ChatKind.LocalSpeech, "You", "shouted echo", 0, 0); Assert.Equal("You say, \"shouted echo\"", ChatVM.FormatEntry(ownEchoSubst)); } [Fact] public void FormatEntry_RangedSpeech_RetailStyleShouts() { var incoming = new ChatEntry(ChatKind.RangedSpeech, "Caith", "hello", 0x5000_0001u, 0); Assert.Equal("Caith shouts, \"hello\"", ChatVM.FormatEntry(incoming)); var ownEcho = new ChatEntry(ChatKind.RangedSpeech, "You", "loud", 0, 0); Assert.Equal("You shout, \"loud\"", ChatVM.FormatEntry(ownEcho)); } [Fact] public void FormatEntry_Channel_UsesChannelNameWhenPresent() { // Friendly name takes precedence — "[Trade] Caith says, \"...\"" var named = new ChatEntry(ChatKind.Channel, "Caith", "g'day", 0x5000_0001u, 7u) { ChannelName = "Trade", }; Assert.Equal("[Trade] Caith says, \"g'day\"", ChatVM.FormatEntry(named)); // Falls back to "ch {id}" when ChannelName isn't set (legacy // path / older callers). var unnamed = new ChatEntry(ChatKind.Channel, "Caith", "g'day", 0x5000_0001u, 7u); Assert.Equal("[ch 7] Caith says, \"g'day\"", ChatVM.FormatEntry(unnamed)); } [Fact] public void FormatEntry_Tell_RetailStyleTells() { // SenderGuid != 0 -> incoming whisper -> "Regal tells you, ..." var incoming = new ChatEntry(ChatKind.Tell, "Regal", "psst", 0x5000_0002u, 0); Assert.Equal("Regal tells you, \"psst\"", ChatVM.FormatEntry(incoming)); // SenderGuid == 0 -> our own outbound echo -> Sender carries // the target name -> "You tell Regal, ..." var ownEcho = new ChatEntry(ChatKind.Tell, "Regal", "psst", 0, 0); Assert.Equal("You tell Regal, \"psst\"", ChatVM.FormatEntry(ownEcho)); } [Fact] public void FormatEntry_System_NoSenderShown() { var entry = new ChatEntry(ChatKind.System, Sender: "", "Your spell fizzled!", 0, 0); Assert.Equal("[System] Your spell fizzled!", ChatVM.FormatEntry(entry)); } [Fact] public void FormatEntry_Popup_Prefixed() { var entry = new ChatEntry(ChatKind.Popup, Sender: "", "A door stands before you.", 0, 0); Assert.Equal("[Popup] A door stands before you.", ChatVM.FormatEntry(entry)); } [Fact] public void Constructor_ThrowsOnNullLog() { Assert.Throws(() => new ChatVM(null!)); } [Fact] public void Constructor_ThrowsOnZeroOrNegativeLimit() { var log = new ChatLog(); Assert.Throws(() => new ChatVM(log, displayLimit: 0)); Assert.Throws(() => new ChatVM(log, displayLimit: -1)); } [Fact] public void RecentLines_ReturnsNewLineData_AfterSubsequentAppend() { // Confirm the VM isn't caching — each call re-snapshots the log. var log = new ChatLog(); var vm = new ChatVM(log); Assert.Empty(vm.RecentLines()); log.OnLocalSpeech("Caith", "hello", 0x5000_0001u, false); var after = vm.RecentLines(); Assert.Single(after); Assert.Equal("Caith says, \"hello\"", after[0]); } }