diff --git a/pom.xml b/pom.xml index a79f3a9d..37e5f505 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ fr.xephi authme - 5.2 + 5.3-SNAPSHOT AuthMeReloaded The first authentication plugin for the Bukkit API! diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 74a98654..1b987080 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -27,7 +27,6 @@ import fr.xephi.authme.permission.PermissionsSystemType; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.service.BackupService; import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.GeoIpService; import fr.xephi.authme.service.MigrationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; @@ -35,11 +34,9 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; -import fr.xephi.authme.util.PlayerUtils; import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -72,8 +69,6 @@ public class AuthMe extends JavaPlugin { private DataSource database; private BukkitService bukkitService; private Injector injector; - private GeoIpService geoIpService; - private PlayerCache playerCache; /** * Constructor. @@ -242,14 +237,13 @@ public class AuthMe extends JavaPlugin { */ protected void instantiateServices(Injector injector) { // PlayerCache is still injected statically sometimes - playerCache = PlayerCache.getInstance(); + PlayerCache playerCache = PlayerCache.getInstance(); injector.register(PlayerCache.class, playerCache); database = injector.getSingleton(DataSource.class); permsMan = injector.getSingleton(PermissionsManager.class); bukkitService = injector.getSingleton(BukkitService.class); commandHandler = injector.getSingleton(CommandHandler.class); - geoIpService = injector.getSingleton(GeoIpService.class); // Trigger construction of API classes; they will keep track of the singleton injector.getSingleton(NewAPI.class); @@ -344,24 +338,6 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.close(); } - public String replaceAllInfo(String message, Player player) { - String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size()); - String ipAddress = PlayerUtils.getPlayerIp(player); - Server server = getServer(); - return message - .replace("&", "\u00a7") - .replace("{PLAYER}", player.getName()) - .replace("{ONLINE}", playersOnline) - .replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers())) - .replace("{IP}", ipAddress) - .replace("{LOGINS}", Integer.toString(playerCache.getLogged())) - .replace("{WORLD}", player.getWorld().getName()) - .replace("{SERVER}", server.getServerName()) - .replace("{VERSION}", server.getBukkitVersion()) - // TODO: We should cache info like this, maybe with a class that extends Player? - .replace("{COUNTRY}", geoIpService.getCountryName(ipAddress)); - } - /** * Handle Bukkit commands. * diff --git a/src/main/java/fr/xephi/authme/command/CommandDescription.java b/src/main/java/fr/xephi/authme/command/CommandDescription.java index b6c006f9..18463bca 100644 --- a/src/main/java/fr/xephi/authme/command/CommandDescription.java +++ b/src/main/java/fr/xephi/authme/command/CommandDescription.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command; import fr.xephi.authme.permission.PermissionNode; -import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import java.util.ArrayList; import java.util.List; @@ -224,7 +224,7 @@ public class CommandDescription { * @return The generated CommandDescription object */ public CommandDescription build() { - checkArgument(!CollectionUtils.isEmpty(labels), "Labels may not be empty"); + checkArgument(!Utils.isCollectionEmpty(labels), "Labels may not be empty"); checkArgument(!StringUtils.isEmpty(description), "Description may not be empty"); checkArgument(!StringUtils.isEmpty(detailedDescription), "Detailed description may not be empty"); checkArgument(executableCommand != null, "Executable command must be set"); diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java index 8e2b1bdc..c5cf91c0 100644 --- a/src/main/java/fr/xephi/authme/command/CommandMapper.java +++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java @@ -2,8 +2,8 @@ package fr.xephi.authme.command; import fr.xephi.authme.command.executable.HelpCommand; import fr.xephi.authme.permission.PermissionsManager; -import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -45,7 +45,7 @@ public class CommandMapper { * @return The generated {@link FoundCommandResult} */ public FoundCommandResult mapPartsToCommand(CommandSender sender, final List parts) { - if (CollectionUtils.isEmpty(parts)) { + if (Utils.isCollectionEmpty(parts)) { return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND); } @@ -142,7 +142,7 @@ public class CommandMapper { * @return A command if there was a complete match (including proper argument count), null otherwise */ private static CommandDescription getSuitableChild(CommandDescription baseCommand, List parts) { - if (CollectionUtils.isEmpty(parts)) { + if (Utils.isCollectionEmpty(parts)) { return null; } diff --git a/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java index 27fa6c2e..af4d5beb 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java @@ -6,7 +6,7 @@ import de.luricos.bukkit.xAuth.xAuth; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.util.CollectionUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.command.CommandSender; import org.bukkit.plugin.PluginManager; @@ -55,7 +55,7 @@ public class xAuthConverter implements Converter { sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data..."); } List players = getXAuthPlayers(); - if (CollectionUtils.isEmpty(players)) { + if (Utils.isCollectionEmpty(players)) { sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players"); return; } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index d64bda77..6fb5fa8b 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -13,15 +13,16 @@ import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BungeeService; import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.WelcomeMessageConfiguration; import fr.xephi.authme.settings.commandconfig.CommandManager; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.util.StringUtils; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.potion.PotionEffectType; import javax.inject.Inject; +import java.util.List; import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN; @@ -54,6 +55,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { @Inject private Settings settings; + @Inject + private WelcomeMessageConfiguration welcomeMessageConfiguration; + ProcessSyncPlayerLogin() { } @@ -103,15 +107,12 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { player.saveData(); // Login is done, display welcome message + List welcomeMessage = welcomeMessageConfiguration.getWelcomeMessage(player); if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) { if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) { - for (String s : settings.getWelcomeMessage()) { - Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); - } + welcomeMessage.forEach(bukkitService::broadcastMessage); } else { - for (String s : settings.getWelcomeMessage()) { - player.sendMessage(plugin.replaceAllInfo(s, player)); - } + welcomeMessage.forEach(player::sendMessage); } } diff --git a/src/main/java/fr/xephi/authme/service/ValidationService.java b/src/main/java/fr/xephi/authme/service/ValidationService.java index 7905b367..7c7abf78 100644 --- a/src/main/java/fr/xephi/authme/service/ValidationService.java +++ b/src/main/java/fr/xephi/authme/service/ValidationService.java @@ -11,7 +11,6 @@ import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.Utils; import org.bukkit.command.CommandSender; @@ -144,11 +143,11 @@ public class ValidationService implements Reloadable { private boolean validateWhitelistAndBlacklist(String value, Property> whitelist, Property> blacklist) { List whitelistValue = settings.getProperty(whitelist); - if (!CollectionUtils.isEmpty(whitelistValue)) { + if (!Utils.isCollectionEmpty(whitelistValue)) { return containsIgnoreCase(whitelistValue, value); } List blacklistValue = settings.getProperty(blacklist); - return CollectionUtils.isEmpty(blacklistValue) || !containsIgnoreCase(blacklistValue, value); + return Utils.isCollectionEmpty(blacklistValue) || !containsIgnoreCase(blacklistValue, value); } private static boolean containsIgnoreCase(Collection coll, String needle) { diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index d287e140..980f2add 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -19,7 +19,6 @@ import static fr.xephi.authme.util.FileUtils.copyFileFromResource; public class Settings extends SettingsManager { private final File pluginFolder; - private String[] welcomeMessage; private String passwordEmailMessage; private String recoveryCodeEmailMessage; @@ -56,19 +55,9 @@ public class Settings extends SettingsManager { return recoveryCodeEmailMessage; } - /** - * Return the lines to output after an in-game registration. - * - * @return The welcome message - */ - public String[] getWelcomeMessage() { - return welcomeMessage; - } - private void loadSettingsFromFiles() { passwordEmailMessage = readFile("email.html"); recoveryCodeEmailMessage = readFile("recovery_code_email.html"); - welcomeMessage = readFile("welcome.txt").split("\\n"); } @Override diff --git a/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java b/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java new file mode 100644 index 00000000..60666f7a --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java @@ -0,0 +1,97 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.GeoIpService; +import fr.xephi.authme.util.PlayerUtils; +import fr.xephi.authme.util.lazytags.Tag; +import fr.xephi.authme.util.lazytags.TagReplacer; +import org.bukkit.Server; +import org.bukkit.entity.Player; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static fr.xephi.authme.util.FileUtils.copyFileFromResource; +import static fr.xephi.authme.util.lazytags.TagBuilder.createTag; + +/** + * Configuration for the welcome message (welcome.txt). + */ +public class WelcomeMessageConfiguration implements Reloadable { + + @DataFolder + @Inject + private File pluginFolder; + + @Inject + private Server server; + + @Inject + private GeoIpService geoIpService; + + @Inject + private BukkitService bukkitService; + + @Inject + private PlayerCache playerCache; + + /** List of all supported tags for the welcome message. */ + private final List> availableTags = Arrays.asList( + createTag("&", () -> "\u00a7"), + createTag("{PLAYER}", pl -> pl.getName()), + createTag("{ONLINE}", () -> Integer.toString(bukkitService.getOnlinePlayers().size())), + createTag("{MAXPLAYERS}", () -> Integer.toString(server.getMaxPlayers())), + createTag("{IP}", pl -> PlayerUtils.getPlayerIp(pl)), + createTag("{LOGINS}", () -> Integer.toString(playerCache.getLogged())), + createTag("{WORLD}", pl -> pl.getWorld().getName()), + createTag("{SERVER}", () -> server.getServerName()), + createTag("{VERSION}", () -> server.getBukkitVersion()), + createTag("{COUNTRY}", pl -> geoIpService.getCountryName(PlayerUtils.getPlayerIp(pl)))); + + private TagReplacer messageSupplier; + + @PostConstruct + @Override + public void reload() { + List welcomeMessage = readWelcomeFile(); + messageSupplier = TagReplacer.newReplacer(availableTags, welcomeMessage); + } + + /** + * Returns the welcome message for the given player. + * + * @param player the player for whom the welcome message should be prepared + * @return the welcome message + */ + public List getWelcomeMessage(Player player) { + return messageSupplier.getAdaptedMessages(player); + } + + /** + * @return the lines of the welcome message file + */ + private List readWelcomeFile() { + File welcomeFile = new File(pluginFolder, "welcome.txt"); + if (copyFileFromResource(welcomeFile, "welcome.txt")) { + try { + return Files.readAllLines(welcomeFile.toPath(), StandardCharsets.UTF_8); + } catch (IOException e) { + ConsoleLogger.logException("Failed to read welcome.txt file:", e); + } + } else { + ConsoleLogger.warning("Failed to copy welcome.txt from JAR"); + } + return Collections.emptyList(); + } +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java index bc0abf38..d87ebd5e 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java @@ -5,13 +5,21 @@ import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.GeoIpService; import fr.xephi.authme.util.FileUtils; +import fr.xephi.authme.util.PlayerUtils; +import fr.xephi.authme.util.lazytags.Tag; +import fr.xephi.authme.util.lazytags.WrappedTagReplacer; import org.bukkit.entity.Player; import javax.inject.Inject; import java.io.File; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import static fr.xephi.authme.util.lazytags.TagBuilder.createTag; + /** * Manages configurable commands to be run when various events occur. */ @@ -19,15 +27,20 @@ public class CommandManager implements Reloadable { private final File dataFolder; private final BukkitService bukkitService; + private final GeoIpService geoIpService; private final CommandMigrationService commandMigrationService; + private final List> availableTags = buildAvailableTags(); - private CommandConfig commandConfig; + private WrappedTagReplacer onJoinCommands; + private WrappedTagReplacer onLoginCommands; + private WrappedTagReplacer onRegisterCommands; @Inject - CommandManager(@DataFolder File dataFolder, BukkitService bukkitService, + CommandManager(@DataFolder File dataFolder, BukkitService bukkitService, GeoIpService geoIpService, CommandMigrationService commandMigrationService) { this.dataFolder = dataFolder; this.bukkitService = bukkitService; + this.geoIpService = geoIpService; this.commandMigrationService = commandMigrationService; reload(); } @@ -38,7 +51,7 @@ public class CommandManager implements Reloadable { * @param player the joining player */ public void runCommandsOnJoin(Player player) { - executeCommands(player, commandConfig.getOnJoin()); + executeCommands(player, onJoinCommands.getAdaptedItems(player)); } /** @@ -47,7 +60,7 @@ public class CommandManager implements Reloadable { * @param player the player who has registered */ public void runCommandsOnRegister(Player player) { - executeCommands(player, commandConfig.getOnRegister()); + executeCommands(player, onRegisterCommands.getAdaptedItems(player)); } /** @@ -56,11 +69,11 @@ public class CommandManager implements Reloadable { * @param player the player that logged in */ public void runCommandsOnLogin(Player player) { - executeCommands(player, commandConfig.getOnLogin()); + executeCommands(player, onLoginCommands.getAdaptedItems(player)); } - private void executeCommands(Player player, Map commands) { - for (Command command : commands.values()) { + private void executeCommands(Player player, List commands) { + for (Command command : commands) { final String execution = command.getCommand().replace("%p", player.getName()); if (Executor.CONSOLE.equals(command.getExecutor())) { bukkitService.dispatchConsoleCommand(execution); @@ -77,8 +90,22 @@ public class CommandManager implements Reloadable { SettingsManager settingsManager = new SettingsManager( new YamlFileResource(file), commandMigrationService, CommandSettingsHolder.class); - commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); + CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); + onJoinCommands = newReplacer(commandConfig.getOnJoin()); + onLoginCommands = newReplacer(commandConfig.getOnLogin()); + onRegisterCommands = newReplacer(commandConfig.getOnRegister()); } + private WrappedTagReplacer newReplacer(Map commands) { + return new WrappedTagReplacer<>(availableTags, commands.values(), Command::getCommand, + (cmd, text) -> new Command(text, cmd.getExecutor())); + } + private List> buildAvailableTags() { + return Arrays.asList( + createTag("%p", pl -> pl.getName()), + createTag("%nick", pl -> pl.getDisplayName()), + createTag("%ip", pl -> PlayerUtils.getPlayerIp(pl)), + createTag("%country", pl -> geoIpService.getCountryName(PlayerUtils.getPlayerIp(pl)))); + } } diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java index 7a1d19fa..fed07768 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java @@ -3,10 +3,10 @@ package fr.xephi.authme.task.purge; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PurgeSettings; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.util.CollectionUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; @@ -75,7 +75,7 @@ public class PurgeService { public void runPurge(CommandSender sender, long until, boolean includeEntriesWithLastLoginZero) { //todo: note this should may run async because it may executes a SQL-Query Set toPurge = dataSource.getRecordsToPurge(until, includeEntriesWithLastLoginZero); - if (CollectionUtils.isEmpty(toPurge)) { + if (Utils.isCollectionEmpty(toPurge)) { logAndSendMessage(sender, "No players to purge"); return; } diff --git a/src/main/java/fr/xephi/authme/util/CollectionUtils.java b/src/main/java/fr/xephi/authme/util/CollectionUtils.java deleted file mode 100644 index 2388fc60..00000000 --- a/src/main/java/fr/xephi/authme/util/CollectionUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package fr.xephi.authme.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Utils class for collections. - */ -public final class CollectionUtils { - - // Utility class - private CollectionUtils() { - } - - /** - * Get a range from a list based on start and count parameters in a safe way. - * - * @param element - * @param list The List - * @param start The start index - * @param count The number of elements to add - * - * @return The sublist consisting at most of {@code count} elements (less if the parameters - * exceed the size of the list) - */ - public static List getRange(List list, int start, int count) { - if (start >= list.size() || count <= 0) { - return new ArrayList<>(); - } else if (start < 0) { - start = 0; - } - int end = Math.min(list.size(), start + count); - return list.subList(start, end); - } - - /** - * Get all elements from a list starting from the given index. - * - * @param element - * @param list The List - * @param start The start index - * - * @return The sublist of all elements from index {@code start} and on; empty list - * if the start index exceeds the list's size - */ - public static List getRange(List list, int start) { - if (start >= list.size()) { - return new ArrayList<>(); - } - return getRange(list, start, list.size() - start); - } - - /** - * Null-safe way to check whether a collection is empty or not. - * - * @param coll The collection to verify - * @return True if the collection is null or empty, false otherwise - */ - public static boolean isEmpty(Collection coll) { - return coll == null || coll.isEmpty(); - } -} diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index b3ec53c7..b7628147 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -2,6 +2,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.ConsoleLogger; +import java.util.Collection; import java.util.regex.Pattern; /** @@ -50,6 +51,16 @@ public final class Utils { } } + /** + * Null-safe way to check whether a collection is empty or not. + * + * @param coll The collection to verify + * @return True if the collection is null or empty, false otherwise + */ + public static boolean isCollectionEmpty(Collection coll) { + return coll == null || coll.isEmpty(); + } + /** * Return the available core count of the JVM. * diff --git a/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java b/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java new file mode 100644 index 00000000..5fb0cd3d --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.util.lazytags; + +import java.util.function.Function; + +/** + * Replaceable tag whose value depends on an argument. + * + * @param the argument type + */ +public class DependentTag implements Tag { + + private final String name; + private final Function replacementFunction; + + /** + * Constructor. + * + * @param name the tag (placeholder) that will be replaced + * @param replacementFunction the function producing the replacement + */ + public DependentTag(String name, Function replacementFunction) { + this.name = name; + this.replacementFunction = replacementFunction; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue(A argument) { + return replacementFunction.apply(argument); + } +} diff --git a/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java b/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java new file mode 100644 index 00000000..a5bb58a2 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.util.lazytags; + +import java.util.function.Supplier; + +/** + * Tag to be replaced that does not depend on an argument. + * + * @param type of the argument (not used in this implementation) + */ +public class SimpleTag implements Tag { + + private final String name; + private final Supplier replacementFunction; + + public SimpleTag(String name, Supplier replacementFunction) { + this.name = name; + this.replacementFunction = replacementFunction; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue(A argument) { + return replacementFunction.get(); + } +} diff --git a/src/main/java/fr/xephi/authme/util/lazytags/Tag.java b/src/main/java/fr/xephi/authme/util/lazytags/Tag.java new file mode 100644 index 00000000..2c7c6ba5 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/Tag.java @@ -0,0 +1,22 @@ +package fr.xephi.authme.util.lazytags; + +/** + * Represents a tag in a text to be replaced with a value (which may depend on some argument). + * + * @param argument type the replacement may depend on + */ +public interface Tag { + + /** + * @return the tag to replace + */ + String getName(); + + /** + * Returns the value to replace the tag with for the given argument. + * + * @param argument the argument to evaluate the replacement for + * @return the replacement + */ + String getValue(A argument); +} diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java b/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java new file mode 100644 index 00000000..677b30e2 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java @@ -0,0 +1,21 @@ +package fr.xephi.authme.util.lazytags; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Utility class for creating tags. + */ +public final class TagBuilder { + + private TagBuilder() { + } + + public static Tag createTag(String name, Function replacementFunction) { + return new DependentTag<>(name, replacementFunction); + } + + public static Tag createTag(String name, Supplier replacementFunction) { + return new SimpleTag<>(name, replacementFunction); + } +} diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java b/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java new file mode 100644 index 00000000..660132fb --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java @@ -0,0 +1,102 @@ +package fr.xephi.authme.util.lazytags; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Replaces tags lazily by first determining which tags are being used + * and only applying those replacements afterwards. + * + * @param the argument type + */ +public class TagReplacer { + + private final List> tags; + private final Collection messages; + + /** + * Private constructor. Use {@link #newReplacer(Collection, Collection)}. + * + * @param tags the tags that are being used in the messages + * @param messages the messages + */ + private TagReplacer(List> tags, Collection messages) { + this.tags = tags; + this.messages = messages; + } + + /** + * Creates a new instance of this class, which will provide the given + * messages adapted with the provided tags. + * + * @param allTags all available tags + * @param messages the messages to use + * @param the argument type + * @return new tag replacer instance + */ + public static TagReplacer newReplacer(Collection> allTags, Collection messages) { + List> usedTags = determineUsedTags(allTags, messages); + return new TagReplacer<>(usedTags, messages); + } + + /** + * Returns the messages with the tags applied for the given argument. + * + * @param argument the argument to get the messages for + * @return the adapted messages + */ + public List getAdaptedMessages(A argument) { + // Note ljacqu 20170121: Using a Map might seem more natural here but we avoid doing so for performance + // Although the performance gain here is probably minimal... + List tagValues = new LinkedList<>(); + for (Tag tag : tags) { + tagValues.add(new TagValue(tag.getName(), tag.getValue(argument))); + } + + List adaptedMessages = new LinkedList<>(); + for (String line : messages) { + String adaptedLine = line; + for (TagValue tagValue : tagValues) { + adaptedLine = adaptedLine.replace(tagValue.tag, tagValue.value); + } + adaptedMessages.add(adaptedLine); + } + return adaptedMessages; + } + + /** + * Determines which tags are used somewhere in the given list of messages. + * + * @param allTags all available tags + * @param messages the messages + * @param argument type + * @return tags used at least once + */ + private static List> determineUsedTags(Collection> allTags, Collection messages) { + return allTags.stream() + .filter(tag -> messages.stream().anyMatch(msg -> msg.contains(tag.getName()))) + .collect(Collectors.toList()); + } + + /** (Tag, value) pair. */ + private static final class TagValue { + + /** The tag to replace. */ + private final String tag; + /** The value to replace with. */ + private final String value; + + TagValue(String tag, String value) { + this.tag = tag; + this.value = value; + } + + @Override + public String toString() { + return "TagValue[tag='" + tag + "', value='" + value + "']"; + } + } + +} diff --git a/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java new file mode 100644 index 00000000..92c3f70d --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java @@ -0,0 +1,61 @@ +package fr.xephi.authme.util.lazytags; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Applies tags lazily to the String property of an item. This class wraps + * a {@link TagReplacer} with the extraction of the String property and + * the creation of new items with the adapted string property. + * + * @param the item type + * @param the argument type to evaluate the replacements + */ +public class WrappedTagReplacer { + + private final Collection items; + private final BiFunction itemCreator; + private final TagReplacer tagReplacer; + + /** + * Constructor. + * + * @param allTags all available tags + * @param items the items to apply the replacements on + * @param stringGetter getter of the String property to adapt on the items + * @param itemCreator a function of signature (T, String) -> T: the original item and the adapted String are passed + */ + public WrappedTagReplacer(Collection> allTags, + Collection items, + Function stringGetter, + BiFunction itemCreator) { + this.items = items; + this.itemCreator = itemCreator; + + List stringItems = items.stream().map(stringGetter).collect(Collectors.toList()); + tagReplacer = TagReplacer.newReplacer(allTags, stringItems); + } + + /** + * Creates adapted items for the given argument. + * + * @param argument the argument to adapt the items for + * @return the adapted items + */ + public List getAdaptedItems(A argument) { + List adaptedStrings = tagReplacer.getAdaptedMessages(argument); + List adaptedItems = new LinkedList<>(); + + Iterator originalItemsIter = items.iterator(); + Iterator newStringsIter = adaptedStrings.iterator(); + while (originalItemsIter.hasNext() && newStringsIter.hasNext()) { + adaptedItems.add(itemCreator.apply(originalItemsIter.next(), newStringsIter.next())); + } + return adaptedItems; + } +} diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java index 597f89a7..6f869564 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java @@ -9,10 +9,14 @@ import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import java.util.Collection; @@ -20,9 +24,13 @@ import java.util.Collection; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link BukkitService}. @@ -38,10 +46,13 @@ public class BukkitServiceTest { private Settings settings; @Mock private Server server; + @Mock + private BukkitScheduler scheduler; @Before public void constructBukkitService() { ReflectionTestUtils.setField(Bukkit.class, null, "server", server); + given(server.getScheduler()).willReturn(scheduler); given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); bukkitService = new BukkitService(authMe, settings); } @@ -101,6 +112,191 @@ public class BukkitServiceTest { verify(server).dispatchCommand(consoleSender, command); } + @Test + public void shouldScheduleSyncDelayedTask() { + // given + Runnable task = () -> {/* noop */}; + given(scheduler.scheduleSyncDelayedTask(authMe, task)).willReturn(123); + + // when + int taskId = bukkitService.scheduleSyncDelayedTask(task); + + // then + verify(scheduler, only()).scheduleSyncDelayedTask(authMe, task); + assertThat(taskId, equalTo(123)); + } + + @Test + public void shouldScheduleSyncDelayedTaskWithDelay() { + // given + Runnable task = () -> {/* noop */}; + int delay = 3; + given(scheduler.scheduleSyncDelayedTask(authMe, task, delay)).willReturn(44); + + // when + int taskId = bukkitService.scheduleSyncDelayedTask(task, delay); + + // then + verify(scheduler, only()).scheduleSyncDelayedTask(authMe, task, delay); + assertThat(taskId, equalTo(44)); + } + + @Test + public void shouldScheduleSyncTask() { + // given + BukkitService spy = Mockito.spy(bukkitService); + doReturn(1).when(spy).scheduleSyncDelayedTask(any(Runnable.class)); + Runnable task = mock(Runnable.class); + + // when + spy.scheduleSyncTaskFromOptionallyAsyncTask(task); + + // then + verify(spy).scheduleSyncDelayedTask(task); + verifyZeroInteractions(task); + } + + @Test + public void shouldRunTaskDirectly() { + // given + given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false); + bukkitService.reload(settings); + BukkitService spy = Mockito.spy(bukkitService); + Runnable task = mock(Runnable.class); + + // when + spy.scheduleSyncTaskFromOptionallyAsyncTask(task); + + // then + verify(task).run(); + verify(spy, only()).scheduleSyncTaskFromOptionallyAsyncTask(task); + } + + @Test + public void shouldRunTask() { + // given + Runnable task = () -> {/* noop */}; + BukkitTask bukkitTask = mock(BukkitTask.class); + given(scheduler.runTask(authMe, task)).willReturn(bukkitTask); + + // when + BukkitTask resultingTask = bukkitService.runTask(task); + + // then + assertThat(resultingTask, equalTo(bukkitTask)); + verify(scheduler, only()).runTask(authMe, task); + } + + @Test + public void shouldRunTaskLater() { + // given + Runnable task = () -> {/* noop */}; + BukkitTask bukkitTask = mock(BukkitTask.class); + long delay = 400; + given(scheduler.runTaskLater(authMe, task, delay)).willReturn(bukkitTask); + + // when + BukkitTask resultingTask = bukkitService.runTaskLater(task, delay); + + // then + assertThat(resultingTask, equalTo(bukkitTask)); + verify(scheduler, only()).runTaskLater(authMe, task, delay); + } + + @Test + public void shouldRunTaskInAsync() { + // given + Runnable task = mock(Runnable.class); + BukkitService spy = Mockito.spy(bukkitService); + doReturn(null).when(spy).runTaskAsynchronously(task); + + // when + spy.runTaskOptionallyAsync(task); + + // then + verifyZeroInteractions(task); + verify(spy).runTaskAsynchronously(task); + } + + @Test + public void shouldRunTaskDirectlyIfConfigured() { + // given + given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false); + bukkitService.reload(settings); + BukkitService spy = Mockito.spy(bukkitService); + Runnable task = mock(Runnable.class); + + // when + spy.runTaskOptionallyAsync(task); + + // then + verify(task).run(); + verify(spy, only()).runTaskOptionallyAsync(task); + } + + @Test + public void shouldRunTaskAsynchronously() { + // given + Runnable task = () -> {/* noop */}; + BukkitTask bukkitTask = mock(BukkitTask.class); + given(scheduler.runTaskAsynchronously(authMe, task)).willReturn(bukkitTask); + + // when + BukkitTask resultingTask = bukkitService.runTaskAsynchronously(task); + + // then + assertThat(resultingTask, equalTo(bukkitTask)); + verify(scheduler, only()).runTaskAsynchronously(authMe, task); + } + + @Test + public void shouldRunTaskTimerAsynchronously() { + // given + Runnable task = () -> {/* */}; + long delay = 20L; + long period = 4000L; + BukkitTask bukkitTask = mock(BukkitTask.class); + given(scheduler.runTaskTimerAsynchronously(authMe, task, delay, period)).willReturn(bukkitTask); + + // when + BukkitTask resultingTask = bukkitService.runTaskTimerAsynchronously(task, delay, period); + + // then + assertThat(resultingTask, equalTo(bukkitTask)); + verify(scheduler).runTaskTimerAsynchronously(authMe, task, delay, period); + } + + @Test + public void shouldRunTaskTimer() { + // given + BukkitRunnable bukkitRunnable = mock(BukkitRunnable.class); + long delay = 20; + long period = 80; + BukkitTask bukkitTask = mock(BukkitTask.class); + given(bukkitRunnable.runTaskTimer(authMe, delay, period)).willReturn(bukkitTask); + + // when + BukkitTask result = bukkitService.runTaskTimer(bukkitRunnable, delay, period); + + // then + assertThat(result, equalTo(bukkitTask)); + verify(bukkitRunnable).runTaskTimer(authMe, delay, period); + } + + @Test + public void shouldBroadcastMessage() { + // given + String message = "Important message to all"; + given(server.broadcastMessage(message)).willReturn(24); + + // when + int result = bukkitService.broadcastMessage(message); + + // then + assertThat(result, equalTo(24)); + verify(server).broadcastMessage(message); + } + // Note: This method is used through reflections public static Player[] onlinePlayersImpl() { return new Player[]{ diff --git a/src/test/java/fr/xephi/authme/settings/SettingsTest.java b/src/test/java/fr/xephi/authme/settings/SettingsTest.java index 42326771..f7d2604c 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsTest.java @@ -4,7 +4,6 @@ import ch.jalu.configme.configurationdata.ConfigurationData; import ch.jalu.configme.configurationdata.ConfigurationDataBuilder; import ch.jalu.configme.resource.PropertyResource; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.TestConfiguration; import org.junit.Before; import org.junit.BeforeClass; @@ -16,11 +15,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; -import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** @@ -45,26 +41,6 @@ public class SettingsTest { testPluginFolder = temporaryFolder.newFolder(); } - @Test - public void shouldLoadWelcomeMessage() throws IOException { - // given - String welcomeMessage = "This is my welcome message for testing\nBye!"; - File welcomeFile = new File(testPluginFolder, "welcome.txt"); - createFile(welcomeFile); - Files.write(welcomeFile.toPath(), welcomeMessage.getBytes()); - - PropertyResource resource = mock(PropertyResource.class); - given(resource.getBoolean(RegistrationSettings.USE_WELCOME_MESSAGE.getPath())).willReturn(true); - Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA); - - // when - String[] result = settings.getWelcomeMessage(); - - // then - assertThat(result, arrayWithSize(2)); - assertThat(result, arrayContaining(welcomeMessage.split("\\n"))); - } - @Test public void shouldLoadEmailMessage() throws IOException { // given diff --git a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java new file mode 100644 index 00000000..37a3386a --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java @@ -0,0 +1,141 @@ +package fr.xephi.authme.settings; + +import ch.jalu.injector.testing.BeforeInjecting; +import ch.jalu.injector.testing.DelayedInjectionRunner; +import ch.jalu.injector.testing.InjectDelayed; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.GeoIpService; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.Matchers.contains; +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; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link WelcomeMessageConfiguration}. + */ +@RunWith(DelayedInjectionRunner.class) +public class WelcomeMessageConfigurationTest { + + @InjectDelayed + private WelcomeMessageConfiguration welcomeMessageConfiguration; + @Mock + private Server server; + @Mock + private BukkitService bukkitService; + @Mock + private GeoIpService geoIpService; + @Mock + private PlayerCache playerCache; + @DataFolder + private File testPluginFolder; + + private File welcomeFile; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @BeforeInjecting + public void createPluginFolder() throws IOException { + testPluginFolder = temporaryFolder.newFolder(); + welcomeFile = new File(testPluginFolder, "welcome.txt"); + welcomeFile.createNewFile(); + } + + @Test + public void shouldLoadWelcomeMessage() throws IOException { + // given + String welcomeMessage = "This is my welcome message for testing\nBye!"; + setWelcomeMessageAndReload(welcomeMessage); + Player player = mock(Player.class); + + // when + List result = welcomeMessageConfiguration.getWelcomeMessage(player); + + // then + assertThat(result, hasSize(2)); + assertThat(result, contains(welcomeMessage.split("\\n"))); + verifyZeroInteractions(player, playerCache, geoIpService, bukkitService, server); + } + + @Test + public void shouldReplaceNameAndIpAndCountry() throws IOException { + // given + String welcomeMessage = "Hello {PLAYER}, your IP is {IP}\nYour country is {COUNTRY}.\nWelcome to {SERVER}!"; + setWelcomeMessageAndReload(welcomeMessage); + + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + TestHelper.mockPlayerIp(player, "123.45.66.77"); + given(geoIpService.getCountryName("123.45.66.77")).willReturn("Syldavia"); + given(server.getServerName()).willReturn("CrazyServer"); + + // when + List result = welcomeMessageConfiguration.getWelcomeMessage(player); + + // then + assertThat(result, hasSize(3)); + assertThat(result.get(0), equalTo("Hello Bobby, your IP is 123.45.66.77")); + assertThat(result.get(1), equalTo("Your country is Syldavia.")); + assertThat(result.get(2), equalTo("Welcome to CrazyServer!")); + verify(server, only()).getServerName(); + verifyZeroInteractions(playerCache); + } + + @Test + public void shouldApplyOtherReplacements() throws IOException { + // given + String welcomeMessage = "{ONLINE}/{MAXPLAYERS} online\n{LOGINS} logged in\nYour world is {WORLD}\nServer: {VERSION}"; + setWelcomeMessageAndReload(welcomeMessage); + given(bukkitService.getOnlinePlayers()).willReturn((List) Arrays.asList(mock(Player.class), mock(Player.class))); + given(server.getMaxPlayers()).willReturn(20); + given(playerCache.getLogged()).willReturn(1); + given(server.getBukkitVersion()).willReturn("Bukkit-456.77.8"); + + World world = mock(World.class); + given(world.getName()).willReturn("Hub"); + Player player = mock(Player.class); + given(player.getWorld()).willReturn(world); + + // when + List result = welcomeMessageConfiguration.getWelcomeMessage(player); + + // then + assertThat(result, hasSize(4)); + assertThat(result.get(0), equalTo("2/20 online")); + assertThat(result.get(1), equalTo("1 logged in")); + assertThat(result.get(2), equalTo("Your world is Hub")); + assertThat(result.get(3), equalTo("Server: Bukkit-456.77.8")); + } + + private void setWelcomeMessageAndReload(String welcomeMessage) { + try { + Files.write(welcomeFile.toPath(), welcomeMessage.getBytes()); + } catch (IOException e) { + throw new IllegalStateException("Could not write to '" + welcomeFile + "'", e); + } + welcomeMessageConfiguration.reload(); + } +} diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java index fb8e4e40..3ec3b447 100644 --- a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java @@ -1,9 +1,9 @@ package fr.xephi.authme.settings.commandconfig; import com.google.common.io.Files; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.GeoIpService; import fr.xephi.authme.settings.SettingsMigrationService; import org.bukkit.entity.Player; import org.junit.Before; @@ -17,13 +17,7 @@ import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; -import java.util.function.BiConsumer; -import static fr.xephi.authme.settings.commandconfig.CommandConfigTestHelper.isCommand; -import static java.lang.String.format; -import static org.hamcrest.Matchers.anEmptyMap; -import static org.hamcrest.Matchers.contains; -import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -31,6 +25,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; /** * Test for {@link CommandManager}. @@ -41,104 +36,116 @@ public class CommandManagerTest { private static final String TEST_FILES_FOLDER = "/fr/xephi/authme/settings/commandconfig/"; private CommandManager manager; + private Player player; + @InjectMocks private CommandMigrationService commandMigrationService; - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - @Mock private BukkitService bukkitService; @Mock + private GeoIpService geoIpService; + @Mock private SettingsMigrationService settingsMigrationService; + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File testFolder; @Before public void setup() throws IOException { testFolder = temporaryFolder.newFolder(); - } - - @Test - @SuppressWarnings("unchecked") - public void shouldLoadCompleteFile() { - // given - copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); - - // when - initManager(); - - // then - CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig"); - assertThat(commandConfig.getOnJoin().keySet(), contains("broadcast")); - assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE))); - assertThat(commandConfig.getOnRegister().keySet(), contains("announce", "notify")); - assertThat(commandConfig.getOnRegister().values(), contains( - isCommand("me I just registered", Executor.PLAYER), - isCommand("log %p registered", Executor.CONSOLE))); - assertThat(commandConfig.getOnLogin().keySet(), contains("welcome", "show_motd", "display_list")); - assertThat(commandConfig.getOnLogin().values(), contains( - isCommand("msg %p Welcome back", Executor.CONSOLE), - isCommand("motd", Executor.PLAYER), - isCommand("list", Executor.PLAYER))); - } - - @Test - public void shouldLoadIncompleteFile() { - // given - copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml"); - - // when - initManager(); - - // then - CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig"); - assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE))); - assertThat(commandConfig.getOnLogin().values(), contains( - isCommand("msg %p Welcome back", Executor.CONSOLE), - isCommand("list", Executor.PLAYER))); - assertThat(commandConfig.getOnRegister(), anEmptyMap()); - } - - @Test - public void shouldExecuteCommandsOnJoin() { - // given - String name = "Bobby1"; - - // when - testCommandExecution(name, CommandManager::runCommandsOnJoin); - - // then - verify(bukkitService, only()).dispatchConsoleCommand(format("broadcast %s has joined", name)); - } - - @Test - public void shouldExecuteCommandsOnRegister() { - // given - String name = "luis"; - - // when - testCommandExecution(name, CommandManager::runCommandsOnRegister); - - // then - verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered")); - verify(bukkitService).dispatchConsoleCommand(format("log %s registered", name)); - verifyNoMoreInteractions(bukkitService); + player = mockPlayer(); } @Test public void shouldExecuteCommandsOnLogin() { // given - String name = "plaYer01"; + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + initManager(); // when - testCommandExecution(name, CommandManager::runCommandsOnLogin); + manager.runCommandsOnLogin(player); // then - verify(bukkitService).dispatchConsoleCommand(format("msg %s Welcome back", name)); + verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back"); verify(bukkitService).dispatchCommand(any(Player.class), eq("motd")); verify(bukkitService).dispatchCommand(any(Player.class), eq("list")); verifyNoMoreInteractions(bukkitService); + verifyZeroInteractions(geoIpService); + } + + @Test + public void shouldExecuteCommandsOnLoginWithIncompleteConfig() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml"); + initManager(); + + // when + manager.runCommandsOnLogin(player); + + // then + verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back, bob"); + verify(bukkitService).dispatchCommand(any(Player.class), eq("list")); + verifyNoMoreInteractions(bukkitService); + verifyZeroInteractions(geoIpService); + } + + @Test + public void shouldExecuteCommandsOnJoin() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + initManager(); + + // when + manager.runCommandsOnJoin(player); + + // then + verify(bukkitService, only()).dispatchConsoleCommand("broadcast bob has joined"); + verifyZeroInteractions(geoIpService); + } + + @Test + public void shouldExecuteCommandsOnJoinWithIncompleteConfig() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml"); + initManager(); + + // when + manager.runCommandsOnJoin(player); + + // then + verify(bukkitService, only()).dispatchConsoleCommand("broadcast Bobby has joined"); + verifyZeroInteractions(geoIpService); + } + + @Test + public void shouldExecuteCommandsOnRegister() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + initManager(); + + // when + manager.runCommandsOnRegister(player); + + // then + verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered")); + verify(bukkitService).dispatchConsoleCommand("log Bobby (127.0.0.3, Syldavia) registered"); + verifyNoMoreInteractions(bukkitService); + } + + @Test + public void shouldExecuteCommandsOnRegisterWithIncompleteConfig() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml"); + initManager(); + + // when + manager.runCommandsOnRegister(player); + + // then + verifyZeroInteractions(bukkitService, geoIpService); } @Test @@ -147,18 +154,8 @@ public class CommandManagerTest { TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandSettingsHolder.class); } - - private void testCommandExecution(String playerName, BiConsumer testMethod) { - copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); - initManager(); - Player player = mock(Player.class); - given(player.getName()).willReturn(playerName); - - testMethod.accept(manager, player); - } - private void initManager() { - manager = new CommandManager(testFolder, bukkitService, commandMigrationService); + manager = new CommandManager(testFolder, bukkitService, geoIpService, commandMigrationService); } private void copyJarFileAsCommandsYml(String path) { @@ -171,4 +168,13 @@ public class CommandManagerTest { } } + private Player mockPlayer() { + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + given(player.getDisplayName()).willReturn("bob"); + String ip = "127.0.0.3"; + TestHelper.mockPlayerIp(player, ip); + given(geoIpService.getCountryName(ip)).willReturn("Syldavia"); + return player; + } } diff --git a/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java b/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java deleted file mode 100644 index f359f088..00000000 --- a/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package fr.xephi.authme.util; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.hamcrest.Matchers.empty; - -/** - * Test for {@link CollectionUtils}. - */ -public class CollectionUtilsTest { - - @Test - public void shouldGetFullList() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 0, 24); - - // then - assertThat(result, equalTo(list)); - } - - @Test - public void shouldReturnEmptyListForZeroCount() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 2, 0); - - // then - assertThat(result, empty()); - } - - - @Test - public void shouldReturnEmptyListForTooHighStart() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 12, 2); - - // then - assertThat(result, empty()); - } - - @Test - public void shouldReturnSubList() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 1, 3); - - // then - assertThat(result, contains("1", "2", "3")); - } - - @Test - public void shouldReturnTillEnd() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 2, 3); - - // then - assertThat(result, contains("2", "3", "4")); - } - - @Test - public void shouldRemoveFirstTwo() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, 2); - - // then - assertThat(result, contains("2", "3", "4")); - } - - @Test - public void shouldHandleNegativeStart() { - // given - List list = Arrays.asList("test", "1", "2", "3", "4"); - - // when - List result = CollectionUtils.getRange(list, -4); - - // then - assertThat(result, equalTo(list)); - } -} diff --git a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java index fa8ee9c6..9d65047c 100644 --- a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java @@ -137,6 +137,11 @@ public class FileUtilsTest { assertThat(result, equalTo("path" + File.separator + "to" + File.separator + "test-file.txt")); } + @Test + public void shouldHaveHiddenConstructor() { + TestHelper.validateHasOnlyPrivateEmptyConstructor(FileUtils.class); + } + private static void createFiles(File... files) throws IOException { for (File file : files) { boolean result = file.getParentFile().mkdirs() & file.createNewFile(); diff --git a/src/test/java/tools/filegeneration/GenerateCommandsYml.java b/src/test/java/tools/filegeneration/GenerateCommandsYml.java index 73113ccc..b99eef52 100644 --- a/src/test/java/tools/filegeneration/GenerateCommandsYml.java +++ b/src/test/java/tools/filegeneration/GenerateCommandsYml.java @@ -26,7 +26,7 @@ public class GenerateCommandsYml implements AutoToolTask { // Get default and add sample entry CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getDefaultValue(); commandConfig.setOnLogin( - ImmutableMap.of("welcome", newCommand("msg %p Welcome back!", Executor.PLAYER))); + ImmutableMap.of("welcome", new Command("msg %p Welcome back!", Executor.PLAYER))); // Export the value to the file SettingsManager settingsManager = new SettingsManager( @@ -41,11 +41,4 @@ public class GenerateCommandsYml implements AutoToolTask { public String getTaskName() { return "generateCommandsYml"; } - - private static Command newCommand(String commandLine, Executor executor) { - Command command = new Command(); - command.setCommand(commandLine); - command.setExecutor(executor); - return command; - } } diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml index 8c6bf79d..757f09ca 100644 --- a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml +++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml @@ -2,14 +2,14 @@ onJoin: broadcast: - command: 'broadcast %p has joined' + command: 'broadcast %nick has joined' executor: CONSOLE onRegister: announce: command: 'me I just registered' executor: PLAYER notify: - command: 'log %p registered' + command: 'log %p (%ip, %country) registered' executor: CONSOLE onLogin: welcome: diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml index 2eef86b0..a1e9060f 100644 --- a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml +++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml @@ -6,7 +6,7 @@ onJoin: executor: CONSOLE onLogin: welcome: - command: 'msg %p Welcome back' + command: 'msg %p Welcome back, %nick' executor: CONSOLE show_motd: # command: 'motd' <-- mandatory property, so entry should be ignored