From 17bb43fed84cc9e334567f20922e42390ccf4a96 Mon Sep 17 00:00:00 2001 From: Drefvelin <54400154+Drefvelin@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:53:09 +0000 Subject: [PATCH 1/2] Show the linked Discord username in /web lookup chat. The command sender gets the account name on its own line, including when it is blank. Co-authored-by: Cursor --- .../tfmcweb/managers/WebCommand.java | 9 ++++++++ .../tfmcweb/managers/WebCommandTest.java | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java diff --git a/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java b/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java index 73226a7..8e04c23 100644 --- a/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java +++ b/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java @@ -166,6 +166,7 @@ private boolean cmdLookup(CommandSender sender, String[] args) { + " in_grace=" + cached.inGrace + " discord=" + nullSafe(cached.discordUsername) + " grace_until=" + nullSafe(cached.graceUntil)); + sender.sendMessage(discordUsernameLine(cached.discordUsername)); } else { sender.sendMessage(ChatColor.GRAY + "cache: (miss)"); } @@ -183,6 +184,7 @@ private boolean cmdLookup(CommandSender sender, String[] args) { + " in_grace=" + status.inGrace + " discord=" + nullSafe(status.discordUsername) + " grace_until=" + nullSafe(status.graceUntil)); + sender.sendMessage(discordUsernameLine(status.discordUsername)); Player online = Bukkit.getPlayer(uuid); if (online != null && online.isOnline()) { gateService.applyGate(online, status.eligible); @@ -285,6 +287,13 @@ private static String nullSafe(String s) { return s == null || s.isBlank() ? "-" : s; } + /** Chat line for the command sender. Blank names stay visible as "-". */ + @SuppressWarnings("deprecation") + static String discordUsernameLine(String username) { + return ChatColor.GRAY + "Discord username: " + + ChatColor.AQUA + nullSafe(username); + } + @Override public List onTabComplete( CommandSender sender, diff --git a/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java b/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java new file mode 100644 index 0000000..224aa40 --- /dev/null +++ b/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java @@ -0,0 +1,22 @@ +package net.tfminecraft.tfmcweb.managers; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class WebCommandTest { + + @Test + void lookupLineNamesTheDiscordUserForTheSender() { + String line = WebCommand.discordUsernameLine("ada_stone"); + assertTrue(line.contains("Discord username:")); + assertTrue(line.contains("ada_stone")); + } + + @Test + void lookupLineShowsABlankWhenTheUsernameIsMissing() { + String line = WebCommand.discordUsernameLine(" "); + assertTrue(line.contains("Discord username:")); + assertTrue(line.endsWith("-")); + } +} From 3db682b0f23506da9243fc9f6b7c2b5f16ecbf1c Mon Sep 17 00:00:00 2001 From: Drefvelin <54400154+Drefvelin@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:00:26 +0000 Subject: [PATCH 2/2] Keep the Discord username on its own lookup line. The summary no longer repeats it, and tests cover both the cached and live replies. Co-authored-by: Cursor --- .../tfmcweb/managers/WebCommand.java | 2 - .../tfmcweb/managers/WebCommandTest.java | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java b/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java index 8e04c23..4d0afaa 100644 --- a/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java +++ b/src/main/java/net/tfminecraft/tfmcweb/managers/WebCommand.java @@ -164,7 +164,6 @@ private boolean cmdLookup(CommandSender sender, String[] args) { sender.sendMessage(ChatColor.GRAY + "cache: linked=" + cached.linked + " eligible=" + cached.eligible + " in_grace=" + cached.inGrace - + " discord=" + nullSafe(cached.discordUsername) + " grace_until=" + nullSafe(cached.graceUntil)); sender.sendMessage(discordUsernameLine(cached.discordUsername)); } else { @@ -182,7 +181,6 @@ private boolean cmdLookup(CommandSender sender, String[] args) { sender.sendMessage(ChatColor.GRAY + "live: linked=" + status.linked + " eligible=" + status.eligible + " in_grace=" + status.inGrace - + " discord=" + nullSafe(status.discordUsername) + " grace_until=" + nullSafe(status.graceUntil)); sender.sendMessage(discordUsernameLine(status.discordUsername)); Player online = Bukkit.getPlayer(uuid); diff --git a/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java b/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java index 224aa40..590ac37 100644 --- a/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java +++ b/src/test/java/net/tfminecraft/tfmcweb/managers/WebCommandTest.java @@ -1,8 +1,29 @@ package net.tfminecraft.tfmcweb.managers; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitScheduler; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import net.tfminecraft.tfmcweb.api.ProvinceSystemClient; +import net.tfminecraft.tfmcweb.api.ProvinceSystemClient.IdentityStatus; +import net.tfminecraft.tfmcweb.cache.LinkCache; +import net.tfminecraft.tfmcweb.gate.DiscordGateService; class WebCommandTest { @@ -19,4 +40,84 @@ void lookupLineShowsABlankWhenTheUsernameIsMissing() { assertTrue(line.contains("Discord username:")); assertTrue(line.endsWith("-")); } + + @Test + void lookupTellsTheSenderTheCachedDiscordUsername() { + UUID uuid = UUID.randomUUID(); + Player target = mock(Player.class); + when(target.getUniqueId()).thenReturn(uuid); + when(target.getName()).thenReturn("Ada"); + + LinkCache cache = new LinkCache(); + cache.putLinked(uuid, "111", "ada_stone"); + + CommandSender sender = mock(CommandSender.class); + when(sender.hasPermission("tfmcweb.admin")).thenReturn(true); + + BukkitScheduler scheduler = mock(BukkitScheduler.class); + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getScheduler).thenReturn(scheduler); + bukkit.when(() -> Bukkit.getPlayerExact("Ada")).thenReturn(target); + + WebCommand command = new WebCommand( + mock(JavaPlugin.class), + cache, + mock(DiscordGateService.class) + ); + command.onCommand(sender, mock(Command.class), "web", new String[] { "lookup", "Ada" }); + } + + verify(sender).sendMessage(contains("Discord username:")); + verify(sender).sendMessage(contains("ada_stone")); + } + + @Test + void lookupTellsTheSenderTheLiveDiscordUsername() { + UUID uuid = UUID.randomUUID(); + Player target = mock(Player.class); + when(target.getUniqueId()).thenReturn(uuid); + when(target.getName()).thenReturn("Ada"); + when(target.isOnline()).thenReturn(true); + + CommandSender sender = mock(CommandSender.class); + when(sender.hasPermission("tfmcweb.admin")).thenReturn(true); + + BukkitScheduler scheduler = mock(BukkitScheduler.class); + when(scheduler.runTaskAsynchronously(any(Plugin.class), any(Runnable.class))) + .thenAnswer(invocation -> { + invocation.getArgument(1, Runnable.class).run(); + return null; + }); + when(scheduler.runTask(any(Plugin.class), any(Runnable.class))) + .thenAnswer(invocation -> { + invocation.getArgument(1, Runnable.class).run(); + return null; + }); + + IdentityStatus status = IdentityStatus.fromJson( + "{\"linked\":true,\"eligible\":true,\"in_grace\":false," + + "\"discord_username\":\"ada_stone\"}" + ); + + try ( + MockedStatic bukkit = mockStatic(Bukkit.class); + MockedStatic client = mockStatic(ProvinceSystemClient.class) + ) { + bukkit.when(Bukkit::getScheduler).thenReturn(scheduler); + bukkit.when(() -> Bukkit.getPlayerExact("Ada")).thenReturn(target); + bukkit.when(() -> Bukkit.getPlayer(uuid)).thenReturn(target); + client.when(() -> ProvinceSystemClient.getIdentityStatus(uuid.toString())) + .thenReturn(status); + + WebCommand command = new WebCommand( + mock(JavaPlugin.class), + new LinkCache(), + mock(DiscordGateService.class) + ); + command.onCommand(sender, mock(Command.class), "web", new String[] { "lookup", "Ada" }); + } + + verify(sender).sendMessage(contains("Discord username:")); + verify(sender).sendMessage(contains("ada_stone")); + } }