From fd1ab22ceed9ec649c986978efa142f2dfbbf251 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Sep 2024 13:22:19 -0700 Subject: [PATCH 1/4] Implements admin command to set an island's max homes #2517 --- pom.xml | 2 +- .../commands/admin/AdminMaxHomesCommand.java | 169 ++++++++++++++++++ .../commands/admin/DefaultAdminCommand.java | 2 + .../bentobox/listeners/JoinLeaveListener.java | 24 ++- .../bentobox/bentobox/util/IslandInfo.java | 1 + src/main/resources/locales/en-US.yml | 8 + 6 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java diff --git a/pom.xml b/pom.xml index 5f5b6f725..e49d241f5 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 2.5.4 + 2.6.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java new file mode 100644 index 000000000..52a568f48 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java @@ -0,0 +1,169 @@ +package world.bentobox.bentobox.api.commands.admin; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import com.google.common.primitives.Ints; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.commands.ConfirmableCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; + +/** + * Sets the maximum number of homes allowed on this island. + * + * Commands: + * + * + * @author tastybento + */ + +public class AdminMaxHomesCommand extends ConfirmableCommand { + + private Integer maxHomes; + private Map islands = new HashMap<>(); + + public AdminMaxHomesCommand(CompositeCommand parent) { + super(parent, "setmaxhomes"); + } + + @Override + public void setup() { + setPermission("mod.maxhomes"); + setOnlyPlayer(false); + setParametersHelp("commands.admin.maxhomes.parameters"); + setDescription("commands.admin.maxhomes.description"); + } + + @Override + public boolean canExecute(User user, String label, List args) { + islands.clear(); + if (args.isEmpty()) { + showHelp(this, user); + return false; + } + // Check arguments + if (args.size() == 1) { + // Player must be in game + if (!user.isPlayer()) { + user.sendMessage("general.errors.use-in-game"); + return false; + } + // Check world + if (user.getWorld() != getWorld()) { + user.sendMessage("general.errors.wrong-world"); + return false; + } + // Arg must be an integer to return true, otherwise false + maxHomes = Ints.tryParse(args.get(0)); + if (maxHomes == null || maxHomes < 1) { + user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(0)); + return false; + } + // Get the island the user is standing on + boolean onIsland = getIslands().getIslandAt(user.getLocation()).map(is -> { + islands.put("", is); + return true; + }).orElse(false); + if (!onIsland) { + user.sendMessage("general.errors.not-on-island"); + return false; + } + return true; + } + // More than one argument + // First arg must be a valid player name + UUID targetUUID = getPlayers().getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + return false; + } + // Second arg must be the max homes number + maxHomes = Ints.tryParse(args.get(1)); + if (maxHomes == null) { + user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1)); + return false; + } + // Get islands + islands = this.getNameIslandMap(User.getInstance(targetUUID)); + if (islands.isEmpty()) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } + if (args.size() > 2) { + // A specific island is mentioned. Parse which one it is and remove the others + final String name = String.join(" ", args.subList(2, args.size())); // Join all the args from here with spaces + + islands.keySet().removeIf(n -> !name.equalsIgnoreCase(n)); + + if (islands.isEmpty()) { + // Failed name check - there are either + user.sendMessage("commands.admin.maxhomes.errors.unknown-island", TextVariables.NAME, name); + return false; + } + } + + return true; + } + + @Override + public boolean execute(User user, String label, List args) { + if (islands.isEmpty() || maxHomes < 1) { + // Sanity check + return false; + } + islands.forEach((name, island) -> { + island.setMaxHomes(maxHomes); + user.sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, name, TextVariables.NUMBER, + String.valueOf(maxHomes)); + }); + return true; + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.size() == 2) { + // Suggest player names + return Optional.of(Util.getOnlinePlayerList(user)); + } + if (args.size() > 3) { + return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg)); + } + return Optional.of(List.of("1")); + + } + + private Map getNameIslandMap(User user) { + Map islandMap = new HashMap<>(); + int index = 0; + for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) { + index++; + if (island.getName() != null && !island.getName().isBlank()) { + // Name has been set + islandMap.put(island.getName(), island); + } else { + // Name has not been set + String text = user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME, + user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index; + islandMap.put(text, island); + } + } + + return islandMap; + + } + +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java index be27bf080..00ad796f8 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommand.java @@ -102,6 +102,8 @@ public abstract class DefaultAdminCommand extends CompositeCommand { new AdminDeleteHomesCommand(this); // Reset name new AdminResetNameCommand(this); + // Max homes + new AdminMaxHomesCommand(this); } /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 11c750889..eac3174e4 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -95,13 +95,7 @@ public class JoinLeaveListener implements Listener { // Set island max members and homes based on permissions if this player is the // owner of an island - plugin.getIWM().getOverWorlds().stream().map(w -> plugin.getIslands().getIsland(w, playerUUID)) - .filter(Objects::nonNull).filter(i -> playerUUID.equals(i.getOwner())).forEach(i -> { - plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK); - plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK); - plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); - plugin.getIslands().getMaxHomes(i); - }); + updateIslandMaxTeamAndHomeSize(user); // Add a player to the bStats cache. plugin.getMetrics().ifPresent(bStats -> bStats.addPlayer(playerUUID)); @@ -125,6 +119,18 @@ public class JoinLeaveListener implements Listener { }); } + private void updateIslandMaxTeamAndHomeSize(User user) { + plugin.getIWM().getOverWorlds().stream() + .flatMap(w -> plugin.getIslands().getIslands(w, user.getUniqueId()).stream()) // Flatten the List into a Stream + .filter(Objects::nonNull).filter(i -> user.getUniqueId().equals(i.getOwner())).forEach(i -> { + plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK); + plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK); + plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); + plugin.getIslands().getMaxHomes(i); + }); + + } + private void firstTime(User user) { // Make sure the player is loaded into the cache or create the player if they // don't exist @@ -206,6 +212,10 @@ public class JoinLeaveListener implements Listener { } } + /** + * Update island range using player perms + * @param user user + */ private void updateIslandRange(User user) { plugin.getIslands().getIslands(user.getUniqueId()).stream() .filter(island -> island.getOwner() != null && island.getOwner().equals(user.getUniqueId())) diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index 9604aa33f..f7a0eecc7 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -143,6 +143,7 @@ public class IslandInfo { // Show team members showMembers(user); } + user.sendMessage("commands.admin.info.max-homes", TextVariables.NUMBER, String.valueOf(island.getMaxHomes())); Vector location = island.getProtectionCenter().toVector(); user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(location)); user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange())); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 1cbaf9f40..bd5529aee 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -59,6 +59,13 @@ commands: admin: help: description: admin command + maxhomes: + description: change the number of homes allowed on this island or player's island + parameters: + max-homes-set: '&a [name] - Set island max homes to [number]' + errors: + unknown-island: &c Unknown island! [name] + resets: description: edit player reset values set: @@ -213,6 +220,7 @@ commands: last-login-date-time-format: EEE MMM dd HH:mm:ss zzz yyyy deaths: 'Deaths: [number]' resets-left: 'Resets: [number] (Max: [total])' + max-homes: 'Max homes: [number]' team-members-title: 'Team members:' team-owner-format: '&a [name] [rank]' team-member-format: '&b [name] [rank]' From 2784f197cbf14e62dc7e005b99a3abc991fa204a Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Sep 2024 15:53:10 -0700 Subject: [PATCH 2/4] Added test class and made some methods easier to test --- .../api/commands/CompositeCommand.java | 2 +- .../commands/admin/AdminMaxHomesCommand.java | 7 +- .../admin/AdminDeleteCommandTest.java | 2 - .../admin/AdminMaxHomesCommandTest.java | 686 ++++++++++++++++++ 4 files changed, 690 insertions(+), 7 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommandTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java index 383f5bab2..4d0a8146e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -758,7 +758,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi * @param user - the User * @return result of help command or false if no help defined */ - protected boolean showHelp(CompositeCommand command, User user) { + public boolean showHelp(CompositeCommand command, User user) { return command.getSubCommand("help") .map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java index 52a568f48..eda3f8f09 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java @@ -9,7 +9,6 @@ import java.util.UUID; import com.google.common.primitives.Ints; -import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.localization.TextVariables; @@ -32,8 +31,8 @@ import world.bentobox.bentobox.util.Util; public class AdminMaxHomesCommand extends ConfirmableCommand { - private Integer maxHomes; - private Map islands = new HashMap<>(); + Integer maxHomes; + Map islands = new HashMap<>(); public AdminMaxHomesCommand(CompositeCommand parent) { super(parent, "setmaxhomes"); @@ -146,7 +145,7 @@ public class AdminMaxHomesCommand extends ConfirmableCommand { } - private Map getNameIslandMap(User user) { + Map getNameIslandMap(User user) { Map islandMap = new HashMap<>(); int index = 0; for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) { diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java index a047957f9..f5ddc6457 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java @@ -68,8 +68,6 @@ public class AdminDeleteCommandTest { @Mock private @Nullable Island island; - /** - */ @Before public void setUp() throws Exception { PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommandTest.java new file mode 100644 index 000000000..903952864 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommandTest.java @@ -0,0 +1,686 @@ +package world.bentobox.bentobox.api.commands.admin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.util.Util; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class, BentoBox.class, User.class, Util.class }) +public class AdminMaxHomesCommandTest { + + @Mock + private CompositeCommand ac; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private PlayersManager pm; + private UUID notUUID; + private UUID uuid; + @Mock + private World world; + @Mock + private @Nullable Island island; + private AdminMaxHomesCommand instance; + private String label; + private ArrayList args = new ArrayList<>(); + + @Before + public void setUp() throws Exception { + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Util + Util.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + Settings s = mock(Settings.class); + when(s.getResetCooldown()).thenReturn(0); + when(plugin.getSettings()).thenReturn(s); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + notUUID = UUID.randomUUID(); + while (notUUID.equals(uuid)) { + notUUID = UUID.randomUUID(); + } + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Parent command has no aliases + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ac.getTopLabel()).thenReturn("admin"); + when(ac.getWorld()).thenReturn(world); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + // Player has island to begin with + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + when(im.hasIsland(any(), any(User.class))).thenReturn(true); + // when(im.isOwner(any(),any())).thenReturn(true); + // when(im.getOwner(any(),any())).thenReturn(uuid); + when(im.getIsland(world, user)).thenReturn(island); + when(im.getIslands(world, notUUID)).thenReturn(List.of(island)); + when(plugin.getIslands()).thenReturn(im); + + // Island + when(island.getOwner()).thenReturn(uuid); + when(island.hasTeam()).thenReturn(true); + + // Has team + when(im.inTeam(any(), eq(uuid))).thenReturn(true); + + when(plugin.getPlayers()).thenReturn(pm); + + // Server & Scheduler + BukkitScheduler sch = mock(BukkitScheduler.class); + when(Bukkit.getScheduler()).thenReturn(sch); + BukkitTask task = mock(BukkitTask.class); + when(sch.runTaskLater(any(), any(Runnable.class), any(Long.class))).thenReturn(task); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(any(), any())).thenReturn("mock translation"); + when(plugin.getLocalesManager()).thenReturn(lm); + + instance = spy(new AdminMaxHomesCommand(ac)); + label = "island"; + } + + @After + public void tearDown() { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsIsEmpty() { + // Arrange: args is already empty + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + // Verify that showHelp was called + verify(instance).showHelp(instance, user); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSize1_UserNotPlayer() { + // Arrange + args.add("5"); + when(user.isPlayer()).thenReturn(false); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.use-in-game"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSize1_WrongWorld() { + // Arrange + args.add("5"); + when(user.isPlayer()).thenReturn(true); + World userWorld = mock(World.class); + World expectedWorld = mock(World.class); + when(user.getWorld()).thenReturn(userWorld); + doReturn(expectedWorld).when(instance).getWorld(); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.wrong-world"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSize1_InvalidMaxHomes() { + // Arrange + args.add("notanumber"); + when(user.isPlayer()).thenReturn(true); + World world = mock(World.class); + when(user.getWorld()).thenReturn(world); + doReturn(world).when(instance).getWorld(); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "notanumber"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSize1_UserNotOnIsland() { + // Arrange + args.add("5"); + when(user.isPlayer()).thenReturn(true); + World world = mock(World.class); + when(user.getWorld()).thenReturn(world); + doReturn(world).when(instance).getWorld(); + + Location location = mock(Location.class); + when(user.getLocation()).thenReturn(location); + + when(im.getIslandAt(location)).thenReturn(Optional.empty()); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.not-on-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSize1_Success() { + // Arrange + args.add("5"); + when(user.isPlayer()).thenReturn(true); + World world = mock(World.class); + when(user.getWorld()).thenReturn(world); + doReturn(world).when(instance).getWorld(); + + Location location = mock(Location.class); + when(user.getLocation()).thenReturn(location); + + Island island = mock(Island.class); + when(im.getIslandAt(location)).thenReturn(Optional.of(island)); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertTrue(result); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSizeGreaterThan1_InvalidPlayer() { + // Arrange + args.add("UnknownPlayer"); + args.add("5"); + + when(pm.getUUID("UnknownPlayer")).thenReturn(null); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.unknown-player", TextVariables.NAME, "UnknownPlayer"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSizeGreaterThan1_InvalidMaxHomes() { + // Arrange + args.add("ValidPlayer"); + args.add("notanumber"); + + UUID playerUUID = UUID.randomUUID(); + when(pm.getUUID("ValidPlayer")).thenReturn(playerUUID); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "notanumber"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSizeGreaterThan1_TargetPlayerHasNoIslands() { + // Arrange + args.add("ValidPlayer"); + args.add("5"); + + UUID playerUUID = UUID.randomUUID(); + when(pm.getUUID("ValidPlayer")).thenReturn(playerUUID); + + User targetUser = mock(User.class); + // Mock static method User.getInstance(UUID) + // Assuming use of Mockito with inline mocking or PowerMockito + PowerMockito.mockStatic(User.class); + when(User.getInstance(playerUUID)).thenReturn(targetUser); + + doReturn(new HashMap()).when(instance).getNameIslandMap(targetUser); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("general.errors.player-has-no-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSizeGreaterThan2_UnknownIsland() { + // Arrange + args.add("ValidPlayer"); + args.add("5"); + args.add("UnknownIsland"); + + UUID playerUUID = UUID.randomUUID(); + when(pm.getUUID("ValidPlayer")).thenReturn(playerUUID); + + User targetUser = mock(User.class); + // Mock static method User.getInstance(UUID) + // Assuming use of Mockito with inline mocking or PowerMockito + PowerMockito.mockStatic(User.class); + when(User.getInstance(playerUUID)).thenReturn(targetUser); + + Map islandsMap = new HashMap<>(); + islandsMap.put("Island1", mock(Island.class)); + doReturn(islandsMap).when(instance).getNameIslandMap(targetUser); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertFalse(result); + verify(user).sendMessage("commands.admin.maxhomes.errors.unknown-island", TextVariables.NAME, "UnknownIsland"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testArgsSizeGreaterThan1_Success() { + // Arrange + args.add("ValidPlayer"); + args.add("5"); + + UUID playerUUID = UUID.randomUUID(); + when(pm.getUUID("ValidPlayer")).thenReturn(playerUUID); + + User targetUser = mock(User.class); + // Mock static method User.getInstance(UUID) + PowerMockito.mockStatic(User.class); + when(User.getInstance(playerUUID)).thenReturn(targetUser); + + Map islandsMap = new HashMap<>(); + islandsMap.put("", mock(Island.class)); // Assuming empty string key as in code + doReturn(islandsMap).when(instance).getNameIslandMap(targetUser); + + // Act + boolean result = instance.canExecute(user, label, args); + + // Assert + assertTrue(result); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("mod.maxhomes", instance.getPermission()); + assertFalse(instance.isOnlyPlayer()); + assertEquals("commands.admin.maxhomes.parameters", instance.getParameters()); + assertEquals("commands.admin.maxhomes.description", instance.getDescription()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabComplete_ArgsSize2_ReturnsPlayerNames() { + // Arrange + args.add("someArg"); // args.size() == 1 + args.add(""); // args.size() == 2 + + // Mock Util.getOnlinePlayerList(user) + List onlinePlayers = Arrays.asList("PlayerOne", "PlayerTwo"); + PowerMockito.mockStatic(Util.class); + when(Util.getOnlinePlayerList(user)).thenReturn(onlinePlayers); + + // Act + Optional> result = instance.tabComplete(user, label, args); + + // Assert + assertTrue(result.isPresent()); + assertEquals(onlinePlayers, result.get()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabComplete_ArgsSizeGreaterThan3_ReturnsIslandNames() { + // Arrange + args.add("someArg"); + args.add("anotherArg"); + args.add("thirdArg"); + args.add(""); // args.size() == 4 (>3) + String lastArg = args.get(args.size() - 1); + + Map nameIslandMap = new HashMap<>(); + nameIslandMap.put("IslandOne", mock(Island.class)); + nameIslandMap.put("IslandTwo", mock(Island.class)); + doReturn(nameIslandMap).when(instance).getNameIslandMap(user); + + // Create the list of island names + List islandNames = new ArrayList<>(nameIslandMap.keySet()); + + // Mock Util.tabLimit() + List limitedIslandNames = Arrays.asList("IslandOne", "IslandTwo"); + PowerMockito.mockStatic(Util.class); + when(Util.tabLimit(islandNames, lastArg)).thenReturn(limitedIslandNames); + + // Act + Optional> result = instance.tabComplete(user, label, args); + + // Assert + assertTrue(result.isPresent()); + assertEquals(limitedIslandNames, result.get()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabComplete_Otherwise_ReturnsListOfOne() { + // Arrange + args.add(""); // args.size() == 1 + + // Act + Optional> result = instance.tabComplete(user, label, args); + + // Assert + assertTrue(result.isPresent()); + assertEquals(Collections.singletonList("1"), result.get()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabComplete_ArgsSize3_ReturnsListOfOne() { + // Arrange + args.add("someArg"); + args.add("anotherArg"); + args.add(""); // args.size() == 3 + + // Act + Optional> result = instance.tabComplete(user, label, args); + + // Assert + assertTrue(result.isPresent()); + assertEquals(Collections.singletonList("1"), result.get()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteWithEmptyIslands_ShouldReturnFalse() { + // Arrange + instance.maxHomes = 5; // Set maxHomes to a valid number + instance.islands = new HashMap<>(); // Empty islands map + + // Act + boolean result = instance.execute(user, label, args); + + // Assert + assertFalse(result); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteWithMaxHomesLessThanOne_ShouldReturnFalse() { + // Arrange + instance.maxHomes = 0; // Invalid maxHomes + Island island = mock(Island.class); + Map islandsMap = new HashMap<>(); + islandsMap.put("TestIsland", island); + instance.islands = islandsMap; + + // Act + boolean result = instance.execute(user, label, args); + + // Assert + assertFalse(result); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteSuccessful_SingleIsland() { + // Arrange + instance.maxHomes = 5; + Island island = mock(Island.class); + Map islandsMap = new HashMap<>(); + islandsMap.put("TestIsland", island); + instance.islands = islandsMap; + + // Act + boolean result = instance.execute(user, label, args); + + // Assert + assertTrue(result); + verify(island).setMaxHomes(5); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "TestIsland", + TextVariables.NUMBER, "5"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteSuccessful_MultipleIslands() { + // Arrange + instance.maxHomes = 3; + Island island1 = mock(Island.class); + Island island2 = mock(Island.class); + Map islandsMap = new HashMap<>(); + islandsMap.put("IslandOne", island1); + islandsMap.put("IslandTwo", island2); + instance.islands = islandsMap; + + // Act + boolean result = instance.execute(user, label, args); + + // Assert + assertTrue(result); + verify(island1).setMaxHomes(3); + verify(island2).setMaxHomes(3); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "IslandOne", + TextVariables.NUMBER, "3"); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "IslandTwo", + TextVariables.NUMBER, "3"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteAfterSuccessfulCanExecute() { + // Arrange + args.add("5"); + when(user.isPlayer()).thenReturn(true); + World world = mock(World.class); + when(user.getWorld()).thenReturn(world); + doReturn(world).when(instance).getWorld(); + + Location location = mock(Location.class); + when(user.getLocation()).thenReturn(location); + + Island island = mock(Island.class); + when(im.getIslandAt(location)).thenReturn(Optional.of(island)); + + // Act + boolean canExecuteResult = instance.canExecute(user, label, args); + boolean executeResult = instance.execute(user, label, args); + + // Assert + assertTrue(canExecuteResult); + assertTrue(executeResult); + verify(island).setMaxHomes(5); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "", TextVariables.NUMBER, + "5"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteWithInvalidMaxHomesAfterCanExecute() { + // Arrange + args.add("-1"); + when(user.isPlayer()).thenReturn(true); + World world = mock(World.class); + when(user.getWorld()).thenReturn(world); + doReturn(world).when(instance).getWorld(); + + // Act + boolean canExecuteResult = instance.canExecute(user, label, args); + boolean executeResult = instance.execute(user, label, args); + + // Assert + assertFalse(canExecuteResult); + assertFalse(executeResult); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminMaxHomesCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteWithMultipleIslandsAfterCanExecute() { + // Arrange + args.add("ValidPlayer"); + args.add("4"); + + UUID playerUUID = UUID.randomUUID(); + when(pm.getUUID("ValidPlayer")).thenReturn(playerUUID); + + User targetUser = mock(User.class); + PowerMockito.mockStatic(User.class); + when(User.getInstance(playerUUID)).thenReturn(targetUser); + + Island island1 = mock(Island.class); + Island island2 = mock(Island.class); + Map islandsMap = new HashMap<>(); + islandsMap.put("IslandA", island1); + islandsMap.put("IslandB", island2); + doReturn(islandsMap).when(instance).getNameIslandMap(targetUser); + + // Act + boolean canExecuteResult = instance.canExecute(user, label, args); + boolean executeResult = instance.execute(user, label, args); + + // Assert + assertTrue(canExecuteResult); + assertTrue(executeResult); + verify(island1).setMaxHomes(4); + verify(island2).setMaxHomes(4); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "IslandA", + TextVariables.NUMBER, "4"); + verify(user).sendMessage("commands.admin.maxhomes.max-homes-set", TextVariables.NAME, "IslandB", + TextVariables.NUMBER, "4"); + } + +} From 4a972a86aeafba9783faf67581f73adba0a96cd9 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Sep 2024 16:02:11 -0700 Subject: [PATCH 3/4] Add placeholders. Put in defensive code against nulls. --- .../commands/admin/AdminMaxHomesCommand.java | 1 + .../bentobox/lists/GameModePlaceholder.java | 19 +++++++ .../bentobox/bentobox/util/IslandInfo.java | 4 +- .../admin/DefaultAdminCommandTest.java | 55 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java index eda3f8f09..8f38bf4d4 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminMaxHomesCommand.java @@ -27,6 +27,7 @@ import world.bentobox.bentobox.util.Util; * * * @author tastybento + * @since 2.6.0 */ public class AdminMaxHomesCommand extends ConfirmableCommand { diff --git a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java index 032617d26..03b67d59b 100644 --- a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java +++ b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java @@ -157,6 +157,15 @@ public enum GameModePlaceholder { * @since 1.5.0 */ ISLAND_VISITORS_COUNT("island_visitors_count", (addon, user, island) -> island == null ? "" : String.valueOf(island.getVisitors().size())), + /** + * Returns the amount of players that are at least MEMBER on the island the player is standing on. + * @since 2.6.0 + */ + ISLAND_MAX_HOMES("island_max_homes", + (addon, user, island) -> island == null ? "" + : String.valueOf( + island.getMaxHomes() == null ? addon.getPlugin().getIWM().getMaxHomes(island.getWorld()) + : island.getMaxHomes())), /** * Returns the amount of players banned from the island. * @since 1.5.0 @@ -281,6 +290,16 @@ public enum GameModePlaceholder { */ VISITED_ISLAND_MEMBERS_COUNT("visited_island_members_count", (addon, user, island) -> getVisitedIsland(addon, user).map(value -> String.valueOf(value.getMemberSet().size())).orElse("")), + /** + * Returns the amount of players that are at least MEMBER on the island the player is standing on. + * @since 2.6.0 + */ + VISITED_ISLAND_MAX_HOMES("visited_island_max_homes", + (addon, user, + island) -> getVisitedIsland(addon, user).map(value -> String.valueOf( + island.getMaxHomes() == null ? addon.getPlugin().getIWM().getMaxHomes(island.getWorld()) + : island.getMaxHomes())) + .orElse("")), /** * Returns the amount of players that are TRUSTED on the island the player is standing on. * @since 1.5.2 diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index f7a0eecc7..909262f44 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -143,7 +143,9 @@ public class IslandInfo { // Show team members showMembers(user); } - user.sendMessage("commands.admin.info.max-homes", TextVariables.NUMBER, String.valueOf(island.getMaxHomes())); + int maxHomes = island.getMaxHomes() == null ? plugin.getIWM().getMaxHomes(island.getWorld()) + : island.getMaxHomes(); + user.sendMessage("commands.admin.info.max-homes", TextVariables.NUMBER, String.valueOf(maxHomes)); Vector location = island.getProtectionCenter().toVector(); user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(location)); user.sendMessage("commands.admin.info.protection-range", RANGE, String.valueOf(island.getProtectionRange())); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java new file mode 100644 index 000000000..26909309b --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java @@ -0,0 +1,55 @@ +/** + * + */ +package world.bentobox.bentobox.api.commands.admin; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * + */ +public class DefaultAdminCommandTest { + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#DefaultAdminCommand(world.bentobox.bentobox.api.addons.GameModeAddon)}. + */ + @Test + public void testDefaultAdminCommand() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#setup()}. + */ + @Test + public void testSetup() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + fail("Not yet implemented"); + } + +} From f1219ecc024d18b7c2089980cf96568bf973a26c Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Sep 2024 16:55:53 -0700 Subject: [PATCH 4/4] Remove test class --- .../admin/DefaultAdminCommandTest.java | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java deleted file mode 100644 index 26909309b..000000000 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/DefaultAdminCommandTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * - */ -package world.bentobox.bentobox.api.commands.admin; - -import static org.junit.Assert.*; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * - */ -public class DefaultAdminCommandTest { - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#DefaultAdminCommand(world.bentobox.bentobox.api.addons.GameModeAddon)}. - */ - @Test - public void testDefaultAdminCommand() { - fail("Not yet implemented"); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#setup()}. - */ - @Test - public void testSetup() { - fail("Not yet implemented"); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.DefaultAdminCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfString() { - fail("Not yet implemented"); - } - -}