diff --git a/README.md b/README.md
index c8a12dd0..9b3d75f0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
#####News:
-- We are going to release a stable (and working!) version of AuthMeBungee, in the meantime, a fixed version of the unofficial AuthMeBridgeBukkit plugin can be found at: https://github.com/Xephi/AuthMeReloaded/issues/557 Thanks to @beetle2k
+- Latest version of AuthMeBridge is finally compatible with latest AuthMe snapshots! ;)
#####Development tools:
@@ -62,7 +62,7 @@ McStats: http://mcstats.org/plugin/AuthMe
#####"The best authentication plugin for the Bukkit/Spigot API!"
-
Prevent username stealing on your server! Fully compatible with UUIDs and Craftbukkit/Spigot 1.8.X!
+
Prevent username stealing on your server!
Use it to secure your Offline mode server or to increase your Online mode server's protection!
+ * The spawn priority setting defines from which sources and in which order the spawn point
+ * should be taken from. In AuthMe, we can distinguish between the regular spawn and a "first spawn",
+ * to which players will be teleported who have joined for the first time.
+ */
+public class SpawnLoader {
+
+ private final File authMeConfigurationFile;
+ private final PluginHooks pluginHooks;
+ private FileConfiguration authMeConfiguration;
+ private String[] spawnPriority;
+ private Location essentialsSpawn;
+
+ /**
+ * Constructor.
+ *
+ * @param pluginFolder The AuthMe data folder
+ * @param settings The setting instance
+ * @param pluginHooks The plugin hooks instance
+ */
+ public SpawnLoader(File pluginFolder, NewSetting settings, PluginHooks pluginHooks) {
+ File spawnFile = new File(pluginFolder, "spawn.yml");
+ // TODO ljacqu 20160312: Check if resource could be copied and handle the case if not
+ FileUtils.copyFileFromResource(spawnFile, "spawn.yml");
+ this.authMeConfigurationFile = new File(pluginFolder, "spawn.yml");
+ this.pluginHooks = pluginHooks;
+ initialize(settings);
+ }
+
+ /**
+ * Retrieve the relevant settings and load the AuthMe spawn.yml file.
+ *
+ * @param settings The settings instance
+ */
+ public void initialize(NewSetting settings) {
+ spawnPriority = settings.getProperty(RestrictionSettings.SPAWN_PRIORITY).split(",");
+ authMeConfiguration = YamlConfiguration.loadConfiguration(authMeConfigurationFile);
+ loadEssentialsSpawn();
+ }
+
+ /**
+ * Return the AuthMe spawn location.
+ *
+ * @return The location of the regular AuthMe spawn point
+ */
+ public Location getSpawn() {
+ return getLocationFromConfiguration(authMeConfiguration, "spawn");
+ }
+
+ /**
+ * Set the AuthMe spawn point.
+ *
+ * @param location The location to use
+ * @return True upon success, false otherwise
+ */
+ public boolean setSpawn(Location location) {
+ return setLocation("spawn", location);
+ }
+
+ /**
+ * Return the AuthMe first spawn location.
+ *
+ * @return The location of the AuthMe spawn point for first timers
+ */
+ public Location getFirstSpawn() {
+ return getLocationFromConfiguration(authMeConfiguration, "firstspawn");
+ }
+
+ /**
+ * Set the AuthMe first spawn location.
+ *
+ * @param location The location to use
+ * @return True upon success, false otherwise
+ */
+ public boolean setFirstSpawn(Location location) {
+ return setLocation("firstspawn", location);
+ }
+
+ /**
+ * Load the spawn point defined in EssentialsSpawn.
+ */
+ public void loadEssentialsSpawn() {
+ // EssentialsSpawn cannot run without Essentials, so it's fine to get the Essentials data folder
+ File essentialsFolder = pluginHooks.getEssentialsDataFolder();
+ if (essentialsFolder == null) {
+ return;
+ }
+
+ File essentialsSpawnFile = new File(essentialsFolder, "spawn.yml");
+ if (essentialsSpawnFile.exists()) {
+ essentialsSpawn = getLocationFromConfiguration(
+ YamlConfiguration.loadConfiguration(essentialsSpawnFile), "spawns.default");
+ } else {
+ essentialsSpawn = null;
+ ConsoleLogger.info("Essentials spawn file not found: '" + essentialsSpawnFile.getAbsolutePath() + "'");
+ }
+ }
+
+ /**
+ * Unset the spawn point defined in EssentialsSpawn.
+ */
+ public void unloadEssentialsSpawn() {
+ essentialsSpawn = null;
+ }
+
+ /**
+ * Return the spawn location for the given player. The source of the spawn location varies
+ * depending on the spawn priority setting.
+ *
+ * @param player The player to retrieve the spawn point for
+ * @return The spawn location, or the default spawn location upon failure
+ * @see RestrictionSettings#SPAWN_PRIORITY
+ */
+ public Location getSpawnLocation(Player player) {
+ AuthMe plugin = AuthMe.getInstance();
+ if (plugin == null || player == null || player.getWorld() == null) {
+ return null;
+ }
+
+ World world = player.getWorld();
+ Location spawnLoc = null;
+ for (String priority : spawnPriority) {
+ switch (priority.toLowerCase().trim()) {
+ case "default":
+ if (world.getSpawnLocation() != null) {
+ spawnLoc = world.getSpawnLocation();
+ }
+ break;
+ case "multiverse":
+ if (Settings.multiverse) {
+ spawnLoc = pluginHooks.getMultiverseSpawn(world);
+ }
+ break;
+ case "essentials":
+ spawnLoc = essentialsSpawn;
+ break;
+ case "authme":
+ String playerNameLower = player.getName().toLowerCase();
+ if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) {
+ spawnLoc = getSpawn();
+ } else if (getFirstSpawn() != null && (!player.hasPlayedBefore() ||
+ !plugin.getDataSource().isAuthAvailable(playerNameLower))) {
+ spawnLoc = getFirstSpawn();
+ } else {
+ spawnLoc = getSpawn();
+ }
+ break;
+ }
+ if (spawnLoc != null) {
+ return spawnLoc;
+ }
+ }
+ return world.getSpawnLocation(); // return default location
+ }
+
+ /**
+ * Save the location under the given prefix.
+ *
+ * @param prefix The prefix to save the spawn under
+ * @param location The location to persist
+ * @return True upon success, false otherwise
+ */
+ private boolean setLocation(String prefix, Location location) {
+ if (location != null && location.getWorld() != null) {
+ authMeConfiguration.set(prefix + ".world", location.getWorld().getName());
+ authMeConfiguration.set(prefix + ".x", location.getX());
+ authMeConfiguration.set(prefix + ".y", location.getY());
+ authMeConfiguration.set(prefix + ".z", location.getZ());
+ authMeConfiguration.set(prefix + ".yaw", location.getYaw());
+ authMeConfiguration.set(prefix + ".pitch", location.getPitch());
+ return saveAuthMeConfig();
+ }
+ return false;
+ }
+
+ private boolean saveAuthMeConfig() {
+ // TODO ljacqu 20160312: Investigate whether this utility should be put in a Utils class
+ try {
+ authMeConfiguration.save(authMeConfigurationFile);
+ return true;
+ } catch (IOException e) {
+ ConsoleLogger.logException("Could not save spawn config (" + authMeConfigurationFile + ")", e);
+ }
+ return false;
+ }
+
+ /**
+ * Build a {@link Location} object from the given path in the file configuration.
+ *
+ * @param configuration The file configuration to read from
+ * @param pathPrefix The path to get the spawn point from
+ * @return Location corresponding to the values in the path
+ */
+ private static Location getLocationFromConfiguration(FileConfiguration configuration, String pathPrefix) {
+ if (containsAllSpawnFields(configuration, pathPrefix)) {
+ String prefix = pathPrefix + ".";
+ String worldName = configuration.getString(prefix + "world");
+ World world = Bukkit.getWorld(worldName);
+ if (!StringUtils.isEmpty(worldName) && world != null) {
+ return new Location(world, configuration.getDouble(prefix + "x"),
+ configuration.getDouble(prefix + "y"), configuration.getDouble(prefix + "z"),
+ getFloat(configuration, prefix + "yaw"), getFloat(configuration, prefix + "pitch"));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return whether the file configuration contains all fields necessary to define a spawn
+ * under the given path.
+ *
+ * @param configuration The file configuration to use
+ * @param pathPrefix The path to verify
+ * @return True if all spawn fields are present, false otherwise
+ */
+ private static boolean containsAllSpawnFields(FileConfiguration configuration, String pathPrefix) {
+ String[] fields = {"world", "x", "y", "z", "yaw", "pitch"};
+ for (String field : fields) {
+ if (!configuration.contains(pathPrefix + "." + field)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieve a property as a float from the given file configuration.
+ *
+ * @param configuration The file configuration to use
+ * @param path The path of the property to retrieve
+ * @return The float
+ */
+ private static float getFloat(FileConfiguration configuration, String path) {
+ Object value = configuration.get(path);
+ // This behavior is consistent with FileConfiguration#getDouble
+ return (value instanceof Number) ? ((Number) value).floatValue() : 0;
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/settings/domain/Comment.java b/src/main/java/fr/xephi/authme/settings/domain/Comment.java
index 07f20e25..0e69ec97 100644
--- a/src/main/java/fr/xephi/authme/settings/domain/Comment.java
+++ b/src/main/java/fr/xephi/authme/settings/domain/Comment.java
@@ -12,6 +12,6 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
public @interface Comment {
- String[] value();
+ String[] value();
}
diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java
new file mode 100644
index 00000000..14d09329
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java
@@ -0,0 +1,49 @@
+package fr.xephi.authme.settings.domain;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Enum property.
+ *
+ * @param The enum class
+ */
+class EnumProperty> extends Property {
+
+ private Class clazz;
+
+ public EnumProperty(Class clazz, String path, E defaultValue) {
+ super(path, defaultValue);
+ this.clazz = clazz;
+ }
+
+ @Override
+ public E getFromFile(FileConfiguration configuration) {
+ String textValue = configuration.getString(getPath());
+ if (textValue == null) {
+ return getDefaultValue();
+ }
+ E mappedValue = mapToEnum(textValue);
+ return mappedValue != null ? mappedValue : getDefaultValue();
+ }
+
+ @Override
+ public boolean isPresent(FileConfiguration configuration) {
+ return super.isPresent(configuration) && mapToEnum(configuration.getString(getPath())) != null;
+ }
+
+ @Override
+ public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
+ E value = getFromFile(configuration);
+ return singleQuoteYaml.dump(value.name());
+ }
+
+ private E mapToEnum(String value) {
+ for (E entry : clazz.getEnumConstants()) {
+ if (entry.name().equalsIgnoreCase(value)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java
deleted file mode 100644
index d38d0649..00000000
--- a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package fr.xephi.authme.settings.domain;
-
-import org.bukkit.configuration.file.FileConfiguration;
-import org.yaml.snakeyaml.Yaml;
-
-/**
- * Enum property type.
- *
- * @param The enum class
- */
-class EnumPropertyType> extends PropertyType {
-
- private Class clazz;
-
- public EnumPropertyType(Class clazz) {
- this.clazz = clazz;
- }
-
- @Override
- public E getFromFile(Property property, FileConfiguration configuration) {
- String textValue = configuration.getString(property.getPath());
- if (textValue == null) {
- return property.getDefaultValue();
- }
- E mappedValue = mapToEnum(textValue);
- return mappedValue != null ? mappedValue : property.getDefaultValue();
- }
-
- @Override
- public boolean contains(Property property, FileConfiguration configuration) {
- return super.contains(property, configuration)
- && mapToEnum(configuration.getString(property.getPath())) != null;
- }
-
- @Override
- public String toYaml(E value, Yaml simpleYaml, Yaml singleQuoteYaml) {
- return singleQuoteYaml.dump(value.name());
- }
-
- private E mapToEnum(String value) {
- for (E entry : clazz.getEnumConstants()) {
- if (entry.name().equalsIgnoreCase(value)) {
- return entry;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/fr/xephi/authme/settings/domain/Property.java b/src/main/java/fr/xephi/authme/settings/domain/Property.java
index f9637a7b..1d5100af 100644
--- a/src/main/java/fr/xephi/authme/settings/domain/Property.java
+++ b/src/main/java/fr/xephi/authme/settings/domain/Property.java
@@ -10,45 +10,27 @@ import java.util.Objects;
/**
* Property class, representing a setting that is read from the config.yml file.
*/
-public class Property {
+public abstract class Property {
- private final PropertyType type;
private final String path;
private final T defaultValue;
- private Property(PropertyType type, String path, T defaultValue) {
+ protected Property(String path, T defaultValue) {
Objects.requireNonNull(defaultValue);
- this.type = type;
this.path = path;
this.defaultValue = defaultValue;
}
/**
- * Create a new property. See also {@link #newProperty(PropertyType, String, Object[])} for lists and
- * {@link #newProperty(Class, String, Enum)}.
+ * Create a new string list property.
*
- * @param type The property type
* @param path The property's path
- * @param defaultValue The default value
- * @param The type of the property
- * @return The created property
- */
- public static Property newProperty(PropertyType type, String path, T defaultValue) {
- return new Property<>(type, path, defaultValue);
- }
-
- /**
- * Create a new list property.
- *
- * @param type The list type of the property
- * @param path The property's path
- * @param defaultValues The default value's items
- * @param The list type
+ * @param defaultValues The items in the default list
* @return The created list property
*/
- @SafeVarargs
- public static Property> newProperty(PropertyType> type, String path, U... defaultValues) {
- return new Property<>(type, path, Arrays.asList(defaultValues));
+ public static Property> newListProperty(String path, String... defaultValues) {
+ // does not have the same name as not to clash with #newProperty(String, String)
+ return new StringListProperty(path, defaultValues);
}
/**
@@ -61,36 +43,28 @@ public class Property {
* @return The created enum property
*/
public static > Property newProperty(Class clazz, String path, E defaultValue) {
- return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue);
+ return new EnumProperty<>(clazz, path, defaultValue);
}
- // -----
- // Overloaded convenience methods for specific types
- // -----
public static Property newProperty(String path, boolean defaultValue) {
- return new Property<>(PropertyType.BOOLEAN, path, defaultValue);
+ return new BooleanProperty(path, defaultValue);
}
public static Property newProperty(String path, int defaultValue) {
- return new Property<>(PropertyType.INTEGER, path, defaultValue);
+ return new IntegerProperty(path, defaultValue);
}
public static Property newProperty(String path, String defaultValue) {
- return new Property<>(PropertyType.STRING, path, defaultValue);
+ return new StringProperty(path, defaultValue);
}
- // -----
- // Hooks to the PropertyType methods
- // -----
/**
* Get the property value from the given configuration – guaranteed to never return null.
*
* @param configuration The configuration to read the value from
* @return The value, or default if not present
*/
- public T getFromFile(FileConfiguration configuration) {
- return type.getFromFile(this, configuration);
- }
+ public abstract T getFromFile(FileConfiguration configuration);
/**
* Return whether or not the given configuration file contains the property.
@@ -99,7 +73,7 @@ public class Property {
* @return True if the property is present, false otherwise
*/
public boolean isPresent(FileConfiguration configuration) {
- return type.contains(this, configuration);
+ return configuration.contains(path);
}
/**
@@ -111,12 +85,9 @@ public class Property {
* @return The generated YAML
*/
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
- return type.toYaml(getFromFile(configuration), simpleYaml, singleQuoteYaml);
+ return simpleYaml.dump(getFromFile(configuration));
}
- // -----
- // Trivial getters
- // -----
/**
* Return the default value of the property.
*
@@ -140,4 +111,89 @@ public class Property {
return "Property '" + path + "'";
}
+
+ /**
+ * Boolean property.
+ */
+ private static final class BooleanProperty extends Property {
+
+ public BooleanProperty(String path, Boolean defaultValue) {
+ super(path, defaultValue);
+ }
+
+ @Override
+ public Boolean getFromFile(FileConfiguration configuration) {
+ return configuration.getBoolean(getPath(), getDefaultValue());
+ }
+ }
+
+ /**
+ * Integer property.
+ */
+ private static final class IntegerProperty extends Property {
+
+ public IntegerProperty(String path, Integer defaultValue) {
+ super(path, defaultValue);
+ }
+
+ @Override
+ public Integer getFromFile(FileConfiguration configuration) {
+ return configuration.getInt(getPath(), getDefaultValue());
+ }
+ }
+
+ /**
+ * String property.
+ */
+ private static final class StringProperty extends Property {
+
+ public StringProperty(String path, String defaultValue) {
+ super(path, defaultValue);
+ }
+
+ @Override
+ public String getFromFile(FileConfiguration configuration) {
+ return configuration.getString(getPath(), getDefaultValue());
+ }
+
+ @Override
+ public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
+ return singleQuoteYaml.dump(getFromFile(configuration));
+ }
+ }
+
+ /**
+ * String list property.
+ */
+ private static final class StringListProperty extends Property> {
+
+ public StringListProperty(String path, String[] defaultValues) {
+ super(path, Arrays.asList(defaultValues));
+ }
+
+ @Override
+ public List getFromFile(FileConfiguration configuration) {
+ if (!configuration.isList(getPath())) {
+ return getDefaultValue();
+ }
+ return configuration.getStringList(getPath());
+ }
+
+ @Override
+ public boolean isPresent(FileConfiguration configuration) {
+ return configuration.isList(getPath());
+ }
+
+ @Override
+ public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
+ List value = getFromFile(configuration);
+ String yaml = singleQuoteYaml.dump(value);
+ // If the property is a non-empty list we need to append a new line because it will be
+ // something like the following, which requires a new line:
+ // - 'item 1'
+ // - 'second item in list'
+ return value.isEmpty() ? yaml : "\n" + yaml;
+ }
+ }
+
}
diff --git a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java
deleted file mode 100644
index 28a505cf..00000000
--- a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package fr.xephi.authme.settings.domain;
-
-import org.bukkit.configuration.file.FileConfiguration;
-import org.yaml.snakeyaml.Yaml;
-
-import java.util.List;
-
-/**
- * Handles a certain property type and provides type-specific functionality.
- *
- * @param The value of the property
- * @see Property
- */
-public abstract class PropertyType {
-
- public static final PropertyType BOOLEAN = new BooleanProperty();
- public static final PropertyType INTEGER = new IntegerProperty();
- public static final PropertyType STRING = new StringProperty();
- public static final PropertyType> STRING_LIST = new StringListProperty();
-
- /**
- * Get the property's value from the given YAML configuration.
- *
- * @param property The property to retrieve
- * @param configuration The YAML configuration to read from
- * @return The read value, or the default value if absent
- */
- public abstract T getFromFile(Property property, FileConfiguration configuration);
-
- /**
- * Return whether the property is present in the given configuration.
- *
- * @param property The property to search for
- * @param configuration The configuration to verify
- * @return True if the property is present, false otherwise
- */
- public boolean contains(Property property, FileConfiguration configuration) {
- return configuration.contains(property.getPath());
- }
-
- /**
- * Format the value as YAML.
- *
- * @param value The value to export
- * @param simpleYaml YAML object (default)
- * @param singleQuoteYaml YAML object set to use single quotes
- * @return The generated YAML
- */
- public String toYaml(T value, Yaml simpleYaml, Yaml singleQuoteYaml) {
- return simpleYaml.dump(value);
- }
-
-
- /**
- * Boolean property.
- */
- private static final class BooleanProperty extends PropertyType {
- @Override
- public Boolean getFromFile(Property property, FileConfiguration configuration) {
- return configuration.getBoolean(property.getPath(), property.getDefaultValue());
- }
- }
-
- /**
- * Integer property.
- */
- private static final class IntegerProperty extends PropertyType {
- @Override
- public Integer getFromFile(Property property, FileConfiguration configuration) {
- return configuration.getInt(property.getPath(), property.getDefaultValue());
- }
- }
-
- /**
- * String property.
- */
- private static final class StringProperty extends PropertyType {
- @Override
- public String getFromFile(Property property, FileConfiguration configuration) {
- return configuration.getString(property.getPath(), property.getDefaultValue());
- }
- @Override
- public String toYaml(String value, Yaml simpleYaml, Yaml singleQuoteYaml) {
- return singleQuoteYaml.dump(value);
- }
- }
-
- /**
- * String list property.
- */
- private static final class StringListProperty extends PropertyType> {
- @Override
- public List getFromFile(Property> property, FileConfiguration configuration) {
- if (!configuration.isList(property.getPath())) {
- return property.getDefaultValue();
- }
- return configuration.getStringList(property.getPath());
- }
-
- @Override
- public boolean contains(Property> property, FileConfiguration configuration) {
- return configuration.contains(property.getPath()) && configuration.isList(property.getPath());
- }
-
- @Override
- public String toYaml(List value, Yaml simpleYaml, Yaml singleQuoteYaml) {
- String yaml = singleQuoteYaml.dump(value);
- // If the property is a non-empty list we need to append a new line because it will be
- // something like the following, which requires a new line:
- // - 'item 1'
- // - 'second item in list'
- return value.isEmpty() ? yaml : "\n" + yaml;
- }
- }
-
-}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java
index 9dc4ad60..35e9cef9 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java
@@ -5,26 +5,24 @@ import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty;
-import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING;
public class ConverterSettings implements SettingsClass {
@Comment("Rakamak file name")
public static final Property RAKAMAK_FILE_NAME =
- newProperty(STRING, "Converter.Rakamak.fileName", "users.rak");
+ newProperty("Converter.Rakamak.fileName", "users.rak");
@Comment("Rakamak use IP?")
public static final Property RAKAMAK_USE_IP =
- newProperty(BOOLEAN, "Converter.Rakamak.useIP", false);
+ newProperty("Converter.Rakamak.useIP", false);
@Comment("Rakamak IP file name")
public static final Property RAKAMAK_IP_FILE_NAME =
- newProperty(STRING, "Converter.Rakamak.ipFileName", "UsersIp.rak");
+ newProperty("Converter.Rakamak.ipFileName", "UsersIp.rak");
@Comment("CrazyLogin database file name")
public static final Property CRAZYLOGIN_FILE_NAME =
- newProperty(STRING, "Converter.CrazyLogin.fileName", "accounts.db");
+ newProperty("Converter.CrazyLogin.fileName", "accounts.db");
private ConverterSettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java
index 7b7ee3ce..c6b6959d 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java
@@ -6,29 +6,26 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
-import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
-import static fr.xephi.authme.settings.domain.PropertyType.INTEGER;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class EmailSettings implements SettingsClass {
@Comment("Email SMTP server host")
public static final Property SMTP_HOST =
- newProperty(STRING, "Email.mailSMTP", "smtp.gmail.com");
+ newProperty("Email.mailSMTP", "smtp.gmail.com");
@Comment("Email SMTP server port")
public static final Property SMTP_PORT =
- newProperty(INTEGER, "Email.mailPort", 465);
+ newProperty("Email.mailPort", 465);
@Comment("Email account which sends the mails")
public static final Property MAIL_ACCOUNT =
- newProperty(STRING, "Email.mailAccount", "");
+ newProperty("Email.mailAccount", "");
@Comment("Email account password")
public static final Property MAIL_PASSWORD =
- newProperty(STRING, "Email.mailPassword", "");
+ newProperty("Email.mailPassword", "");
@Comment("Custom sender name, replacing the mailAccount name in the email")
public static final Property MAIL_SENDER_NAME =
@@ -36,39 +33,39 @@ public class EmailSettings implements SettingsClass {
@Comment("Recovery password length")
public static final Property RECOVERY_PASSWORD_LENGTH =
- newProperty(INTEGER, "Email.RecoveryPasswordLength", 8);
+ newProperty("Email.RecoveryPasswordLength", 8);
@Comment("Mail Subject")
public static final Property RECOVERY_MAIL_SUBJECT =
- newProperty(STRING, "Email.mailSubject", "Your new AuthMe password");
+ newProperty("Email.mailSubject", "Your new AuthMe password");
@Comment("Like maxRegPerIP but with email")
public static final Property MAX_REG_PER_EMAIL =
- newProperty(INTEGER, "Email.maxRegPerEmail", 1);
+ newProperty("Email.maxRegPerEmail", 1);
@Comment("Recall players to add an email?")
public static final Property RECALL_PLAYERS =
- newProperty(BOOLEAN, "Email.recallPlayers", false);
+ newProperty("Email.recallPlayers", false);
@Comment("Delay in minute for the recall scheduler")
public static final Property DELAY_RECALL =
- newProperty(INTEGER, "Email.delayRecall", 5);
+ newProperty("Email.delayRecall", 5);
@Comment("Blacklist these domains for emails")
public static final Property> DOMAIN_BLACKLIST =
- newProperty(STRING_LIST, "Email.emailBlacklisted", "10minutemail.com");
+ newListProperty("Email.emailBlacklisted", "10minutemail.com");
@Comment("Whitelist ONLY these domains for emails")
public static final Property> DOMAIN_WHITELIST =
- newProperty(STRING_LIST, "Email.emailWhitelisted");
+ newListProperty("Email.emailWhitelisted");
@Comment("Send the new password drawn in an image?")
public static final Property PASSWORD_AS_IMAGE =
- newProperty(BOOLEAN, "Email.generateImage", false);
+ newProperty("Email.generateImage", false);
@Comment("The OAuth2 token")
public static final Property OAUTH2_TOKEN =
- newProperty(STRING, "Email.emailOauth2Token", "");
+ newProperty("Email.emailOauth2Token", "");
private EmailSettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java
index b20d2868..95d91f85 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java
@@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
-import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
public class HooksSettings implements SettingsClass {
@@ -48,7 +48,7 @@ public class HooksSettings implements SettingsClass {
@Comment("Other MySQL columns where we need to put the username (case-sensitive)")
public static final Property> MYSQL_OTHER_USERNAME_COLS =
- newProperty(PropertyType.STRING_LIST, "ExternalBoardOptions.mySQLOtherUsernameColumns");
+ newListProperty("ExternalBoardOptions.mySQLOtherUsernameColumns");
@Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)")
public static final Property BCRYPT_LOG2_ROUND =
diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
index f5a51215..a4792f91 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
@@ -6,39 +6,37 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
-import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
-import static fr.xephi.authme.settings.domain.PropertyType.INTEGER;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class ProtectionSettings implements SettingsClass {
@Comment("Enable some servers protection (country based login, antibot)")
public static final Property ENABLE_PROTECTION =
- newProperty(BOOLEAN, "Protection.enableProtection", false);
+ newProperty("Protection.enableProtection", false);
@Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes",
"PLEASE USE QUOTES!"})
public static final Property> COUNTRIES_WHITELIST =
- newProperty(STRING_LIST, "Protection.countries", "US", "GB", "A1");
+ newListProperty("Protection.countries", "US", "GB", "A1");
@Comment({"Countries not allowed to join the server and register",
"PLEASE USE QUOTES!"})
public static final Property> COUNTRIES_BLACKLIST =
- newProperty(STRING_LIST, "Protection.countriesBlacklist");
+ newListProperty("Protection.countriesBlacklist");
@Comment("Do we need to enable automatic antibot system?")
public static final Property ENABLE_ANTIBOT =
- newProperty(BOOLEAN, "Protection.enableAntiBot", false);
+ newProperty("Protection.enableAntiBot", false);
- @Comment("Max number of player allowed to login in 5 secs before enable AntiBot system automatically")
+ @Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically")
public static final Property ANTIBOT_SENSIBILITY =
- newProperty(INTEGER, "Protection.antiBotSensibility", 5);
+ newProperty("Protection.antiBotSensibility", 5);
@Comment("Duration in minutes of the antibot automatic system")
public static final Property ANTIBOT_DURATION =
- newProperty(INTEGER, "Protection.antiBotDuration", 10);
+ newProperty("Protection.antiBotDuration", 10);
private ProtectionSettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java
index b4de9abf..2ec55799 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java
@@ -5,43 +5,40 @@ import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty;
-import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
-import static fr.xephi.authme.settings.domain.PropertyType.INTEGER;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING;
public class PurgeSettings implements SettingsClass {
@Comment("If enabled, AuthMe automatically purges old, unused accounts")
public static final Property USE_AUTO_PURGE =
- newProperty(BOOLEAN, "Purge.useAutoPurge", false);
+ newProperty("Purge.useAutoPurge", false);
@Comment("Number of Days an account become Unused")
public static final Property DAYS_BEFORE_REMOVE_PLAYER =
- newProperty(INTEGER, "Purge.daysBeforeRemovePlayer", 60);
+ newProperty("Purge.daysBeforeRemovePlayer", 60);
@Comment("Do we need to remove the player.dat file during purge process?")
public static final Property REMOVE_PLAYER_DAT =
- newProperty(BOOLEAN, "Purge.removePlayerDat", false);
+ newProperty("Purge.removePlayerDat", false);
@Comment("Do we need to remove the Essentials/users/player.yml file during purge process?")
public static final Property REMOVE_ESSENTIALS_FILES =
- newProperty(BOOLEAN, "Purge.removeEssentialsFile", false);
+ newProperty("Purge.removeEssentialsFile", false);
@Comment("World where are players.dat stores")
public static final Property DEFAULT_WORLD =
- newProperty(STRING, "Purge.defaultWorld", "world");
+ newProperty("Purge.defaultWorld", "world");
@Comment("Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?")
public static final Property REMOVE_LIMITED_CREATIVE_INVENTORIES =
- newProperty(BOOLEAN, "Purge.removeLimitedCreativesInventories", false);
+ newProperty("Purge.removeLimitedCreativesInventories", false);
@Comment("Do we need to remove the AntiXRayData/PlayerData/player file during purge process?")
public static final Property REMOVE_ANTI_XRAY_FILE =
- newProperty(BOOLEAN, "Purge.removeAntiXRayFile", false);
+ newProperty("Purge.removeAntiXRayFile", false);
@Comment("Do we need to remove permissions?")
public static final Property REMOVE_PERMISSIONS =
- newProperty(BOOLEAN, "Purge.removePermissions", false);
+ newProperty("Purge.removePermissions", false);
private PurgeSettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java
index 0be9f67a..c2c2bbe3 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java
@@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
-import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
public class RegistrationSettings implements SettingsClass {
@@ -39,7 +39,7 @@ public class RegistrationSettings implements SettingsClass {
newProperty("settings.registration.doubleEmailCheck", false);
@Comment({
- "Do we force kicking player after a successful registration?",
+ "Do we force kick a player after a successful registration?",
"Do not use with login feature below"})
public static final Property FORCE_KICK_AFTER_REGISTER =
newProperty("settings.registration.forceKickAfterRegister", false);
@@ -50,21 +50,21 @@ public class RegistrationSettings implements SettingsClass {
@Comment("Force these commands after /login, without any '/', use %p to replace with player name")
public static final Property> FORCE_COMMANDS =
- newProperty(PropertyType.STRING_LIST, "settings.forceCommands");
+ newListProperty("settings.forceCommands");
@Comment("Force these commands after /login as service console, without any '/'. "
+ "Use %p to replace with player name")
public static final Property> FORCE_COMMANDS_AS_CONSOLE =
- newProperty(PropertyType.STRING_LIST, "settings.forceCommandsAsConsole");
+ newListProperty("settings.forceCommandsAsConsole");
@Comment("Force these commands after /register, without any '/', use %p to replace with player name")
public static final Property> FORCE_REGISTER_COMMANDS =
- newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommands");
+ newListProperty("settings.forceRegisterCommands");
@Comment("Force these commands after /register as a server console, without any '/'. "
+ "Use %p to replace with player name")
public static final Property> FORCE_REGISTER_COMMANDS_AS_CONSOLE =
- newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommandsAsConsole");
+ newListProperty("settings.forceRegisterCommandsAsConsole");
@Comment({
"Enable to display the welcome message (welcome.txt) after a registration or a login",
diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java
index 4025c0be..c2b2cb68 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java
@@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property;
-import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
public class RestrictionSettings implements SettingsClass {
@@ -26,7 +26,7 @@ public class RestrictionSettings implements SettingsClass {
@Comment("Allowed commands for unauthenticated players")
public static final Property> ALLOW_COMMANDS =
- newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands",
+ newListProperty("settings.restrictions.allowCommands",
"login", "register", "l", "reg", "email", "captcha");
@Comment("Max number of allowed registrations per IP")
@@ -64,7 +64,7 @@ public class RestrictionSettings implements SettingsClass {
@Comment({
"To activate the restricted user feature you need",
- "to enable this option and configure the AllowedRestrctedUser field."})
+ "to enable this option and configure the AllowedRestrictedUser field."})
public static final Property ENABLE_RESTRICTED_USERS =
newProperty("settings.restrictions.AllowRestrictedUser", false);
@@ -75,7 +75,7 @@ public class RestrictionSettings implements SettingsClass {
" AllowedRestrictedUser:",
" - playername;127.0.0.1"})
public static final Property> ALLOWED_RESTRICTED_USERS =
- newProperty(PropertyType.STRING_LIST, "settings.restrictions.AllowedRestrictedUser");
+ newListProperty("settings.restrictions.AllowedRestrictedUser");
@Comment("Should unregistered players be kicked immediately?")
public static final Property KICK_NON_REGISTERED =
@@ -148,7 +148,7 @@ public class RestrictionSettings implements SettingsClass {
"WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled",
"Case-sensitive!"})
public static final Property> FORCE_SPAWN_ON_WORLDS =
- newProperty(PropertyType.STRING_LIST, "settings.restrictions.ForceSpawnOnTheseWorlds",
+ newListProperty("settings.restrictions.ForceSpawnOnTheseWorlds",
"world", "world_nether", "world_the_end");
@Comment("Ban ip when the ip is not the ip registered in database")
@@ -189,7 +189,7 @@ public class RestrictionSettings implements SettingsClass {
"It is case-sensitive!"
})
public static final Property> UNRESTRICTED_NAMES =
- newProperty(PropertyType.STRING_LIST, "settings.unrestrictions.UnrestrictedName");
+ newListProperty("settings.unrestrictions.UnrestrictedName");
private RestrictionSettings() {
diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
index 6a2ace71..9a52ca82 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
@@ -7,8 +7,8 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List;
+import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty;
-import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class SecuritySettings implements SettingsClass {
@@ -98,8 +98,7 @@ public class SecuritySettings implements SettingsClass {
"- '123456'",
"- 'password'"})
public static final Property> UNSAFE_PASSWORDS =
- newProperty(STRING_LIST, "settings.security.unsafePasswords",
- "123456", "password", "qwerty", "12345", "54321");
+ newListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321");
private SecuritySettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java
index 70d8ce23..5586a578 100644
--- a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java
+++ b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java
@@ -1,5 +1,7 @@
package fr.xephi.authme.settings.propertymap;
+import fr.xephi.authme.ConsoleLogger;
+
import java.util.ArrayList;
import java.util.List;
@@ -37,14 +39,13 @@ final class Node {
}
/**
- * Add a node to the root, creating any intermediary children that don't exist.
+ * Add a child node, creating any intermediary children that don't exist.
*
- * @param root The root to add the path to
* @param fullPath The entire path of the node to add, separate by periods
*/
- public static void addNode(Node root, String fullPath) {
+ public void addNode(String fullPath) {
String[] pathParts = fullPath.split("\\.");
- Node parent = root;
+ Node parent = this;
for (String part : pathParts) {
Node child = parent.getChild(part);
if (child == null) {
@@ -59,17 +60,16 @@ final class Node {
* Compare two nodes by this class' sorting behavior (insertion order).
* Note that this method assumes that both supplied paths exist in the tree.
*
- * @param root The root of the tree
* @param fullPath1 The full path to the first node
* @param fullPath2 The full path to the second node
* @return The comparison result, in the same format as {@link Comparable#compareTo}
*/
- public static int compare(Node root, String fullPath1, String fullPath2) {
+ public int compare(String fullPath1, String fullPath2) {
String[] path1 = fullPath1.split("\\.");
String[] path2 = fullPath2.split("\\.");
int commonCount = 0;
- Node commonNode = root;
+ Node commonNode = this;
while (commonCount < path1.length && commonCount < path2.length
&& path1[commonCount].equals(path2[commonCount]) && commonNode != null) {
commonNode = commonNode.getChild(path1[commonCount]);
@@ -77,7 +77,7 @@ final class Node {
}
if (commonNode == null) {
- System.err.println("Could not find common node for '" + fullPath1 + "' at index " + commonCount);
+ ConsoleLogger.showError("Could not find common node for '" + fullPath1 + "' at index " + commonCount);
return fullPath1.compareTo(fullPath2); // fallback
} else if (commonCount >= path1.length || commonCount >= path2.length) {
return Integer.compare(path1.length, path2.length);
diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java
index 77f5f60e..a7ec1cb3 100644
--- a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java
+++ b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java
@@ -23,12 +23,12 @@ final class PropertyMapComparator implements Comparator {
* @param property The property that is being added
*/
public void add(Property property) {
- Node.addNode(parent, property.getPath());
+ parent.addNode(property.getPath());
}
@Override
public int compare(Property p1, Property p2) {
- return Node.compare(parent, p1.getPath(), p2.getPath());
+ return parent.compare(p1.getPath(), p2.getPath());
}
}
diff --git a/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java b/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java
index 0551b8ca..c5ff5b95 100644
--- a/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java
+++ b/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java
@@ -53,12 +53,12 @@ public class ChangePasswordTask implements Runnable {
@Override
public void run() {
- ByteArrayDataOutput out = ByteStreams.newDataOutput();
- out.writeUTF("Forward");
- out.writeUTF("ALL");
- out.writeUTF("AuthMe");
- out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt);
- player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ out.writeUTF("Forward");
+ out.writeUTF("ALL");
+ out.writeUTF("AuthMe");
+ out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt);
+ player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
});
}
diff --git a/src/main/java/fr/xephi/authme/task/MessageTask.java b/src/main/java/fr/xephi/authme/task/MessageTask.java
index 92310f37..56d1493d 100644
--- a/src/main/java/fr/xephi/authme/task/MessageTask.java
+++ b/src/main/java/fr/xephi/authme/task/MessageTask.java
@@ -22,13 +22,13 @@ public class MessageTask implements Runnable {
*
* @param plugin AuthMe
* @param name String
- * @param strings String[]
+ * @param lines String[]
* @param interval int
*/
- public MessageTask(AuthMe plugin, String name, String[] strings, int interval) {
+ public MessageTask(AuthMe plugin, String name, String[] lines, int interval) {
this.plugin = plugin;
this.name = name;
- this.msg = strings;
+ this.msg = lines;
this.interval = interval;
}
@@ -49,7 +49,7 @@ public class MessageTask implements Runnable {
}
BukkitTask nextTask = plugin.getServer().getScheduler().runTaskLater(plugin, this, interval * 20);
if (LimboCache.getInstance().hasLimboPlayer(name)) {
- LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nextTask);
+ LimboCache.getInstance().getLimboPlayer(name).setMessageTask(nextTask);
}
return;
}
diff --git a/src/main/java/fr/xephi/authme/util/BukkitService.java b/src/main/java/fr/xephi/authme/util/BukkitService.java
new file mode 100644
index 00000000..053fe789
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/BukkitService.java
@@ -0,0 +1,57 @@
+package fr.xephi.authme.util;
+
+import fr.xephi.authme.AuthMe;
+import org.bukkit.Bukkit;
+
+/**
+ * Service for operations requiring server entities, such as for scheduling.
+ */
+public class BukkitService {
+
+ /** Number of ticks per second in the Bukkit main thread. */
+ public static final int TICKS_PER_SECOND = 20;
+ /** Number of ticks per minute. */
+ public static final int TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND;
+
+ private final AuthMe authMe;
+
+ public BukkitService(AuthMe authMe) {
+ this.authMe = authMe;
+ }
+
+ /**
+ * Schedules a once off task to occur as soon as possible.
+ *
+ * This task will be executed by the main server thread.
+ *
+ * @param task Task to be executed
+ * @return Task id number (-1 if scheduling failed)
+ */
+ public int scheduleSyncDelayedTask(Runnable task) {
+ return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task);
+ }
+
+ /**
+ * Schedules a once off task to occur after a delay.
+ *
+ * This task will be executed by the main server thread.
+ *
+ * @param task Task to be executed
+ * @param delay Delay in server ticks before executing task
+ * @return Task id number (-1 if scheduling failed)
+ */
+ public int scheduleSyncDelayedTask(Runnable task, long delay) {
+ return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task, delay);
+ }
+
+ /**
+ * Broadcast a message to all players.
+ *
+ * @param message the message
+ * @return the number of players
+ */
+ public int broadcastMessage(String message) {
+ return Bukkit.broadcastMessage(message);
+ }
+
+}
diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java
new file mode 100644
index 00000000..d34c41f8
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/FileUtils.java
@@ -0,0 +1,52 @@
+package fr.xephi.authme.util;
+
+import fr.xephi.authme.AuthMe;
+import fr.xephi.authme.ConsoleLogger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+
+import static java.lang.String.format;
+
+/**
+ * File utilities.
+ */
+public class FileUtils {
+
+ private FileUtils() {
+ }
+
+ /**
+ * Copy a resource file (from the JAR) to the given file if it doesn't exist.
+ *
+ * @param destinationFile The file to check and copy to (outside of JAR)
+ * @param resourcePath Absolute path to the resource file (path to file within JAR)
+ * @return False if the file does not exist and could not be copied, true otherwise
+ */
+ public static boolean copyFileFromResource(File destinationFile, String resourcePath) {
+ if (destinationFile.exists()) {
+ return true;
+ } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) {
+ ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'");
+ return false;
+ }
+
+ // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/'
+ final String normalizedPath = resourcePath.replace("\\", "/");
+ try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) {
+ if (is == null) {
+ ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource",
+ resourcePath, destinationFile.getPath()));
+ } else {
+ Files.copy(is, destinationFile.toPath());
+ return true;
+ }
+ } catch (IOException e) {
+ ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':",
+ resourcePath, destinationFile.getPath()), e);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java
index f878d85a..d5cba2d7 100644
--- a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java
+++ b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java
@@ -22,8 +22,11 @@ public class GeoLiteAPI {
private static LookupService lookupService;
private static Thread downloadTask;
+ private GeoLiteAPI() {
+ }
+
/**
- * Download (if absent) the GeoIpLite data file and then try to load it.
+ * Download (if absent or old) the GeoIpLite data file and then try to load it.
*
* @return True if the data is available, false otherwise.
*/
diff --git a/src/main/java/fr/xephi/authme/util/MigrationService.java b/src/main/java/fr/xephi/authme/util/MigrationService.java
index 6da4d95e..86f98e4c 100644
--- a/src/main/java/fr/xephi/authme/util/MigrationService.java
+++ b/src/main/java/fr/xephi/authme/util/MigrationService.java
@@ -5,6 +5,8 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
+import fr.xephi.authme.datasource.FlatFile;
+import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.SHA256;
@@ -29,6 +31,7 @@ public final class MigrationService {
* @param dataSource The data source
* @param authmeSha256 Instance to the AuthMe SHA256 encryption method implementation
*/
+ @SuppressWarnings("deprecation")
public static void changePlainTextToSha256(NewSetting settings, DataSource dataSource,
SHA256 authmeSha256) {
if (HashAlgorithm.PLAINTEXT == settings.getProperty(SecuritySettings.PASSWORD_HASH)) {
@@ -54,16 +57,22 @@ public final class MigrationService {
* @param dataSource The data source
* @return The converted datasource (SQLite), or null if no migration was necessary
*/
+ @SuppressWarnings("deprecation")
public static DataSource convertFlatfileToSqlite(NewSetting settings, DataSource dataSource) {
if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated; it will be changed "
+ "to SQLite... Connection will be impossible until conversion is done!");
- ForceFlatToSqlite converter = new ForceFlatToSqlite(dataSource, settings);
- DataSource result = converter.run();
- if (result == null) {
- throw new IllegalStateException("Error during conversion from flatfile to SQLite");
- } else {
- return result;
+ FlatFile flatFile = (FlatFile) dataSource;
+ try {
+ SQLite sqlite = new SQLite(settings);
+ ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, sqlite);
+ converter.run();
+ settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE);
+ settings.save();
+ return sqlite;
+ } catch (Exception e) {
+ ConsoleLogger.logException("Error during conversion from Flatfile to SQLite", e);
+ throw new IllegalStateException(e);
}
}
return null;
diff --git a/src/main/java/fr/xephi/authme/util/Profiler.java b/src/main/java/fr/xephi/authme/util/Profiler.java
index 0784aa65..73bdf5e4 100644
--- a/src/main/java/fr/xephi/authme/util/Profiler.java
+++ b/src/main/java/fr/xephi/authme/util/Profiler.java
@@ -2,15 +2,13 @@ package fr.xephi.authme.util;
import java.text.DecimalFormat;
-/**
- */
-@SuppressWarnings("UnusedDeclaration")
public class Profiler {
/**
* Defines the past time in milliseconds.
*/
private long time = 0;
+
/**
* Defines the time in milliseconds the profiler last started at.
*/
diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java
index f12c8b34..c399f655 100644
--- a/src/main/java/fr/xephi/authme/util/Utils.java
+++ b/src/main/java/fr/xephi/authme/util/Utils.java
@@ -9,10 +9,10 @@ import fr.xephi.authme.events.AuthMeTeleportEvent;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
-
import fr.xephi.authme.settings.properties.EmailSettings;
import org.bukkit.Bukkit;
import org.bukkit.Location;
+import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
@@ -154,8 +154,7 @@ public final class Utils {
public static boolean isUnrestricted(Player player) {
return Settings.isAllowRestrictedIp
- && !Settings.getUnrestrictedName.isEmpty()
- && (Settings.getUnrestrictedName.contains(player.getName().toLowerCase()));
+ && Settings.getUnrestrictedName.contains(player.getName().toLowerCase());
}
public static void packCoords(double x, double y, double z, String w, final Player pl) {
@@ -216,8 +215,7 @@ public final class Utils {
ConsoleLogger.showError("Unknown list of online players of type " + type);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- ConsoleLogger.showError("Could not retrieve list of online players: ["
- + e.getClass().getName() + "] " + e.getMessage());
+ ConsoleLogger.logException("Could not retrieve list of online players:", e);
}
return Collections.emptyList();
}
@@ -242,8 +240,7 @@ public final class Utils {
}
public static boolean isNPC(Player player) {
- return player.hasMetadata("NPC") || plugin.combatTagPlus != null
- && plugin.combatTagPlus.getNpcPlayerHelper().isNpc(player);
+ return player.hasMetadata("NPC") || plugin.getPluginHooks().isNpcInCombatTagPlus(player);
}
public static void teleportToSpawn(Player player) {
@@ -281,8 +278,16 @@ public final class Utils {
return false;
}
- /**
- */
+ public static String getUUIDorName(OfflinePlayer player) {
+ String uuidOrName;
+ try {
+ uuidOrName = player.getUniqueId().toString();
+ } catch (Exception ignore) {
+ uuidOrName = player.getName();
+ }
+ return uuidOrName;
+ }
+
public enum GroupType {
UNREGISTERED,
REGISTERED,
diff --git a/src/main/java/fr/xephi/authme/util/Wrapper.java b/src/main/java/fr/xephi/authme/util/Wrapper.java
index 77f65cff..bdd1d5a2 100644
--- a/src/main/java/fr/xephi/authme/util/Wrapper.java
+++ b/src/main/java/fr/xephi/authme/util/Wrapper.java
@@ -8,7 +8,6 @@ import org.bukkit.Server;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
-import java.util.logging.Logger;
/**
* Wrapper for the retrieval of common singletons used throughout the application.
@@ -48,10 +47,6 @@ public class Wrapper {
return getAuthMe().getServer();
}
- public Logger getLogger() {
- return getAuthMe().getLogger();
- }
-
public Messages getMessages() {
return getAuthMe().getMessages();
}
diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml
index 942460ac..04345292 100644
--- a/src/main/resources/messages/messages_de.yml
+++ b/src/main/resources/messages/messages_de.yml
@@ -1,5 +1,5 @@
unknown_user: '&cBenutzer ist nicht in der Datenbank'
-unsafe_spawn: '&cDeine Logoutposition war unsicher, du wurdest zum Spawn teleportiert'
+unsafe_spawn: '&cDeine Logoutposition war unsicher, Du wurdest zum Spawn teleportiert'
not_logged_in: '&cNicht eingeloggt!'
reg_voluntarily: 'Du kannst dich mit folgendem Befehl registrieren "/register "'
usage_log: '&cBenutze: /login '
@@ -9,11 +9,11 @@ reg_disabled: '&cRegistrierungen sind deaktiviert'
valid_session: '&2Erfolgreich eingeloggt!'
login: '&2Erfolgreich eingeloggt!'
vb_nonActiv: '&cDein Account wurde noch nicht aktiviert. Bitte prüfe Deine E-Mails!'
-user_regged: '&cBenutzername ist schon vergeben'
+user_regged: '&cDieser Benutzername ist schon vergeben'
usage_reg: '&cBenutze: /register '
-max_reg: '&cDu hast die maximale Anzahl an Accounts erreicht'
+max_reg: '&cDu hast die maximale Anzahl an Accounts erreicht.'
no_perm: '&4Du hast keine Rechte, um diese Aktion auszuführen!'
-error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator'
+error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator.'
login_msg: '&cBitte logge Dich ein mit "/login "'
reg_msg: '&3Bitte registriere Dich mit "/register "'
reg_email_msg: '&3Bitte registriere Dich mit "/register "'
@@ -21,44 +21,43 @@ usage_unreg: '&cBenutze: /unregister '
pwd_changed: '&2Passwort geändert!'
user_unknown: '&cBenutzername nicht registriert!'
password_error: '&cPasswörter stimmen nicht überein!'
-password_error_nick: '&cDu kannst nicht deinen Namen als Passwort nutzen!'
+password_error_nick: '&cDu kannst nicht Deinen Namen als Passwort nutzen!'
password_error_unsafe: '&cDu kannst nicht unsichere Passwörter nutzen!'
invalid_session: '&cUngültige Session. Bitte starte das Spiel neu oder warte, bis die Session abgelaufen ist'
-reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com zum registrieren'
+reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com zum Registrieren'
logged_in: '&cBereits eingeloggt!'
logout: '&2Erfolgreich ausgeloggt'
same_nick: '&4Jemand mit diesem Namen spielt bereits auf dem Server!'
registered: '&2Erfolgreich registriert!'
pass_len: '&cDein Passwort ist zu kurz oder zu lang!'
-reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen'
+reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen.'
timeout: '&4Zeitüberschreitung beim Login'
usage_changepassword: '&cBenutze: /changepassword '
-name_len: '&4Dein Nickname ist zu kurz oder zu lang'
+name_len: '&4Dein Nickname ist zu kurz oder zu lang.'
regex: '&4Dein Nickname enthält nicht erlaubte Zeichen. Zulässige Zeichen: REG_EX'
-add_email: '&3Bitte hinterlege Deine E-Mail Adresse: /email add '
+add_email: '&3Bitte hinterlege Deine E-Mail-Adresse: /email add '
recovery_email: '&3Passwort vergessen? Nutze "/email recovery " für ein neues Passwort'
usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha '
wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA'
valid_captcha: '&2Das Captcha ist korrekt!'
-kick_forvip: '&3Ein VIP Spieler hat den vollen Server betreten!'
+kick_forvip: '&3Ein VIP-Spieler hat den vollen Server betreten!'
kick_fullserver: '&4Der Server ist momentan voll, Sorry!'
usage_email_add: '&cBenutze: /email add '
usage_email_change: '&cBenutze: /email change '
usage_email_recovery: '&cBenutze: /email recovery '
-new_email_invalid: '&cDie neue Email ist ungültig!'
-old_email_invalid: '&cDie alte Email ist ungültig!'
-email_invalid: '&cUngültige Email'
-email_added: '&2Email hinzugefügt!'
-email_confirm: '&cBitte bestätige deine Email!'
-email_changed: '&2Email aktualisiert!'
-email_send: '&2Wiederherstellungs-Email wurde gesendet!'
-email_exists: '&cEine Wiederherstellungs-Email wurde bereits versandt! Nutze folgenden Befehl um eine neue Email zu versenden:'
-country_banned: '&4Dein Land ist gesperrt'
+new_email_invalid: '&cDie neue E-Mail ist ungültig!'
+old_email_invalid: '&cDie alte E-Mail ist ungültig!'
+email_invalid: '&cUngültige E-Mail!'
+email_added: '&2E-Mail hinzugefügt!'
+email_confirm: '&cBitte bestätige Deine E-Mail!'
+email_changed: '&2E-Mail aktualisiert!'
+email_send: '&2Wiederherstellungs-E-Mail wurde gesendet!'
+email_exists: '&cEine Wiederherstellungs-E-Mail wurde bereits versandt! Nutze folgenden Befehl um eine neue E-Mail zu versenden:'
+country_banned: '&4Dein Land ist gesperrt!'
antibot_auto_enabled: '&4[AntiBotService] AntiBotMod wurde aufgrund hoher Netzauslastung automatisch aktiviert!'
-antibot_auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei'
-kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du dich mit dem Server verbindest'
-# TODO two_factor_create: Missing tag %url
-two_factor_create: '&2Dein geheimer Code ist %code'
-# TODO email_already_used: '&4The email address is already being used'
-# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
-# TODO not_owner_error: 'You are not the owner of this account. Please try another name!'
\ No newline at end of file
+antibot_auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei.'
+kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor Du Dich mit dem Server verbindest.'
+two_factor_create: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url'
+email_already_used: '&4Diese E-Mail-Adresse wird bereits genutzt.'
+invalid_name_case: 'Dein registrierter Benutzername ist &2%valid&f - nicht &4%invalid&f.'
+not_owner_error: 'Du bist nicht der Besitzer dieses Accounts. Bitte wähle einen anderen Namen!'
diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml
index 64aa9099..5b474e2d 100644
--- a/src/main/resources/messages/messages_en.yml
+++ b/src/main/resources/messages/messages_en.yml
@@ -12,7 +12,7 @@ login: '&2Successful login!'
vb_nonActiv: '&cYour account isn''t activated yet, please check your emails!'
user_regged: '&cYou already have registered this username!'
usage_reg: '&cUsage: /register '
-max_reg: '&cYou have exceeded the maximum number of registrations for your connection!'
+max_reg: '&cYou have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection!'
no_perm: '&4You don''t have the permission to perform this action!'
error: '&4An unexpected error occurred, please contact an administrator!'
login_msg: '&cPlease, login with the command "/login "'
@@ -59,5 +59,5 @@ antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number
antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'
email_already_used: '&4The email address is already being used'
two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
-not_owner_error: 'You are not the owner of this account. Please try another name!'
+not_owner_error: 'You are not the owner of this account. Please choose another name!'
invalid_name_case: 'You should join using username %valid, not %invalid.'
diff --git a/src/test/java/fr/xephi/authme/AntiBotTest.java b/src/test/java/fr/xephi/authme/AntiBotTest.java
index 25e250fd..4dace294 100644
--- a/src/test/java/fr/xephi/authme/AntiBotTest.java
+++ b/src/test/java/fr/xephi/authme/AntiBotTest.java
@@ -1,35 +1,212 @@
package fr.xephi.authme;
-import fr.xephi.authme.settings.Settings;
-import fr.xephi.authme.util.WrapperMock;
+import fr.xephi.authme.output.MessageKey;
+import fr.xephi.authme.output.Messages;
+import fr.xephi.authme.permission.PermissionsManager;
+import fr.xephi.authme.permission.PlayerStatePermission;
+import fr.xephi.authme.settings.NewSetting;
+import fr.xephi.authme.settings.properties.ProtectionSettings;
+import fr.xephi.authme.util.BukkitService;
+import org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import java.util.List;
+
+import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE;
+import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+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.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Test for {@link AntiBot}.
*/
+@RunWith(MockitoJUnitRunner.class)
public class AntiBotTest {
- private WrapperMock wrapper;
+ @Mock
+ private NewSetting settings;
+ @Mock
+ private Messages messages;
+ @Mock
+ private PermissionsManager permissionsManager;
+ @Mock
+ private BukkitService bukkitService;
@Before
- public void setUpMocks() {
- wrapper = WrapperMock.createInstance();
+ public void setDefaultSettingValues() {
+ given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true);
}
@Test
- public void shouldNotEnableAntiBot() {
- // given
- Settings.enableAntiBot = false;
-
- // when
- AntiBot.setupAntiBotService();
+ public void shouldKeepAntiBotDisabled() {
+ // given / when
+ given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(false);
+ AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService);
// then
- verify(wrapper.getScheduler(), never()).scheduleSyncDelayedTask(any(AuthMe.class), any(Runnable.class));
+ verify(bukkitService, never()).scheduleSyncDelayedTask(any(Runnable.class), anyLong());
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.DISABLED));
}
+
+ @Test
+ public void shouldTransitionToListening() {
+ // given / when
+ AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService);
+ TestHelper.runSyncDelayedTaskWithDelay(bukkitService);
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING));
+ }
+
+ @Test
+ public void shouldSetStatusToActive() {
+ // given
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.overrideAntiBotStatus(true);
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.ACTIVE));
+ }
+
+ @Test
+ public void shouldSetStatusToListening() {
+ // given
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.overrideAntiBotStatus(false);
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING));
+ }
+
+ @Test
+ public void shouldRemainDisabled() {
+ // given
+ given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(false);
+ AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService);
+
+ // when
+ antiBot.overrideAntiBotStatus(true);
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.DISABLED));
+ }
+
+ @Test
+ public void shouldActivateAntiBot() {
+ // given
+ given(messages.retrieve(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE))
+ .willReturn(new String[]{"Test line #1", "Test line #2"});
+ int duration = 300;
+ given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(duration);
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.activateAntiBot();
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.ACTIVE));
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(bukkitService, times(2)).broadcastMessage(captor.capture());
+ assertThat(captor.getAllValues(), contains("Test line #1", "Test line #2"));
+ long expectedTicks = duration * TICKS_PER_MINUTE;
+ verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(expectedTicks));
+ }
+
+ @Test
+ public void shouldDisableAntiBotAfterSetDuration() {
+ // given
+ given(messages.retrieve(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)).willReturn(new String[0]);
+ given(messages.retrieve(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE))
+ .willReturn(new String[]{"Disabled...", "Placeholder: %m."});
+ given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(4);
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.activateAntiBot();
+ TestHelper.runSyncDelayedTaskWithDelay(bukkitService);
+
+ // then
+ assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING));
+ verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq((long) 4800));
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(bukkitService, times(2)).broadcastMessage(captor.capture());
+ assertThat(captor.getAllValues(), contains("Disabled...", "Placeholder: 4."));
+ }
+
+ @Test
+ public void shouldCheckPlayerAndRemoveHimLater() {
+ // given
+ Player player = mock(Player.class);
+ given(player.getName()).willReturn("Plaer");
+ given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false);
+ given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(10);
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.checkAntiBot(player);
+
+ // then
+ List playerList = (List) ReflectionTestUtils
+ .getFieldValue(AntiBot.class, antiBot, "antibotPlayers");
+ assertThat(playerList, hasSize(1));
+ verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq((long) 15 * TICKS_PER_SECOND));
+
+ // Follow-up: Check that player will be removed from list again by running the Runnable
+ // given (2)
+ // Add another player to the list
+ playerList.add("other_player");
+
+ // when (2)
+ TestHelper.runSyncDelayedTaskWithDelay(bukkitService);
+
+ // then (2)
+ assertThat(playerList, contains("other_player"));
+ }
+
+ @Test
+ public void shouldNotUpdateListForPlayerWithByPassPermission() {
+ // given
+ Player player = mock(Player.class);
+ given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(true);
+ given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(3);
+ AntiBot antiBot = createListeningAntiBot();
+
+ // when
+ antiBot.checkAntiBot(player);
+
+ // then
+ List> playerList = (List) ReflectionTestUtils.getFieldValue(AntiBot.class, antiBot, "antibotPlayers");
+ assertThat(playerList, empty());
+ verify(bukkitService, never()).scheduleSyncDelayedTask(any(Runnable.class), anyLong());
+ }
+
+ private AntiBot createListeningAntiBot() {
+ AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService);
+ TestHelper.runSyncDelayedTaskWithDelay(bukkitService);
+ // Make BukkitService forget about all interactions up to here
+ reset(bukkitService);
+ return antiBot;
+ }
+
}
diff --git a/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java b/src/test/java/fr/xephi/authme/AuthMeMatchers.java
similarity index 98%
rename from src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java
rename to src/test/java/fr/xephi/authme/AuthMeMatchers.java
index 798d5315..aed77cb1 100644
--- a/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java
+++ b/src/test/java/fr/xephi/authme/AuthMeMatchers.java
@@ -1,4 +1,4 @@
-package fr.xephi.authme.datasource;
+package fr.xephi.authme;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
diff --git a/src/test/java/fr/xephi/authme/TestHelper.java b/src/test/java/fr/xephi/authme/TestHelper.java
index 06ff06a4..5f1db10d 100644
--- a/src/test/java/fr/xephi/authme/TestHelper.java
+++ b/src/test/java/fr/xephi/authme/TestHelper.java
@@ -1,10 +1,17 @@
package fr.xephi.authme;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.util.BukkitService;
+import org.mockito.ArgumentCaptor;
+
import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.verify;
+
/**
* AuthMe test utilities.
*/
@@ -47,4 +54,46 @@ public final class TestHelper {
return url;
}
+ /**
+ * Execute a {@link Runnable} passed to a mock's {@link CommandService#runTaskAsynchronously} method.
+ * Note that calling this method expects that there be a runnable sent to the method and will fail
+ * otherwise.
+ *
+ * @param service The mock service
+ */
+ public static void runInnerRunnable(CommandService service) {
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(service).runTaskAsynchronously(captor.capture());
+ Runnable runnable = captor.getValue();
+ runnable.run();
+ }
+
+ /**
+ * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable)} method.
+ * Note that calling this method expects that there be a runnable sent to the method and will fail
+ * otherwise.
+ *
+ * @param service The mock service
+ */
+ public static void runSyncDelayedTask(BukkitService service) {
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(service).scheduleSyncDelayedTask(captor.capture());
+ Runnable runnable = captor.getValue();
+ runnable.run();
+ }
+
+ /**
+ * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable, long)}
+ * method. Note that calling this method expects that there be a runnable sent to the method and will fail
+ * otherwise.
+ *
+ * @param service The mock service
+ */
+ public static void runSyncDelayedTaskWithDelay(BukkitService service) {
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(service).scheduleSyncDelayedTask(captor.capture(), anyLong());
+ Runnable runnable = captor.getValue();
+ runnable.run();
+ }
+
}
diff --git a/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java b/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java
new file mode 100644
index 00000000..96489155
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java
@@ -0,0 +1,63 @@
+package fr.xephi.authme.cache;
+
+import fr.xephi.authme.settings.NewSetting;
+import fr.xephi.authme.settings.properties.SecuritySettings;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test for {@link CaptchaManager}.
+ */
+public class CaptchaManagerTest {
+
+ @Test
+ public void shouldAddCounts() {
+ // given
+ NewSetting settings = mockSettings(3, 4);
+ CaptchaManager manager = new CaptchaManager(settings);
+ String player = "tester";
+
+ // when
+ for (int i = 0; i < 2; ++i) {
+ manager.increaseCount(player);
+ }
+
+ // then
+ assertThat(manager.isCaptchaRequired(player), equalTo(false));
+ manager.increaseCount(player);
+ assertThat(manager.isCaptchaRequired(player.toUpperCase()), equalTo(true));
+ assertThat(manager.isCaptchaRequired("otherPlayer"), equalTo(false));
+ }
+
+ @Test
+ public void shouldCreateAndCheckCaptcha() {
+ // given
+ String player = "Miner";
+ NewSetting settings = mockSettings(1, 4);
+ CaptchaManager manager = new CaptchaManager(settings);
+ String captchaCode = manager.getCaptchaCode(player);
+
+ // when
+ boolean badResult = manager.checkCode(player, "wrong_code");
+ boolean goodResult = manager.checkCode(player, captchaCode);
+
+ // then
+ assertThat(captchaCode.length(), equalTo(4));
+ assertThat(badResult, equalTo(false));
+ assertThat(goodResult, equalTo(true));
+ // Supplying correct code should clear the entry, and any code should be valid if no entry is present
+ assertThat(manager.checkCode(player, "bogus"), equalTo(true));
+ }
+
+
+ private static NewSetting mockSettings(int maxTries, int captchaLength) {
+ NewSetting settings = mock(NewSetting.class);
+ given(settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA)).willReturn(maxTries);
+ given(settings.getProperty(SecuritySettings.CAPTCHA_LENGTH)).willReturn(captchaLength);
+ return settings;
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java b/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java
new file mode 100644
index 00000000..50c73d30
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java
@@ -0,0 +1,64 @@
+package fr.xephi.authme.cache;
+
+import fr.xephi.authme.settings.NewSetting;
+import fr.xephi.authme.settings.properties.HooksSettings;
+import org.bukkit.entity.Player;
+import org.junit.Test;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test for {@link IpAddressManager}.
+ */
+public class IpAddressManagerTest {
+
+ @Test
+ public void shouldRetrieveFromCache() {
+ // given
+ IpAddressManager ipAddressManager = new IpAddressManager(mockSettings(true));
+ ipAddressManager.addCache("Test", "my test IP");
+
+ // when
+ String result = ipAddressManager.getPlayerIp(mockPlayer("test", "123.123.123.123"));
+
+ // then
+ assertThat(result, equalTo("my test IP"));
+ }
+
+ @Test
+ public void shouldReturnPlainIp() {
+ // given
+ IpAddressManager ipAddressManager = new IpAddressManager(mockSettings(false));
+
+ // when
+ String result = ipAddressManager.getPlayerIp(mockPlayer("bobby", "8.8.8.8"));
+
+ // then
+ assertThat(result, equalTo("8.8.8.8"));
+ }
+
+
+
+ private static NewSetting mockSettings(boolean useVeryGames) {
+ NewSetting settings = mock(NewSetting.class);
+ given(settings.getProperty(HooksSettings.ENABLE_VERYGAMES_IP_CHECK)).willReturn(useVeryGames);
+ return settings;
+ }
+
+ private static Player mockPlayer(String name, String ip) {
+ Player player = mock(Player.class);
+ given(player.getName()).willReturn(name);
+ InetAddress inetAddress = mock(InetAddress.class);
+ given(inetAddress.getHostAddress()).willReturn(ip);
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 8093);
+ given(player.getAddress()).willReturn(inetSocketAddress);
+ return player;
+ }
+
+}
diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java
index 62c2b890..7e62023f 100644
--- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java
+++ b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java
@@ -1,14 +1,13 @@
package fr.xephi.authme.command;
-import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
+import fr.xephi.authme.permission.AdminPermission;
+import fr.xephi.authme.permission.PermissionNode;
+import fr.xephi.authme.util.StringUtils;
+import fr.xephi.authme.util.WrapperMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -17,13 +16,12 @@ import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import fr.xephi.authme.permission.AdminPermission;
-import fr.xephi.authme.permission.PermissionNode;
-import fr.xephi.authme.util.StringUtils;
-import fr.xephi.authme.util.WrapperMock;
+import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
/**
* Test for {@link CommandInitializer} to guarantee the integrity of the defined commands.
@@ -241,15 +239,11 @@ public class CommandInitializerTest {
public void shouldNotHavePlayerPermissionIfDefaultsToOpOnly() {
// given
BiConsumer adminPermissionChecker = new BiConsumer() {
- // The only exception to this check is the force login command, which should default to OP_ONLY
- // but semantically it is a player permission
- final List forceLoginLabels = Arrays.asList("forcelogin", "login");
-
@Override
public void accept(CommandDescription command, int depth) {
CommandPermissions permissions = command.getCommandPermissions();
if (permissions != null && OP_ONLY.equals(permissions.getDefaultPermission())) {
- if (!hasAdminNode(permissions) && !command.getLabels().equals(forceLoginLabels)) {
+ if (!hasAdminNode(permissions)) {
fail("The command with labels " + command.getLabels() + " has OP_ONLY default "
+ "permission but no permission node on admin level");
}
diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java
index 929fd26a..d34ee633 100644
--- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java
+++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java
@@ -1,23 +1,29 @@
package fr.xephi.authme.command;
+import fr.xephi.authme.AntiBot;
import fr.xephi.authme.AuthMe;
+import fr.xephi.authme.cache.IpAddressManager;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.hooks.PluginHooks;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.settings.NewSetting;
-import fr.xephi.authme.settings.properties.SecuritySettings;
+import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.domain.Property;
+import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
-import java.io.File;
import java.util.Arrays;
import java.util.List;
@@ -31,28 +37,37 @@ import static org.mockito.Mockito.verify;
/**
* Test for {@link CommandService}.
*/
+@RunWith(MockitoJUnitRunner.class)
public class CommandServiceTest {
- private AuthMe authMe;
- private CommandMapper commandMapper;
- private HelpProvider helpProvider;
- private Messages messages;
- private PasswordSecurity passwordSecurity;
private CommandService commandService;
+ @Mock
+ private AuthMe authMe;
+ @Mock
+ private CommandMapper commandMapper;
+ @Mock
+ private HelpProvider helpProvider;
+ @Mock
+ private Messages messages;
+ @Mock
+ private PasswordSecurity passwordSecurity;
+ @Mock
private PermissionsManager permissionsManager;
+ @Mock
private NewSetting settings;
+ @Mock
+ private IpAddressManager ipAddressManager;
+ @Mock
+ private PluginHooks pluginHooks;
+ @Mock
+ private SpawnLoader spawnLoader;
+ @Mock
+ private AntiBot antiBot;
@Before
public void setUpService() {
- authMe = mock(AuthMe.class);
- commandMapper = mock(CommandMapper.class);
- helpProvider = mock(HelpProvider.class);
- messages = mock(Messages.class);
- passwordSecurity = mock(PasswordSecurity.class);
- permissionsManager = mock(PermissionsManager.class);
- settings = mock(NewSetting.class);
- commandService = new CommandService(
- authMe, commandMapper, helpProvider, messages, passwordSecurity, permissionsManager, settings);
+ commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity,
+ permissionsManager, settings, ipAddressManager, pluginHooks, spawnLoader, antiBot);
}
@Test
@@ -187,18 +202,6 @@ public class CommandServiceTest {
verify(settings).getProperty(property);
}
- @Test
- public void shouldReloadMessages() {
- // given
- File file = new File("some/bogus-file.test");
-
- // when
- commandService.reloadMessages(file);
-
- // then
- verify(messages).reload(file);
- }
-
@Test
public void shouldReturnSettings() {
// given/when
@@ -216,4 +219,13 @@ public class CommandServiceTest {
// then
assertThat(result, equalTo(authMe));
}
+
+ @Test
+ public void shouldReturnIpAddressManager() {
+ // given/when
+ IpAddressManager ipManager = commandService.getIpAddressManager();
+
+ // then
+ assertThat(ipManager, equalTo(ipAddressManager));
+ }
}
diff --git a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java
index 0f4dff18..e3970fa6 100644
--- a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java
+++ b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java
@@ -30,7 +30,7 @@ public class CommandUtilsTest {
@Test
public void shouldPrintEmptyStringForNoArguments() {
// given
- List parts = Collections.EMPTY_LIST;
+ List parts = Collections.emptyList();
// when
String str = CommandUtils.labelsToString(parts);
diff --git a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java
index 3fcc05ca..4afee564 100644
--- a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java
+++ b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java
@@ -28,7 +28,7 @@ public class PlayerCommandTest {
PlayerCommandImpl command = new PlayerCommandImpl();
// when
- command.executeCommand(sender, Collections.EMPTY_LIST, mock(CommandService.class));
+ command.executeCommand(sender, Collections. emptyList(), mock(CommandService.class));
// then
ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
@@ -58,7 +58,7 @@ public class PlayerCommandTest {
PlayerCommandWithAlt command = new PlayerCommandWithAlt();
// when
- command.executeCommand(sender, Collections.EMPTY_LIST, mock(CommandService.class));
+ command.executeCommand(sender, Collections. emptyList(), mock(CommandService.class));
// then
ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java
index 92bcf4fc..82469635 100644
--- a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java
@@ -13,6 +13,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import static fr.xephi.authme.TestHelper.runInnerRunnable;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.BDDMockito.given;
@@ -46,7 +47,7 @@ public class AccountsCommandTest {
public void shouldGetAccountsOfCurrentUser() {
// given
given(sender.getName()).willReturn("Tester");
- List arguments = Collections.EMPTY_LIST;
+ List arguments = Collections.emptyList();
given(dataSource.getAuth("tester")).willReturn(authWithIp("123.45.67.89"));
given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Arrays.asList("Toaster", "Pester"));
@@ -80,7 +81,7 @@ public class AccountsCommandTest {
// given
List arguments = Collections.singletonList("SomeUser");
given(dataSource.getAuth("someuser")).willReturn(mock(PlayerAuth.class));
- given(dataSource.getAllAuthsByIp(anyString())).willReturn(Collections.EMPTY_LIST);
+ given(dataSource.getAllAuthsByIp(anyString())).willReturn(Collections. emptyList());
// when
command.executeCommand(sender, arguments, service);
@@ -114,7 +115,7 @@ public class AccountsCommandTest {
public void shouldReturnIpUnknown() {
// given
List arguments = Collections.singletonList("123.45.67.89");
- given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Collections.EMPTY_LIST);
+ given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Collections. emptyList());
// when
command.executeCommand(sender, arguments, service);
@@ -156,13 +157,6 @@ public class AccountsCommandTest {
assertThat(messages[1], containsString("Tester, Lester, Taster"));
}
- private static void runInnerRunnable(CommandService service) {
- ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
- verify(service).runTaskAsynchronously(captor.capture());
- Runnable runnable = captor.getValue();
- runnable.run();
- }
-
private static String[] getMessagesSentToSender(CommandSender sender, int expectedCount) {
ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
verify(sender, times(expectedCount)).sendMessage(captor.capture());
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java
new file mode 100644
index 00000000..8be1185a
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java
@@ -0,0 +1,38 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import org.bukkit.command.CommandSender;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link AuthMeCommand}.
+ */
+public class AuthMeCommandTest {
+
+ @Test
+ public void shouldDisplayInformation() {
+ // given
+ ExecutableCommand command = new AuthMeCommand();
+ CommandSender sender = mock(CommandSender.class);
+ CommandService service = mock(CommandService.class);
+
+ // when
+ command.executeCommand(sender, Collections. emptyList(), service);
+
+ // then
+ ArgumentCaptor messagesCaptor = ArgumentCaptor.forClass(String.class);
+ verify(sender, times(3)).sendMessage(messagesCaptor.capture());
+ assertThat(messagesCaptor.getAllValues().get(1), containsString("/authme help"));
+ assertThat(messagesCaptor.getAllValues().get(2), containsString("/authme about"));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java
new file mode 100644
index 00000000..28a6a3f8
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java
@@ -0,0 +1,253 @@
+package fr.xephi.authme.command.executable.authme;
+
+import com.google.common.base.Strings;
+import fr.xephi.authme.ConsoleLoggerTestInitializer;
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.cache.auth.PlayerCache;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.output.MessageKey;
+import fr.xephi.authme.security.PasswordSecurity;
+import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.settings.properties.RestrictionSettings;
+import fr.xephi.authme.settings.properties.SecuritySettings;
+import org.bukkit.command.CommandSender;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static fr.xephi.authme.TestHelper.runInnerRunnable;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link ChangePasswordAdminCommand}.
+ */
+public class ChangePasswordAdminCommandTest {
+
+ private CommandService service;
+
+ @BeforeClass
+ public static void setUpLogger() {
+ ConsoleLoggerTestInitializer.setupLogger();
+ }
+
+ @Before
+ public void setUpServiceMock() {
+ service = mock(CommandService.class);
+ given(service.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).willReturn("[a-zA-Z]+");
+ given(service.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).willReturn(3);
+ given(service.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).willReturn(20);
+ given(service.getProperty(SecuritySettings.UNSAFE_PASSWORDS))
+ .willReturn(Arrays.asList("unsafe", "otherUnsafe"));
+ }
+
+ @Test
+ public void shouldRejectPasswordSameAsUsername() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ // when
+ command.executeCommand(sender, Arrays.asList("bobby", "Bobby"), service);
+
+ // then
+ verify(service).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR);
+ verify(service, never()).getDataSource();
+ }
+
+ @Test
+ public void shouldRejectPasswordNotMatchingPattern() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+ // service mock returns pattern a-zA-Z -> numbers should not be accepted
+ String invalidPassword = "invalid1234";
+
+ // when
+ command.executeCommand(sender, Arrays.asList("myPlayer123", invalidPassword), service);
+
+ // then
+ verify(service).send(sender, MessageKey.PASSWORD_MATCH_ERROR);
+ verify(service, never()).getDataSource();
+ }
+
+ @Test
+ public void shouldRejectTooShortPassword() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ // when
+ command.executeCommand(sender, Arrays.asList("player", "ab"), service);
+
+ // then
+ verify(service).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
+ verify(service, never()).getDataSource();
+ }
+
+ @Test
+ public void shouldRejectTooLongPassword() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ // when
+ command.executeCommand(sender, Arrays.asList("player", Strings.repeat("a", 30)), service);
+
+ // then
+ verify(service).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
+ verify(service, never()).getDataSource();
+ }
+
+ @Test
+ public void shouldRejectUnsafePassword() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ // when
+ command.executeCommand(sender, Arrays.asList("player", "unsafe"), service);
+
+ // then
+ verify(service).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR);
+ verify(service, never()).getDataSource();
+ }
+
+ @Test
+ public void shouldRejectCommandForUnknownUser() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+ String player = "player";
+
+ PlayerCache playerCache = mock(PlayerCache.class);
+ given(playerCache.isAuthenticated(player)).willReturn(false);
+ given(service.getPlayerCache()).willReturn(playerCache);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(player)).willReturn(null);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ // when
+ command.executeCommand(sender, Arrays.asList(player, "password"), service);
+ runInnerRunnable(service);
+
+ // then
+ verify(service).send(sender, MessageKey.UNKNOWN_USER);
+ verify(dataSource, never()).updatePassword(any(PlayerAuth.class));
+ }
+
+ @Test
+ public void shouldUpdatePasswordOfLoggedInUser() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ String player = "my_user12";
+ String password = "passPass";
+ PlayerAuth auth = mock(PlayerAuth.class);
+
+ PlayerCache playerCache = mock(PlayerCache.class);
+ given(playerCache.isAuthenticated(player)).willReturn(true);
+ given(playerCache.getAuth(player)).willReturn(auth);
+ given(service.getPlayerCache()).willReturn(playerCache);
+
+ PasswordSecurity passwordSecurity = mock(PasswordSecurity.class);
+ HashedPassword hashedPassword = mock(HashedPassword.class);
+ given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword);
+ given(service.getPasswordSecurity()).willReturn(passwordSecurity);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.updatePassword(auth)).willReturn(true);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ // when
+ command.executeCommand(sender, Arrays.asList(player, password), service);
+ runInnerRunnable(service);
+
+ // then
+ verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
+ verify(passwordSecurity).computeHash(password, player);
+ verify(auth).setPassword(hashedPassword);
+ verify(dataSource).updatePassword(auth);
+ }
+
+ @Test
+ public void shouldUpdatePasswordOfOfflineUser() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ String player = "my_user12";
+ String password = "passPass";
+ PlayerAuth auth = mock(PlayerAuth.class);
+
+ PlayerCache playerCache = mock(PlayerCache.class);
+ given(playerCache.isAuthenticated(player)).willReturn(false);
+ given(service.getPlayerCache()).willReturn(playerCache);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.isAuthAvailable(player)).willReturn(true);
+ given(dataSource.getAuth(player)).willReturn(auth);
+ given(dataSource.updatePassword(auth)).willReturn(true);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ PasswordSecurity passwordSecurity = mock(PasswordSecurity.class);
+ HashedPassword hashedPassword = mock(HashedPassword.class);
+ given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword);
+ given(service.getPasswordSecurity()).willReturn(passwordSecurity);
+
+ // when
+ command.executeCommand(sender, Arrays.asList(player, password), service);
+ runInnerRunnable(service);
+
+ // then
+ verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
+ verify(passwordSecurity).computeHash(password, player);
+ verify(auth).setPassword(hashedPassword);
+ verify(dataSource).updatePassword(auth);
+ }
+
+ @Test
+ public void shouldReportWhenSaveFailed() {
+ // given
+ ExecutableCommand command = new ChangePasswordAdminCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ String player = "my_user12";
+ String password = "passPass";
+ PlayerAuth auth = mock(PlayerAuth.class);
+
+ PlayerCache playerCache = mock(PlayerCache.class);
+ given(playerCache.isAuthenticated(player)).willReturn(true);
+ given(playerCache.getAuth(player)).willReturn(auth);
+ given(service.getPlayerCache()).willReturn(playerCache);
+
+ PasswordSecurity passwordSecurity = mock(PasswordSecurity.class);
+ HashedPassword hashedPassword = mock(HashedPassword.class);
+ given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword);
+ given(service.getPasswordSecurity()).willReturn(passwordSecurity);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.updatePassword(auth)).willReturn(false);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ // when
+ command.executeCommand(sender, Arrays.asList(player, password), service);
+ runInnerRunnable(service);
+
+ // then
+ verify(service).send(sender, MessageKey.ERROR);
+ verify(passwordSecurity).computeHash(password, player);
+ verify(auth).setPassword(hashedPassword);
+ verify(dataSource).updatePassword(auth);
+ }
+
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java
new file mode 100644
index 00000000..aa4f7b74
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java
@@ -0,0 +1,65 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.settings.SpawnLoader;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link FirstSpawnCommand}.
+ */
+public class FirstSpawnCommandTest {
+
+ @Test
+ public void shouldTeleportToFirstSpawn() {
+ // given
+ Location firstSpawn = mock(Location.class);
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.getFirstSpawn()).willReturn(firstSpawn);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+ Player player = mock(Player.class);
+ ExecutableCommand command = new FirstSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(player).teleport(firstSpawn);
+ verify(spawnLoader, atLeastOnce()).getFirstSpawn();
+ }
+
+ @Test
+ public void shouldHandleMissingFirstSpawn() {
+ // given
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.getFirstSpawn()).willReturn(null);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+ Player player = mock(Player.class);
+ ExecutableCommand command = new FirstSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(player).sendMessage(captor.capture());
+ assertThat(captor.getValue(), containsString("spawn has failed"));
+ verify(player, never()).teleport(any(Location.class));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java
new file mode 100644
index 00000000..247c43ac
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java
@@ -0,0 +1,68 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.output.MessageKey;
+import org.bukkit.command.CommandSender;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link GetEmailCommand}.
+ */
+public class GetEmailCommandTest {
+
+ @Test
+ public void shouldReportUnknownUser() {
+ // given
+ String user = "myTestUser";
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(user)).willReturn(null);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new GetEmailCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(user), service);
+
+ // then
+ verify(service).send(sender, MessageKey.UNKNOWN_USER);
+ }
+
+ @Test
+ public void shouldReturnEmail() {
+ // given
+ String user = "userToView";
+ String email = "user.email@example.org";
+ PlayerAuth auth = mock(PlayerAuth.class);
+ given(auth.getEmail()).willReturn(email);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(user)).willReturn(auth);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new GetEmailCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(user), service);
+
+ // then
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(sender).sendMessage(captor.capture());
+ assertThat(captor.getValue(), containsString(email));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java
new file mode 100644
index 00000000..96cf8844
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java
@@ -0,0 +1,119 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.output.MessageKey;
+import org.bukkit.command.CommandSender;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+import java.util.Date;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link LastLoginCommand}.
+ */
+public class LastLoginCommandTest {
+
+ private static final long HOUR_IN_MSEC = 3600 * 1000;
+ private static final long DAY_IN_MSEC = 24 * HOUR_IN_MSEC;
+
+ @Test
+ public void shouldRejectNonExistentUser() {
+ // given
+ String player = "tester";
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(player)).willReturn(null);
+
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new LastLoginCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(player), service);
+
+ // then
+ verify(dataSource).getAuth(player);
+ verify(service).send(sender, MessageKey.USER_NOT_REGISTERED);
+ }
+
+ @Test
+ public void shouldDisplayLastLoginOfUser() {
+ // given
+ String player = "SomePlayer";
+ long lastLogin = System.currentTimeMillis() -
+ (412 * DAY_IN_MSEC + 10 * HOUR_IN_MSEC - 9000);
+ PlayerAuth auth = mock(PlayerAuth.class);
+ given(auth.getLastLogin()).willReturn(lastLogin);
+ given(auth.getIp()).willReturn("123.45.66.77");
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(player)).willReturn(auth);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new LastLoginCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(player), service);
+
+ // then
+ verify(dataSource).getAuth(player);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(sender, times(3)).sendMessage(captor.capture());
+ String lastLoginString = new Date(lastLogin).toString();
+ assertThat(captor.getAllValues().get(0),
+ allOf(containsString(player), containsString(lastLoginString)));
+ assertThat(captor.getAllValues().get(1), containsString("412 days 9 hours"));
+ assertThat(captor.getAllValues().get(2), containsString("123.45.66.77"));
+ }
+
+ @Test
+ public void shouldDisplayLastLoginOfCommandSender() {
+ // given
+ String name = "CommandSender";
+ CommandSender sender = mock(CommandSender.class);
+ given(sender.getName()).willReturn(name);
+
+ long lastLogin = System.currentTimeMillis() -
+ (412 * DAY_IN_MSEC + 10 * HOUR_IN_MSEC - 9000);
+ PlayerAuth auth = mock(PlayerAuth.class);
+ given(auth.getLastLogin()).willReturn(lastLogin);
+ given(auth.getIp()).willReturn("123.45.66.77");
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(name)).willReturn(auth);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+
+ ExecutableCommand command = new LastLoginCommand();
+
+ // when
+ command.executeCommand(sender, Collections. emptyList(), service);
+
+ // then
+ verify(dataSource).getAuth(name);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(sender, times(3)).sendMessage(captor.capture());
+ String lastLoginString = new Date(lastLogin).toString();
+ assertThat(captor.getAllValues().get(0),
+ allOf(containsString(name), containsString(lastLoginString)));
+ assertThat(captor.getAllValues().get(1), containsString("412 days 9 hours"));
+ assertThat(captor.getAllValues().get(2), containsString("123.45.66.77"));
+ }
+
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java
new file mode 100644
index 00000000..9f207866
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java
@@ -0,0 +1,124 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.output.MessageKey;
+import org.bukkit.command.CommandSender;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link PurgeLastPositionCommand}.
+ */
+public class PurgeLastPositionCommandTest {
+
+ @Test
+ public void shouldPurgeLastPosOfUser() {
+ // given
+ String player = "_Bobby";
+ PlayerAuth auth = mock(PlayerAuth.class);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(player)).willReturn(auth);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new PurgeLastPositionCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(player), service);
+
+ // then
+ verify(dataSource).getAuth(player);
+ verifyPositionWasReset(auth);
+ verify(sender).sendMessage(argThat(containsString("last position location is now reset")));
+ }
+
+ @Test
+ public void shouldPurgePositionOfCommandSender() {
+ // given
+ String player = "_Bobby";
+ CommandSender sender = mock(CommandSender.class);
+ given(sender.getName()).willReturn(player);
+
+ PlayerAuth auth = mock(PlayerAuth.class);
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAuth(player)).willReturn(auth);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ ExecutableCommand command = new PurgeLastPositionCommand();
+
+ // when
+ command.executeCommand(sender, Collections. emptyList(), service);
+
+ // then
+ verify(dataSource).getAuth(player);
+ verifyPositionWasReset(auth);
+ verify(sender).sendMessage(argThat(containsString("position location is now reset")));
+ }
+
+ @Test
+ public void shouldHandleNonExistentUser() {
+ // given
+ DataSource dataSource = mock(DataSource.class);
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ ExecutableCommand command = new PurgeLastPositionCommand();
+ CommandSender sender = mock(CommandSender.class);
+ String name = "invalidPlayer";
+
+ // when
+ command.executeCommand(sender, Collections.singletonList(name), service);
+
+ // then
+ verify(dataSource).getAuth(name);
+ verify(service).send(sender, MessageKey.UNKNOWN_USER);
+ }
+
+ @Test
+ public void shouldResetAllLastPositions() {
+ // given
+ PlayerAuth auth1 = mock(PlayerAuth.class);
+ PlayerAuth auth2 = mock(PlayerAuth.class);
+ PlayerAuth auth3 = mock(PlayerAuth.class);
+
+ DataSource dataSource = mock(DataSource.class);
+ given(dataSource.getAllAuths()).willReturn(Arrays.asList(auth1, auth2, auth3));
+ CommandService service = mock(CommandService.class);
+ given(service.getDataSource()).willReturn(dataSource);
+
+ ExecutableCommand command = new PurgeLastPositionCommand();
+ CommandSender sender = mock(CommandSender.class);
+
+ // when
+ command.executeCommand(sender, Collections.singletonList("*"), service);
+
+ // then
+ verify(dataSource).getAllAuths();
+ verifyPositionWasReset(auth1);
+ verifyPositionWasReset(auth2);
+ verifyPositionWasReset(auth3);
+ verify(sender).sendMessage(argThat(containsString("last position locations are now reset")));
+ }
+
+
+ private static void verifyPositionWasReset(PlayerAuth auth) {
+ verify(auth).setQuitLocX(0);
+ verify(auth).setQuitLocY(0);
+ verify(auth).setQuitLocZ(0);
+ verify(auth).setWorld("world");
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java
new file mode 100644
index 00000000..873108ee
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java
@@ -0,0 +1,65 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.AuthMe;
+import fr.xephi.authme.ConsoleLoggerTestInitializer;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.output.MessageKey;
+import org.bukkit.command.CommandSender;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.matches;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link ReloadCommand}.
+ */
+public class ReloadCommandTest {
+
+ @BeforeClass
+ public static void setUpLogger() {
+ ConsoleLoggerTestInitializer.setupLogger();
+ }
+
+ @Test
+ public void shouldReload() throws Exception {
+ // given
+ AuthMe authMe = mock(AuthMe.class);
+ CommandService service = mock(CommandService.class);
+ given(service.getAuthMe()).willReturn(authMe);
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new ReloadCommand();
+
+ // when
+ command.executeCommand(sender, Collections. emptyList(), service);
+
+ // then
+ verify(authMe).reload();
+ verify(service).send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
+ }
+
+ @Test
+ public void shouldHandleReloadError() throws Exception {
+ // given
+ AuthMe authMe = mock(AuthMe.class);
+ doThrow(IllegalStateException.class).when(authMe).reload();
+ CommandService service = mock(CommandService.class);
+ given(service.getAuthMe()).willReturn(authMe);
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new ReloadCommand();
+
+ // when
+ command.executeCommand(sender, Collections. emptyList(), service);
+
+ // then
+ verify(authMe).reload();
+ verify(sender).sendMessage(matches("Error occurred.*"));
+ verify(authMe).stopOrUnload();
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java
new file mode 100644
index 00000000..307831ce
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java
@@ -0,0 +1,66 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.settings.SpawnLoader;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link SetFirstSpawnCommand}.
+ */
+public class SetFirstSpawnCommandTest {
+
+ @Test
+ public void shouldSetFirstSpawn() {
+ // given
+ Player player = mock(Player.class);
+ Location location = mock(Location.class);
+ given(player.getLocation()).willReturn(location);
+
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.setFirstSpawn(location)).willReturn(true);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+
+ ExecutableCommand command = new SetFirstSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(spawnLoader).setFirstSpawn(location);
+ verify(player).sendMessage(argThat(containsString("defined new first spawn")));
+ }
+
+ @Test
+ public void shouldHandleError() {
+ // given
+ Player player = mock(Player.class);
+ Location location = mock(Location.class);
+ given(player.getLocation()).willReturn(location);
+
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.setFirstSpawn(location)).willReturn(false);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+
+ ExecutableCommand command = new SetFirstSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(spawnLoader).setFirstSpawn(location);
+ verify(player).sendMessage(argThat(containsString("has failed")));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java
new file mode 100644
index 00000000..11c28d32
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java
@@ -0,0 +1,66 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.settings.SpawnLoader;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link SetSpawnCommand}.
+ */
+public class SetSpawnCommandTest {
+
+ @Test
+ public void shouldSetSpawn() {
+ // given
+ Player player = mock(Player.class);
+ Location location = mock(Location.class);
+ given(player.getLocation()).willReturn(location);
+
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.setSpawn(location)).willReturn(true);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+
+ ExecutableCommand command = new SetSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(spawnLoader).setSpawn(location);
+ verify(player).sendMessage(argThat(containsString("defined new spawn")));
+ }
+
+ @Test
+ public void shouldHandleError() {
+ // given
+ Player player = mock(Player.class);
+ Location location = mock(Location.class);
+ given(player.getLocation()).willReturn(location);
+
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.setSpawn(location)).willReturn(false);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+
+ ExecutableCommand command = new SetSpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(spawnLoader).setSpawn(location);
+ verify(player).sendMessage(argThat(containsString("has failed")));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java
new file mode 100644
index 00000000..7b76ad02
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java
@@ -0,0 +1,62 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.settings.SpawnLoader;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link SpawnCommand}.
+ */
+public class SpawnCommandTest {
+
+ @Test
+ public void shouldTeleportToSpawn() {
+ // given
+ Location spawn = mock(Location.class);
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.getSpawn()).willReturn(spawn);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+ Player player = mock(Player.class);
+ ExecutableCommand command = new SpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(player).teleport(spawn);
+ verify(spawnLoader, atLeastOnce()).getSpawn();
+ }
+
+ @Test
+ public void shouldHandleMissingSpawn() {
+ // given
+ SpawnLoader spawnLoader = mock(SpawnLoader.class);
+ given(spawnLoader.getSpawn()).willReturn(null);
+ CommandService service = mock(CommandService.class);
+ given(service.getSpawnLoader()).willReturn(spawnLoader);
+ Player player = mock(Player.class);
+ ExecutableCommand command = new SpawnCommand();
+
+ // when
+ command.executeCommand(player, Collections. emptyList(), service);
+
+ // then
+ verify(player).sendMessage(argThat(containsString("Spawn has failed")));
+ verify(player, never()).teleport(any(Location.class));
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java
new file mode 100644
index 00000000..3f9767f9
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java
@@ -0,0 +1,99 @@
+package fr.xephi.authme.command.executable.authme;
+
+import fr.xephi.authme.AntiBot;
+import fr.xephi.authme.command.CommandService;
+import fr.xephi.authme.command.ExecutableCommand;
+import fr.xephi.authme.command.FoundCommandResult;
+import fr.xephi.authme.command.help.HelpProvider;
+import org.bukkit.command.CommandSender;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static java.util.Arrays.asList;
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link SwitchAntiBotCommand}.
+ */
+public class SwitchAntiBotCommandTest {
+
+ @Test
+ public void shouldReturnAntiBotState() {
+ // given
+ AntiBot antiBot = mock(AntiBot.class);
+ given(antiBot.getAntiBotStatus()).willReturn(AntiBot.AntiBotStatus.ACTIVE);
+ CommandService service = mock(CommandService.class);
+ given(service.getAntiBot()).willReturn(antiBot);
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new SwitchAntiBotCommand();
+
+ // when
+ command.executeCommand(sender, Collections.emptyList(), service);
+
+ // then
+ verify(sender).sendMessage(argThat(containsString("status: ACTIVE")));
+ }
+
+ @Test
+ public void shouldActivateAntiBot() {
+ // given
+ AntiBot antiBot = mock(AntiBot.class);
+ CommandService service = mock(CommandService.class);
+ given(service.getAntiBot()).willReturn(antiBot);
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new SwitchAntiBotCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList("on"), service);
+
+ // then
+ verify(antiBot).overrideAntiBotStatus(true);
+ verify(sender).sendMessage(argThat(containsString("enabled")));
+ }
+
+ @Test
+ public void shouldDeactivateAntiBot() {
+ // given
+ AntiBot antiBot = mock(AntiBot.class);
+ CommandService service = mock(CommandService.class);
+ given(service.getAntiBot()).willReturn(antiBot);
+ CommandSender sender = mock(CommandSender.class);
+ ExecutableCommand command = new SwitchAntiBotCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList("Off"), service);
+
+ // then
+ verify(antiBot).overrideAntiBotStatus(false);
+ verify(sender).sendMessage(argThat(containsString("disabled")));
+ }
+
+ @Test
+ public void shouldShowHelpForUnknownState() {
+ // given
+ CommandSender sender = mock(CommandSender.class);
+
+ AntiBot antiBot = mock(AntiBot.class);
+ FoundCommandResult foundCommandResult = mock(FoundCommandResult.class);
+ CommandService service = mock(CommandService.class);
+ given(service.getAntiBot()).willReturn(antiBot);
+ given(service.mapPartsToCommand(sender, asList("authme", "antibot"))).willReturn(foundCommandResult);
+
+ ExecutableCommand command = new SwitchAntiBotCommand();
+
+ // when
+ command.executeCommand(sender, Collections.singletonList("wrong"), service);
+
+ // then
+ verify(antiBot, never()).overrideAntiBotStatus(anyBoolean());
+ verify(sender).sendMessage(argThat(containsString("Invalid")));
+ verify(service).outputHelp(sender, foundCommandResult, HelpProvider.SHOW_ARGUMENTS);
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java
index 3936e506..213dba4c 100644
--- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java
+++ b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java
@@ -7,8 +7,6 @@ import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.ChangePasswordTask;
-import fr.xephi.authme.util.WrapperMock;
-import org.bukkit.Server;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -21,6 +19,7 @@ import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.any;
@@ -35,21 +34,17 @@ import static org.mockito.Mockito.when;
*/
public class ChangePasswordCommandTest {
- private WrapperMock wrapperMock;
- private PlayerCache cacheMock;
private CommandService commandService;
@Before
public void setUpMocks() {
- wrapperMock = WrapperMock.createInstance();
- cacheMock = wrapperMock.getPlayerCache();
commandService = mock(CommandService.class);
when(commandService.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).thenReturn(2);
when(commandService.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).thenReturn(50);
// Only allow passwords with alphanumerical characters for the test
when(commandService.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).thenReturn("[a-zA-Z0-9]+");
- when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections.EMPTY_LIST);
+ when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections. emptyList());
}
@Test
@@ -62,7 +57,9 @@ public class ChangePasswordCommandTest {
command.executeCommand(sender, new ArrayList(), commandService);
// then
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(sender).sendMessage(captor.capture());
+ assertThat(captor.getValue(), containsString("only for players"));
}
@Test
@@ -76,7 +73,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.NOT_LOGGED_IN);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
@@ -90,7 +86,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.PASSWORD_MATCH_ERROR);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@@ -105,7 +100,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
@@ -120,7 +114,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
@@ -135,7 +128,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
@@ -151,7 +143,6 @@ public class ChangePasswordCommandTest {
// then
verify(commandService).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR);
- assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
@@ -175,7 +166,9 @@ public class ChangePasswordCommandTest {
private Player initPlayerWithName(String name, boolean loggedIn) {
Player player = mock(Player.class);
when(player.getName()).thenReturn(name);
- when(cacheMock.isAuthenticated(name)).thenReturn(loggedIn);
+ PlayerCache playerCache = mock(PlayerCache.class);
+ when(playerCache.isAuthenticated(name)).thenReturn(loggedIn);
+ when(commandService.getPlayerCache()).thenReturn(playerCache);
return 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 45e4d424..6568a773 100644
--- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java
+++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java
@@ -253,7 +253,7 @@ public class HelpProviderTest {
public void shouldHandleUnboundFoundCommandResult() {
// given
FoundCommandResult result = new FoundCommandResult(null, Arrays.asList("authme", "test"),
- Collections.EMPTY_LIST, 0.0, FoundResultStatus.UNKNOWN_LABEL);
+ Collections. emptyList(), 0.0, FoundResultStatus.UNKNOWN_LABEL);
// when
List lines = helpProvider.printHelp(sender, result, ALL_OPTIONS);
@@ -317,7 +317,7 @@ public class HelpProviderTest {
* @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);
+ return new FoundCommandResult(command, labels, Collections. emptyList(), 0.0, FoundResultStatus.SUCCESS);
}
private static String removeColors(String str) {
diff --git a/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java b/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java
new file mode 100644
index 00000000..17d4fb06
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java
@@ -0,0 +1,72 @@
+package fr.xephi.authme.converter;
+
+import com.google.common.io.Files;
+import fr.xephi.authme.ConsoleLoggerTestInitializer;
+import fr.xephi.authme.TestHelper;
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.datasource.DataSource;
+import fr.xephi.authme.datasource.FlatFile;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentCaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData;
+import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link ForceFlatToSqlite}.
+ */
+public class ForceFlatToSqliteTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private FlatFile flatFile;
+
+ @BeforeClass
+ public static void setup() {
+ ConsoleLoggerTestInitializer.setupLogger();
+ }
+
+ @Before
+ public void copyFile() throws IOException {
+ File source = TestHelper.getJarFile("/datasource-integration/flatfile-test.txt");
+ File destination = temporaryFolder.newFile();
+ Files.copy(source, destination);
+ flatFile = new FlatFile(destination);
+ }
+
+ @Test
+ public void shouldConvertToSqlite() {
+ // given
+ DataSource dataSource = mock(DataSource.class);
+ ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, dataSource);
+
+ // when
+ converter.run();
+
+ // then
+ ArgumentCaptor authCaptor = ArgumentCaptor.forClass(PlayerAuth.class);
+ verify(dataSource, times(7)).saveAuth(authCaptor.capture());
+ List auths = authCaptor.getAllValues();
+ assertThat(auths, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")));
+ assertThat(auths, hasItem(hasAuthLocation(1.05, 2.1, 4.2, "world")));
+ assertThat(auths, hasItem(hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90")));
+ assertThat(auths, hasItem(hasAuthLocation(124.1, 76.3, -127.8, "nether")));
+ assertThat(auths, hasItem(hasAuthBasicData("eightfields", "eightFields", "your@email.com", "6.6.6.66")));
+ assertThat(auths, hasItem(hasAuthLocation(8.8, 17.6, 26.4, "eightworld")));
+ }
+
+}
diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java
index abe1d521..077209f4 100644
--- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java
@@ -7,9 +7,9 @@ import org.junit.Test;
import java.util.Arrays;
import java.util.List;
-import static fr.xephi.authme.datasource.AuthMeMatchers.equalToHash;
-import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthBasicData;
-import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthLocation;
+import static fr.xephi.authme.AuthMeMatchers.equalToHash;
+import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData;
+import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
@@ -129,15 +129,9 @@ public abstract class AbstractDataSourceIntegrationTest {
// then
assertThat(response, equalTo(true));
assertThat(authList, hasSize(2));
+ assertThat(authList, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")));
assertThat(newAuthList, hasSize(3));
- boolean hasBobby = false;
- for (PlayerAuth auth : authList) {
- if (auth.getNickname().equals("bobby")) {
- hasBobby = true;
- break;
- }
- }
- assertThat(hasBobby, equalTo(true));
+ assertThat(newAuthList, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")));
}
@Test
@@ -292,4 +286,18 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(updatedList, hasItem(equalTo("test-1")));
}
+ @Test
+ public void shouldUpdateRealName() {
+ // given
+ DataSource dataSource = getDataSource();
+
+ // when
+ boolean response1 = dataSource.updateRealName("bobby", "BOBBY");
+ boolean response2 = dataSource.updateRealName("notExists", "NOTEXISTS");
+
+ // then
+ assertThat(response1 && response2, equalTo(true));
+ assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89"));
+ }
+
}
diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java
new file mode 100644
index 00000000..69bb0512
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java
@@ -0,0 +1,322 @@
+package fr.xephi.authme.datasource;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import fr.xephi.authme.ConsoleLoggerTestInitializer;
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.security.HashAlgorithm;
+import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.settings.NewSetting;
+import fr.xephi.authme.settings.domain.Property;
+import fr.xephi.authme.settings.properties.SecuritySettings;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test class which runs through a datasource implementation and verifies that all
+ * instances of {@link AutoCloseable} that are created in the calls are closed again.
+ *
+ * Instead of an actual connection to a datasource, we pass a mock Connection object
+ * which is set to create additional mocks on demand for Statement and ResultSet objects.
+ * This test ensures that all such objects that are created will be closed again by
+ * keeping a list of mocks ({@link #closeables}) and then verifying that all have been
+ * closed {@link #verifyHaveMocksBeenClosed()}.
+ */
+@RunWith(Parameterized.class)
+public abstract class AbstractResourceClosingTest {
+
+ /** List of DataSource method names not to test. */
+ private static final Set IGNORED_METHODS = ImmutableSet.of("reload", "close", "getType");
+
+ /** Collection of values to use to call methods with the parameters they expect. */
+ private static final Map, Object> PARAM_VALUES = getDefaultParameters();
+
+ /**
+ * Custom list of hash algorithms to use to test a method. By default we define {@link HashAlgorithm#XFBCRYPT} as
+ * algorithms we use as a lot of methods execute additional statements in {@link MySQL}. If other algorithms
+ * have custom behaviors, they can be supplied in this map so it will be tested as well.
+ */
+ private static final Map CUSTOM_ALGORITHMS = getCustomAlgorithmList();
+
+ /** Mock of a settings instance. */
+ private static NewSetting settings;
+
+ /** The datasource to test. */
+ private DataSource dataSource;
+
+ /** The DataSource method to test. */
+ private Method method;
+
+ /** Keeps track of the closeables which are created during the tested call. */
+ private List closeables = new ArrayList<>();
+
+ /**
+ * Constructor for the test instance verifying the given method with the given hash algorithm.
+ *
+ * @param method The DataSource method to test
+ * @param name The name of the method
+ * @param algorithm The hash algorithm to use
+ */
+ public AbstractResourceClosingTest(Method method, String name, HashAlgorithm algorithm) {
+ // Note ljacqu 20160227: The name parameter is necessary as we pass it from the @Parameters method;
+ // we use the method name in the annotation to name the test sensibly
+ this.method = method;
+ given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm);
+ }
+
+ /** Initialize the settings mock and makes it return the default of any given property by default. */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @BeforeClass
+ public static void initializeSettings() throws IOException, ClassNotFoundException {
+ settings = mock(NewSetting.class);
+ given(settings.getProperty(any(Property.class))).willAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ return ((Property) invocation.getArguments()[0]).getDefaultValue();
+ }
+ });
+ ConsoleLoggerTestInitializer.setupLogger();
+ }
+
+ /** Initialize the dataSource implementation to test based on a mock connection. */
+ @Before
+ public void setUpMockConnection() throws Exception {
+ Connection connection = initConnection();
+ dataSource = createDataSource(settings, connection);
+ }
+
+ /**
+ * The actual test -- executes the method given through the constructor and then verifies that all
+ * AutoCloseable mocks it constructed have been closed.
+ */
+ @Test
+ public void shouldCloseResources() throws IllegalAccessException, InvocationTargetException {
+ method.invoke(dataSource, buildParamListForMethod(method));
+ verifyHaveMocksBeenClosed();
+ }
+
+ /**
+ * Initialization method -- provides the parameters to run the test with by scanning all DataSource
+ * methods. By default, we run one test per method with the default hash algorithm, XFBCRYPT.
+ * If the map of custom algorithms has an entry for the method name, we add an entry for each algorithm
+ * supplied by the map.
+ *
+ * @return Test parameters
+ */
+ @Parameterized.Parameters(name = "{1}({2})")
+ public static Collection