#792 Add missing tests / fix CodeClimate issues

This commit is contained in:
ljacqu 2017-10-20 17:49:30 +02:00
parent 90073ef95d
commit a425eacf2d
7 changed files with 165 additions and 33 deletions

View File

@ -98,24 +98,33 @@ class MySqlDefaultChanger implements DebugSection {
} }
} }
/**
* Adds a default value to the column definition and adds a {@code NOT NULL} constraint for
* the specified column.
*
* @param sender the command sender initiation the action
* @param column the column to modify
* @param con connection to the database
* @throws SQLException .
*/
private void changeColumnToNotNullWithDefault(CommandSender sender, Columns column, private void changeColumnToNotNullWithDefault(CommandSender sender, Columns column,
Connection con) throws SQLException { Connection con) throws SQLException {
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
final String columnName = settings.getProperty(column.columnName); final String columnName = settings.getProperty(column.getColumnNameProperty());
// Replace NULLs with future default value // Replace NULLs with future default value
String sql = format("UPDATE %s SET %s = ? WHERE %s IS NULL;", tableName, columnName, columnName); String sql = format("UPDATE %s SET %s = ? WHERE %s IS NULL;", tableName, columnName, columnName);
int updatedRows; int updatedRows;
try (PreparedStatement pst = con.prepareStatement(sql)) { try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setObject(1, column.defaultValue); pst.setObject(1, column.getDefaultValue());
updatedRows = pst.executeUpdate(); updatedRows = pst.executeUpdate();
} }
sender.sendMessage("Replaced NULLs with default value ('" + column.defaultValue sender.sendMessage("Replaced NULLs with default value ('" + column.getDefaultValue()
+ "'), modifying " + updatedRows + " entries"); + "'), modifying " + updatedRows + " entries");
// Change column definition to NOT NULL version // Change column definition to NOT NULL version
try (Statement st = con.createStatement()) { try (Statement st = con.createStatement()) {
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.notNullDefinition)); st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNotNullDefinition()));
sender.sendMessage("Changed column '" + columnName + "' to have NOT NULL constraint"); sender.sendMessage("Changed column '" + columnName + "' to have NOT NULL constraint");
} }
@ -124,13 +133,22 @@ class MySqlDefaultChanger implements DebugSection {
+ sender.getName() + "'"); + sender.getName() + "'");
} }
/**
* Removes the {@code NOT NULL} constraint of a column definition and replaces rows with the
* default value to {@code NULL}.
*
* @param sender the command sender initiation the action
* @param column the column to modify
* @param con connection to the database
* @throws SQLException .
*/
private void removeNotNullAndDefault(CommandSender sender, Columns column, Connection con) throws SQLException { private void removeNotNullAndDefault(CommandSender sender, Columns column, Connection con) throws SQLException {
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
final String columnName = settings.getProperty(column.columnName); final String columnName = settings.getProperty(column.getColumnNameProperty());
// Change column definition to nullable version // Change column definition to nullable version
try (Statement st = con.createStatement()) { try (Statement st = con.createStatement()) {
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.nullableDefinition)); st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNullableDefinition()));
sender.sendMessage("Changed column '" + columnName + "' to allow nulls"); sender.sendMessage("Changed column '" + columnName + "' to allow nulls");
} }
@ -138,10 +156,10 @@ class MySqlDefaultChanger implements DebugSection {
String sql = format("UPDATE %s SET %s = NULL WHERE %s = ?;", tableName, columnName, columnName); String sql = format("UPDATE %s SET %s = NULL WHERE %s = ?;", tableName, columnName, columnName);
int updatedRows; int updatedRows;
try (PreparedStatement pst = con.prepareStatement(sql)) { try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setObject(1, column.defaultValue); pst.setObject(1, column.getDefaultValue());
updatedRows = pst.executeUpdate(); updatedRows = pst.executeUpdate();
} }
sender.sendMessage("Replaced default value ('" + column.defaultValue sender.sendMessage("Replaced default value ('" + column.getDefaultValue()
+ "') to be NULL, modifying " + updatedRows + " entries"); + "') to be NULL, modifying " + updatedRows + " entries");
// Log success message // Log success message
@ -183,7 +201,8 @@ class MySqlDefaultChanger implements DebugSection {
List<String> formattedColumns = new ArrayList<>(Columns.values().length); List<String> formattedColumns = new ArrayList<>(Columns.values().length);
for (Columns col : Columns.values()) { for (Columns col : Columns.values()) {
boolean isNotNull = isNotNullColumn(metaData, tableName, settings.getProperty(col.columnName)); String columnName = settings.getProperty(col.getColumnNameProperty());
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
String formattedColumn = (isNotNull ? ChatColor.DARK_AQUA : ChatColor.GOLD) + col.name().toLowerCase(); String formattedColumn = (isNotNull ? ChatColor.DARK_AQUA : ChatColor.GOLD) + col.name().toLowerCase();
formattedColumns.add(formattedColumn); formattedColumns.add(formattedColumn);
} }
@ -212,6 +231,12 @@ class MySqlDefaultChanger implements DebugSection {
return false; return false;
} }
/**
* Gets the Connection object from the MySQL data source.
*
* @param mySql the MySQL data source to get the connection from
* @return the connection
*/
@VisibleForTesting @VisibleForTesting
Connection getConnection(MySQL mySql) { Connection getConnection(MySQL mySql) {
try { try {
@ -259,6 +284,7 @@ class MySqlDefaultChanger implements DebugSection {
ADD, REMOVE ADD, REMOVE
} }
/** MySQL columns which can be toggled between being NOT NULL and allowing NULL values. */
enum Columns { enum Columns {
LASTLOGIN(DatabaseSettings.MYSQL_COL_LASTLOGIN, LASTLOGIN(DatabaseSettings.MYSQL_COL_LASTLOGIN,
@ -267,16 +293,37 @@ class MySqlDefaultChanger implements DebugSection {
EMAIL(DatabaseSettings.MYSQL_COL_EMAIL, EMAIL(DatabaseSettings.MYSQL_COL_EMAIL,
"VARCHAR(255)", "VARCHAR(255) NOT NULL DEFAULT 'your@email.com'", DB_EMAIL_DEFAULT); "VARCHAR(255)", "VARCHAR(255) NOT NULL DEFAULT 'your@email.com'", DB_EMAIL_DEFAULT);
final Property<String> columnName; private final Property<String> columnNameProperty;
final String nullableDefinition; private final String nullableDefinition;
final String notNullDefinition; private final String notNullDefinition;
final Object defaultValue; private final Object defaultValue;
Columns(Property<String> columnName, String nullableDefinition, String notNullDefinition, Object defaultValue) { Columns(Property<String> columnNameProperty, String nullableDefinition,
this.columnName = columnName; String notNullDefinition, Object defaultValue) {
this.columnNameProperty = columnNameProperty;
this.nullableDefinition = nullableDefinition; this.nullableDefinition = nullableDefinition;
this.notNullDefinition = notNullDefinition; this.notNullDefinition = notNullDefinition;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
/** @return property defining the column name in the database */
Property<String> getColumnNameProperty() {
return columnNameProperty;
}
/** @return SQL definition of the column allowing NULL values */
String getNullableDefinition() {
return nullableDefinition;
}
/** @return SQL definition of the column with a NOT NULL constraint */
String getNotNullDefinition() {
return notNullDefinition;
}
/** @return the default value used in {@link #notNullDefinition} */
Object getDefaultValue() {
return defaultValue;
}
} }
} }

View File

@ -12,6 +12,8 @@ import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
/** /**
* Handles the user sessions. * Handles the user sessions.
*/ */
@ -66,11 +68,13 @@ public class SessionService implements Reloadable {
if (auth == null) { if (auth == null) {
ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check"); ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check");
return false; return false;
} else if (auth.getLastLogin() == null) {
return false;
} }
long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin(); long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin();
return PlayerUtils.getPlayerIp(player).equals(auth.getLastIp()) return PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())
&& timeSinceLastLogin > 0 && timeSinceLastLogin > 0
&& timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * 60 * 1000; && timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE;
} }
public void grantSession(String name) { public void grantSession(String name) {

View File

@ -21,9 +21,9 @@ public class MySqlDefaultChangerColumnsTest {
// when / then // when / then
for (MySqlDefaultChanger.Columns col : MySqlDefaultChanger.Columns.values()) { for (MySqlDefaultChanger.Columns col : MySqlDefaultChanger.Columns.values()) {
if (!properties.add(col.columnName.getPath())) { if (!properties.add(col.getColumnNameProperty().getPath())) {
fail("Column '" + col + "' has a column name property path that was already encountered: " fail("Column '" + col + "' has a column name property path that was already encountered: "
+ col.columnName.getPath()); + col.getColumnNameProperty().getPath());
} }
} }
} }
@ -44,8 +44,8 @@ public class MySqlDefaultChangerColumnsTest {
private void verifyHasCorrespondingColumnDefinitions(MySqlDefaultChanger.Columns column) { private void verifyHasCorrespondingColumnDefinitions(MySqlDefaultChanger.Columns column) {
// given / when // given / when
String nullable = column.nullableDefinition; String nullable = column.getNullableDefinition();
String notNull = column.notNullDefinition; String notNull = column.getNotNullDefinition();
// then // then
String expectedNotNull = nullable + " NOT NULL DEFAULT "; String expectedNotNull = nullable + " NOT NULL DEFAULT ";
@ -56,8 +56,8 @@ public class MySqlDefaultChangerColumnsTest {
private void verifyHasSameDefaultValueInNotNullDefinition(MySqlDefaultChanger.Columns column) { private void verifyHasSameDefaultValueInNotNullDefinition(MySqlDefaultChanger.Columns column) {
// given / when // given / when
String notNull = column.notNullDefinition; String notNull = column.getNotNullDefinition();
Object defaultValue = column.defaultValue; Object defaultValue = column.getDefaultValue();
// then // then
String defaultValueAsString = String.valueOf(defaultValue); String defaultValueAsString = String.valueOf(defaultValue);

View File

@ -1,18 +1,29 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.datasource.MySqlTestUtil;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.sql.Connection;
import java.sql.SQLException;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/** /**
* Test for {@link MySqlDefaultChanger}. * Test for {@link MySqlDefaultChanger}.
@ -49,7 +60,54 @@ public class MySqlDefaultChangerTest {
assertThat(result, equalTo(source)); assertThat(result, equalTo(source));
} }
// TODO #792: Add more tests @Test
public void shouldReturnMySqlConnection() throws SQLException {
// given
Settings settings = mock(Settings.class);
TestHelper.returnDefaultsForAllProperties(settings);
HikariDataSource dataSource = mock(HikariDataSource.class);
Connection connection = mock(Connection.class);
given(dataSource.getConnection()).willReturn(connection);
MySQL mySQL = MySqlTestUtil.createMySql(settings, dataSource);
MySqlDefaultChanger defaultChanger = createDefaultChanger(mySQL);
// when
Connection result = defaultChanger.getConnection(mySQL);
// then
assertThat(result, equalTo(connection));
verify(dataSource).getConnection();
}
@Test
public void shouldSetMySqlFieldOnInitialization() {
// given
MySQL mySql = mock(MySQL.class);
MySqlDefaultChanger defaultChanger = createDefaultChanger(mySql);
// when
defaultChanger.setMySqlField();
// then
assertThat(ReflectionTestUtils.getFieldValue(MySqlDefaultChanger.class, defaultChanger, "mySql"),
sameInstance(mySql));
}
@Test
public void shouldLeaveMySqlFieldToNullOnInitialization() {
// given
DataSource dataSource = mock(DataSource.class);
PlayerCache playerCache = mock(PlayerCache.class);
CacheDataSource cacheDataSource = new CacheDataSource(dataSource, playerCache);
MySqlDefaultChanger defaultChanger = createDefaultChanger(cacheDataSource);
// when
defaultChanger.setMySqlField();
// then
assertThat(ReflectionTestUtils.getFieldValue(MySqlDefaultChanger.class, defaultChanger, "mySql"),
nullValue());
}
private MySqlDefaultChanger createDefaultChanger(DataSource dataSource) { private MySqlDefaultChanger createDefaultChanger(DataSource dataSource) {
MySqlDefaultChanger defaultChanger = new MySqlDefaultChanger(); MySqlDefaultChanger defaultChanger = new MySqlDefaultChanger();

View File

@ -4,8 +4,6 @@ import ch.jalu.configme.properties.Property;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After; import org.junit.After;
@ -19,7 +17,6 @@ import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -30,8 +27,6 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
/** Mock of a settings instance. */ /** Mock of a settings instance. */
private static Settings settings; private static Settings settings;
/** Mock of extensions factory. */
private static MySqlExtensionsFactory extensionsFactory;
/** SQL statement to execute before running a test. */ /** SQL statement to execute before running a test. */
private static String sqlInitialize; private static String sqlInitialize;
/** Connection to the H2 test database. */ /** Connection to the H2 test database. */
@ -47,8 +42,6 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
settings = mock(Settings.class); settings = mock(Settings.class);
TestHelper.returnDefaultsForAllProperties(settings); TestHelper.returnDefaultsForAllProperties(settings);
extensionsFactory = mock(MySqlExtensionsFactory.class);
when(extensionsFactory.buildExtension(any(Columns.class))).thenReturn(mock(MySqlExtension.class));
set(DatabaseSettings.MYSQL_DATABASE, "h2_test"); set(DatabaseSettings.MYSQL_DATABASE, "h2_test");
set(DatabaseSettings.MYSQL_TABLE, "authme"); set(DatabaseSettings.MYSQL_TABLE, "authme");
TestHelper.setRealLogger(); TestHelper.setRealLogger();
@ -83,7 +76,7 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
@Override @Override
protected DataSource getDataSource(String saltColumn) { protected DataSource getDataSource(String saltColumn) {
when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn); when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn);
return new MySQL(settings, hikariSource, extensionsFactory); return MySqlTestUtil.createMySql(settings, hikariSource);
} }
private static <T> void set(Property<T> property, T value) { private static <T> void set(Property<T> property, T value) {

View File

@ -0,0 +1,30 @@
package fr.xephi.authme.datasource;
import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
import fr.xephi.authme.settings.Settings;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Test util for the MySQL data source.
*/
public final class MySqlTestUtil {
private MySqlTestUtil() {
}
public static MySQL createMySql(Settings settings, HikariDataSource hikariDataSource) {
MySqlExtensionsFactory extensionsFactory = mock(MySqlExtensionsFactory.class);
given(extensionsFactory.buildExtension(any())).willReturn(mock(MySqlExtension.class));
return createMySql(settings, hikariDataSource, extensionsFactory);
}
public static MySQL createMySql(Settings settings, HikariDataSource hikariDataSource,
MySqlExtensionsFactory extensionsFactory) {
return new MySQL(settings, hikariDataSource, extensionsFactory);
}
}

View File

@ -113,7 +113,7 @@ public class SessionServiceTest {
} }
@Test @Test
public void shouldRefuseSessionForAuthWithZeroLastLoginTimestamp() { public void shouldRefuseSessionForAuthWithNullLastLoginTimestamp() {
// given // given
String name = "Bobby"; String name = "Bobby";
String ip = "127.3.12.15"; String ip = "127.3.12.15";
@ -122,7 +122,7 @@ public class SessionServiceTest {
given(dataSource.hasSession(name)).willReturn(true); given(dataSource.hasSession(name)).willReturn(true);
PlayerAuth auth = PlayerAuth.builder() PlayerAuth auth = PlayerAuth.builder()
.name(name) .name(name)
.lastLogin(0L) .lastLogin(null)
.lastIp(ip).build(); .lastIp(ip).build();
given(dataSource.getAuth(name)).willReturn(auth); given(dataSource.getAuth(name)).willReturn(auth);