From 9e8f51c616ab695a2e052a932554938cee8282c9 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 24 Dec 2015 15:26:25 +0100 Subject: [PATCH] #306 Fully test HelpProvider and CommandSyntaxHelper --- .../authme/command/help/HelpProvider.java | 10 +- .../authme/command/TestCommandsUtil.java | 88 +++-- .../command/help/CommandSyntaxHelperTest.java | 69 ++++ .../authme/command/help/HelpProviderTest.java | 312 ++++++++++++++++-- 4 files changed, 425 insertions(+), 54 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java diff --git a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java index 11235de5..c5bf2ccd 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java @@ -70,7 +70,7 @@ public class HelpProvider { if (hasFlag(SHOW_ARGUMENTS, options)) { printArguments(command, lines); } - if (hasFlag(SHOW_PERMISSIONS, options) && sender != null && permissionsManager != null) { + if (hasFlag(SHOW_PERMISSIONS, options) && sender != null) { printPermissions(command, sender, permissionsManager, lines); } if (hasFlag(SHOW_ALTERNATIVES, options)) { @@ -84,8 +84,8 @@ public class HelpProvider { } private static void printDetailedDescription(CommandDescription command, List lines) { - lines.add(ChatColor.GOLD + "Short Description: " + ChatColor.WHITE + command.getDescription()); - lines.add(ChatColor.GOLD + "Detailed Description:"); + lines.add(ChatColor.GOLD + "Short description: " + ChatColor.WHITE + command.getDescription()); + lines.add(ChatColor.GOLD + "Detailed description:"); lines.add(ChatColor.WHITE + " " + command.getDetailedDescription()); } @@ -95,8 +95,9 @@ public class HelpProvider { } lines.add(ChatColor.GOLD + "Arguments:"); + StringBuilder argString = new StringBuilder(); for (CommandArgumentDescription argument : command.getArguments()) { - StringBuilder argString = new StringBuilder(); + argString.setLength(0); argString.append(" ").append(ChatColor.YELLOW).append(ChatColor.ITALIC).append(argument.getName()) .append(": ").append(ChatColor.WHITE).append(argument.getDescription()); @@ -129,6 +130,7 @@ public class HelpProvider { private static void printPermissions(CommandDescription command, CommandSender sender, PermissionsManager permissionsManager, List lines) { CommandPermissions permissions = command.getCommandPermissions(); + // TODO ljacqu 20151224: Isn't it possible to have a default permission but no permission nodes? if (permissions == null || CollectionUtils.isEmpty(permissions.getPermissionNodes())) { return; } diff --git a/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java b/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java index 1a6c6757..3beb7693 100644 --- a/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java +++ b/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java @@ -2,6 +2,7 @@ package fr.xephi.authme.command; import fr.xephi.authme.command.executable.HelpCommand; import fr.xephi.authme.permission.DefaultPermission; +import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PlayerPermission; import java.util.Collection; @@ -21,17 +22,22 @@ public final class TestCommandsUtil { private TestCommandsUtil() { } + /** + * Generate the collection of test commands. + * + * @return The generated commands + */ public static Set generateCommands() { // Register /authme CommandDescription authMeBase = createCommand(null, null, singletonList("authme")); // Register /authme login createCommand(PlayerPermission.LOGIN, authMeBase, singletonList("login"), newArgument("password", false)); - // Register /authme register , alias: /authme reg - createCommand(PlayerPermission.LOGIN, authMeBase, asList("register", "reg"), + // Register /authme register , aliases: /authme reg, /authme r + createCommand(PlayerPermission.LOGIN, authMeBase, asList("register", "reg", "r"), newArgument("password", false), newArgument("confirmation", false)); // Register /email [player] - CommandDescription emailBase = createCommand(null, null, singletonList("email")); + CommandDescription emailBase = createCommand(null, null, singletonList("email"), newArgument("player", true)); // Register /email helptest -- use only to test for help command arguments special case CommandDescription.builder().parent(emailBase).labels("helptest").executableCommand(mock(HelpCommand.class)) .description("test").detailedDescription("Test.").withArgument("Query", "", false).build(); @@ -43,41 +49,69 @@ public final class TestCommandsUtil { return newHashSet(authMeBase, emailBase, unregisterBase); } - private static CommandDescription createCommand(PlayerPermission permission, CommandDescription parent, - List labels, CommandArgumentDescription... arguments) { - CommandDescription.CommandBuilder command = CommandDescription.builder() - .labels(labels) - .parent(parent) - .permissions(DefaultPermission.OP_ONLY, permission) - .description(labels.get(0)) - .detailedDescription("'" + labels.get(0) + "' test command") - .executableCommand(mock(ExecutableCommand.class)); - - if (arguments != null && arguments.length > 0) { - for (CommandArgumentDescription argument : arguments) { - command.withArgument(argument.getName(), "Test description", argument.isOptional()); - } - } - - return command.build(); - } - - private static CommandArgumentDescription newArgument(String label, boolean isOptional) { - return new CommandArgumentDescription(label, "Test description", isOptional); - } - + /** + * Retrieve the command with the given label from the collection of commands. + * Example: getCommandWithLabel(commands, "authme", "reg") to find the command description + * which defines the command /authme reg. + * + * @param commands The commands to search in + * @param parentLabel The parent label to search for + * @param childLabel The child label to find + * @return The matched command, or throws an exception if no command could be found + */ public static CommandDescription getCommandWithLabel(Collection commands, String parentLabel, String childLabel) { CommandDescription parent = getCommandWithLabel(commands, parentLabel); return getCommandWithLabel(parent.getChildren(), childLabel); } + /** + * Retrieve the command with the given label from the collection of commands. + * + * @param commands The commands to search in + * @param label The label to search for + * @return The matched command, or throws an exception if no command could be found + */ public static CommandDescription getCommandWithLabel(Collection commands, String label) { for (CommandDescription child : commands) { if (child.hasLabel(label)) { return child; } } - throw new RuntimeException("Could not find command with label '" + label + "'"); + throw new IllegalStateException("Could not find command with label '" + label + "'"); } + + /** Shortcut command to initialize a new test command. */ + private static CommandDescription createCommand(PermissionNode permission, CommandDescription parent, + List labels, CommandArgumentDescription... arguments) { + PermissionNode[] notNullPermission; + if (permission != null) { + notNullPermission = new PermissionNode[1]; + notNullPermission[0] = permission; + } else { + notNullPermission = new PermissionNode[0]; + } + + CommandDescription.CommandBuilder command = CommandDescription.builder() + .labels(labels) + .parent(parent) + .permissions(DefaultPermission.OP_ONLY, notNullPermission) + .description(labels.get(0) + " cmd") + .detailedDescription("'" + labels.get(0) + "' test command") + .executableCommand(mock(ExecutableCommand.class)); + + if (arguments != null && arguments.length > 0) { + for (CommandArgumentDescription argument : arguments) { + command.withArgument(argument.getName(), argument.getDescription(), argument.isOptional()); + } + } + + return command.build(); + } + + /** Shortcut command to initialize a new argument description. */ + private static CommandArgumentDescription newArgument(String label, boolean isOptional) { + return new CommandArgumentDescription(label, "'" + label + "' argument description", isOptional); + } + } diff --git a/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java b/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java new file mode 100644 index 00000000..608951de --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java @@ -0,0 +1,69 @@ +package fr.xephi.authme.command.help; + +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.TestCommandsUtil; +import org.bukkit.ChatColor; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link CommandSyntaxHelper}. + */ +public class CommandSyntaxHelperTest { + + private static Set commands; + + @BeforeClass + public static void setUpTestCommands() { + commands = TestCommandsUtil.generateCommands(); + } + + @Test + public void shouldFormatSimpleArgument() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme"); + List labels = Collections.singletonList("authme"); + + // when + String result = CommandSyntaxHelper.getSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW)); + } + + @Test + public void shouldFormatCommandWithMultipleArguments() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme", "register"); + List labels = Arrays.asList("authme", "reg"); + + // when + String result = CommandSyntaxHelper.getSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW + " reg ")); + } + + + @Test + public void shouldFormatCommandWithOptionalArgument() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "email"); + List labels = Collections.singletonList("email"); + + // when + String result = CommandSyntaxHelper.getSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]")); + } + +} diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index dbc62dc8..cb9c8501 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -1,15 +1,38 @@ package fr.xephi.authme.command.help; import fr.xephi.authme.command.CommandDescription; -import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.command.FoundCommandResult; +import fr.xephi.authme.command.FoundResultStatus; +import fr.xephi.authme.command.TestCommandsUtil; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerPermission; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.WrapperMock; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; +import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel; +import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS; +import static fr.xephi.authme.command.help.HelpProvider.HIDE_COMMAND; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_ARGUMENTS; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_LONG_DESCRIPTION; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_PERMISSIONS; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** @@ -17,49 +40,292 @@ import static org.mockito.Mockito.mock; */ public class HelpProviderTest { - private static CommandDescription parent; - private static CommandDescription child; + private HelpProvider helpProvider; + private PermissionsManager permissionsManager; + private CommandSender sender; + private static Set commands; @BeforeClass public static void setUpCommands() { - parent = CommandDescription.builder() - .executableCommand(mock(ExecutableCommand.class)) - .labels("base", "b") - .description("Parent") - .detailedDescription("Test base command.") - .build(); - child = CommandDescription.builder() - .executableCommand(mock(ExecutableCommand.class)) - .parent(parent) - .labels("child", "c") - .description("Child") - .detailedDescription("Child test command.") - .withArgument("Argument", "The argument", false) - .build(); + WrapperMock.createInstance(); + Settings.helpHeader = "Help"; + commands = TestCommandsUtil.generateCommands(); + } + + @Before + public void setUpHelpProvider() { + permissionsManager = mock(PermissionsManager.class); + helpProvider = new HelpProvider(permissionsManager); + sender = mock(CommandSender.class); + } + + @Test + public void shouldShowLongDescription() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "login"); + FoundCommandResult result = newFoundResult(command, Arrays.asList("authme", "login")); + + // when + List lines = helpProvider.printHelp(sender, result, SHOW_LONG_DESCRIPTION); + + // then + assertThat(lines, hasSize(5)); + assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(removeColors(lines.get(1)), containsString("Command: /authme login ")); + assertThat(removeColors(lines.get(2)), containsString("Short description: login cmd")); + assertThat(removeColors(lines.get(3)), equalTo("Detailed description:")); + assertThat(removeColors(lines.get(4)), containsString("'login' test command")); + } + + @Test + public void shouldShowArguments() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); + FoundCommandResult result = newFoundResult(command, Arrays.asList("authme", "reg")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_ARGUMENTS); + + // then + assertThat(lines, hasSize(4)); + assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(removeColors(lines.get(1)), equalTo("Arguments:")); + assertThat(removeColors(lines.get(2)), containsString("password: 'password' argument description")); + assertThat(removeColors(lines.get(3)), containsString("confirmation: 'confirmation' argument description")); + } + + @Test + public void shouldShowSpecifyIfArgumentIsOptional() { + // given + CommandDescription command = getCommandWithLabel(commands, "email"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("email")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_ARGUMENTS); + + // then + assertThat(lines, hasSize(3)); + assertThat(removeColors(lines.get(2)), containsString("player: 'player' argument description (Optional)")); + } + + /** Verifies that the "Arguments:" line is not shown if the command has no arguments. */ + @Test + public void shouldNotShowAnythingIfCommandHasNoArguments() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_ARGUMENTS); + + // then + assertThat(lines, hasSize(1)); // only has the help banner + } + + @Test + public void shouldShowAndEvaluatePermissions() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "login"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + given(sender.isOp()).willReturn(true); + given(permissionsManager.hasPermission(sender, PlayerPermission.LOGIN)).willReturn(true); + given(permissionsManager.hasPermission(sender, command)).willReturn(true); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_PERMISSIONS); + + // then + assertThat(lines, hasSize(5)); + assertThat(removeColors(lines.get(1)), containsString("Permissions:")); + assertThat(removeColors(lines.get(2)), + containsString(PlayerPermission.LOGIN.getNode() + " (You have permission)")); + assertThat(removeColors(lines.get(3)), containsString("Default: OP's only (You have permission)")); + assertThat(removeColors(lines.get(4)), containsString("Result: You have permission")); + } + + @Test + public void shouldShowAndEvaluateForbiddenPermissions() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "login"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + given(sender.isOp()).willReturn(false); + given(permissionsManager.hasPermission(sender, PlayerPermission.LOGIN)).willReturn(false); + given(permissionsManager.hasPermission(sender, command)).willReturn(false); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_PERMISSIONS); + + // then + assertThat(lines, hasSize(5)); + assertThat(removeColors(lines.get(1)), containsString("Permissions:")); + assertThat(removeColors(lines.get(2)), + containsString(PlayerPermission.LOGIN.getNode() + " (No permission)")); + assertThat(removeColors(lines.get(3)), containsString("Default: OP's only (No permission)")); + assertThat(removeColors(lines.get(4)), containsString("Result: No permission")); + } + + @Test + public void shouldNotShowAnythingForEmptyPermissions() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_PERMISSIONS); + + // then + assertThat(lines, hasSize(1)); + } + + @Test + public void shouldNotShowAnythingForNullPermissionsOnCommand() { + // given + CommandDescription command = mock(CommandDescription.class); + given(command.getCommandPermissions()).willReturn(null); + given(command.getLabels()).willReturn(Collections.singletonList("test")); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("test")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_PERMISSIONS); + + // then + assertThat(lines, hasSize(1)); + } + + @Test + public void shouldShowAlternatives() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); + FoundCommandResult result = newFoundResult(command, Arrays.asList("authme", "reg")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_ALTERNATIVES); + + // then + assertThat(lines, hasSize(4)); + assertThat(removeColors(lines.get(1)), containsString("Alternatives:")); + assertThat(removeColors(lines.get(2)), containsString("/authme register ")); + assertThat(removeColors(lines.get(3)), containsString("/authme r ")); + } + + @Test + public void shouldNotShowAnythingIfHasNoAlternatives() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "login"); + FoundCommandResult result = newFoundResult(command, Arrays.asList("authme", "login")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_ALTERNATIVES); + + // then + assertThat(lines, hasSize(1)); + } + + @Test + public void shouldShowChildren() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_CHILDREN); + + // then + assertThat(lines, hasSize(4)); + assertThat(removeColors(lines.get(1)), containsString("Commands:")); + assertThat(removeColors(lines.get(2)), containsString("/authme login: login cmd")); + assertThat(removeColors(lines.get(3)), containsString("/authme register: register cmd")); + } + + @Test + public void shouldNotShowCommandsTitleForCommandWithNoChildren() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + + // when + List lines = helpProvider.printHelp(sender, result, HIDE_COMMAND | SHOW_CHILDREN); + + // then + assertThat(lines, hasSize(1)); + } + + @Test + public void shouldHandleUnboundFoundCommandResult() { + // given + FoundCommandResult result = new FoundCommandResult(null, Arrays.asList("authme", "test"), + Collections.EMPTY_LIST, 0.0, FoundResultStatus.UNKNOWN_LABEL); + + // when + List lines = helpProvider.printHelp(sender, result, ALL_OPTIONS); + + // then + assertThat(lines, hasSize(1)); + assertThat(lines.get(0), containsString("Failed to retrieve any help information")); + } + + /** + * Since command parts may be mapped to a command description with labels that don't completely correspond to it, + * (e.g. suggest "register command" for /authme ragister), we need to check the labels and construct a correct list + */ + @Test + public void shouldShowCommandSyntaxWithCorrectLabels() { + // given + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); + FoundCommandResult result = newFoundResult(command, Arrays.asList("authme", "ragister")); + + // when + List lines = helpProvider.printHelp(sender, result, 0); + + // then + assertThat(lines, hasSize(2)); + assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(removeColors(lines.get(1)), containsString("Command: /authme register ")); } @Test public void shouldRetainCorrectLabels() { // given - List labels = Arrays.asList("b", "child"); + List labels = Arrays.asList("authme", "reg"); + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); // when - List result = HelpProvider.filterCorrectLabels(child, labels); + List result = HelpProvider.filterCorrectLabels(command, labels); // then - assertThat(result, contains("b", "child")); + assertThat(result, equalTo(labels)); } @Test public void shouldReplaceIncorrectLabels() { // given - List labels = Arrays.asList("base", "wrong"); + List labels = Arrays.asList("authme", "wrong"); + CommandDescription command = getCommandWithLabel(commands, "authme", "register"); // when - List result = HelpProvider.filterCorrectLabels(child, labels); + List result = HelpProvider.filterCorrectLabels(command, labels); // then - assertThat(result, contains("base", "child")); + assertThat(result, contains("authme", "register")); + } + + /** + * Generate an instance of {@link FoundCommandResult} with the given command and labels. All other fields aren't + * retrieved by {@link HelpProvider} and so are initialized to default values for the tests. + * + * @param command The command description + * @param labels The labels of the command (as in a real use case, they do not have to be correct) + * @return The generated FoundCommandResult object + */ + private static FoundCommandResult newFoundResult(CommandDescription command, List labels) { + return new FoundCommandResult(command, labels, Collections.EMPTY_LIST, 0.0, FoundResultStatus.SUCCESS); + } + + private static String removeColors(String str) { + for (ChatColor color : ChatColor.values()) { + str = str.replace(color.toString(), ""); + } + return str; } }