Remove duplicate column initialization code, add datasource integration tests

- Drop initialization of all columns on table create in favor of checking each column individually. This is slower but guarantees that each column is only defined once in the code. Columns are only created once so having clean code outweighs performance.
- Write more datasource integration tests
This commit is contained in:
ljacqu 2016-07-02 19:16:26 +02:00
parent f1d5f3df28
commit 22911a0bb9
6 changed files with 185 additions and 81 deletions

View File

@ -3,14 +3,12 @@ package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.XFBCRYPT; import fr.xephi.authme.security.crypts.XFBCRYPT;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
@ -69,7 +67,7 @@ public class MySQL implements DataSource {
// Initialize the database // Initialize the database
try { try {
setupConnection(); checkTablesAndColumns();
} catch (SQLException e) { } catch (SQLException e) {
close(); close();
ConsoleLogger.logException("Can't initialize the MySQL database:", e); ConsoleLogger.logException("Can't initialize the MySQL database:", e);
@ -140,24 +138,13 @@ public class MySQL implements DataSource {
return ds.getConnection(); return ds.getConnection();
} }
private void setupConnection() throws SQLException { private void checkTablesAndColumns() throws SQLException {
try (Connection con = getConnection(); Statement st = con.createStatement()) { try (Connection con = getConnection(); Statement st = con.createStatement()) {
// Create table if not exists. // Create table with ID column if it doesn't exist
String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " (" String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT," + col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT,"
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE,"
+ col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ col.PASSWORD + " VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,"
+ col.IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1',"
+ col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0,"
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "',"
+ col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com',"
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0',"
+ "PRIMARY KEY (" + col.ID + ")" + "PRIMARY KEY (" + col.ID + ")"
+ ") CHARACTER SET = utf8"; + ") CHARACTER SET = utf8;";
st.executeUpdate(sql); st.executeUpdate(sql);
DatabaseMetaData md = con.getMetaData(); DatabaseMetaData md = con.getMetaData();
@ -177,8 +164,7 @@ public class MySQL implements DataSource {
} }
if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) { if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
+ " ADD COLUMN " + col.SALT + " VARCHAR(255);");
} }
if (isColumnMissing(md, col.IP)) { if (isColumnMissing(md, col.IP)) {
@ -219,8 +205,6 @@ public class MySQL implements DataSource {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL); + col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL);
} }
st.close();
} }
ConsoleLogger.info("MySQL setup finished"); ConsoleLogger.info("MySQL setup finished");
} }

View File

@ -5,11 +5,11 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -69,67 +69,73 @@ public class SQLite implements DataSource {
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db"); this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
} }
private void setup() throws SQLException { @VisibleForTesting
Statement st = null; protected void setup() throws SQLException {
ResultSet rs = null; try (Statement st = con.createStatement()) {
try { // Note: cannot add unique fields later on in SQLite, so we add it on initialization
st = con.createStatement(); st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " ("
st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + col.ID + " INTEGER AUTO_INCREMENT," + col.NAME + " VARCHAR(255) NOT NULL UNIQUE," + col.PASSWORD + " VARCHAR(255) NOT NULL," + col.IP + " VARCHAR(40) NOT NULL," + col.LAST_LOGIN + " BIGINT," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com'," + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));"); + col.ID + " INTEGER AUTO_INCREMENT, "
rs = con.getMetaData().getColumns(null, null, tableName, col.PASSWORD); + col.NAME + " VARCHAR(255) NOT NULL UNIQUE, "
if (!rs.next()) { + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;");
DatabaseMetaData md = con.getMetaData();
if (isColumnMissing(md, col.REAL_NAME)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
} }
rs.close();
if (!col.SALT.isEmpty()) { if (isColumnMissing(md, col.PASSWORD)) {
rs = con.getMetaData().getColumns(null, null, tableName, col.SALT); st.executeUpdate("ALTER TABLE " + tableName
if (!rs.next()) { + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL DEFAULT '';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
} }
rs = con.getMetaData().getColumns(null, null, tableName, col.IP);
if (!rs.next()) { if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;"); st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LAST_LOGIN); if (isColumnMissing(md, col.IP)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP DEFAULT current_timestamp;"); + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL DEFAULT '';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_X); if (isColumnMissing(md, col.LAST_LOGIN)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0';"); + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP;");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_WORLD); if (isColumnMissing(md, col.LASTLOC_X)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';"); + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y
+ " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z
+ " DOUBLE NOT NULL DEFAULT '0.0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.EMAIL); if (isColumnMissing(md, col.LASTLOC_WORLD)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';"); + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.IS_LOGGED); if (isColumnMissing(md, col.EMAIL)) {
if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';");
}
if (isColumnMissing(md, col.IS_LOGGED)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';"); st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';");
} }
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.REAL_NAME);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
}
} finally {
close(rs);
close(st);
} }
ConsoleLogger.info("SQLite Setup finished"); ConsoleLogger.info("SQLite Setup finished");
} }
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {
return !rs.next();
}
}
@Override @Override
public void reload() { public void reload() {
close(con); close(con);
@ -146,7 +152,7 @@ public class SQLite implements DataSource {
PreparedStatement pst = null; PreparedStatement pst = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"); pst = con.prepareStatement("SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);");
pst.setString(1, user); pst.setString(1, user);
rs = pst.executeQuery(); rs = pst.executeQuery();
return rs.next(); return rs.next();

View File

@ -18,20 +18,16 @@ public final class Settings {
public static List<String> getUnrestrictedName; public static List<String> getUnrestrictedName;
public static boolean isPermissionCheckEnabled; public static boolean isPermissionCheckEnabled;
public static boolean isTeleportToSpawnEnabled; public static boolean isTeleportToSpawnEnabled;
public static boolean isSessionsEnabled;
public static boolean isAllowRestrictedIp; public static boolean isAllowRestrictedIp;
public static boolean isSaveQuitLocationEnabled; public static boolean isSaveQuitLocationEnabled;
public static boolean protectInventoryBeforeLogInEnabled; public static boolean protectInventoryBeforeLogInEnabled;
public static boolean isStopEnabled; public static boolean isStopEnabled;
public static boolean reloadSupport; public static boolean reloadSupport;
public static boolean noTeleport; public static boolean noTeleport;
public static boolean isRemoveSpeedEnabled;
public static String getUnloggedinGroup; public static String getUnloggedinGroup;
public static String unRegisteredGroup; public static String unRegisteredGroup;
public static String getRegisteredGroup; public static String getRegisteredGroup;
public static String defaultWorld; public static String defaultWorld;
public static String crazyloginFileName;
public static int getSessionTimeout;
public static int getNonActivatedGroup; public static int getNonActivatedGroup;
private static FileConfiguration configFile; private static FileConfiguration configFile;
@ -48,10 +44,7 @@ public final class Settings {
private static void loadVariables() { private static void loadVariables() {
isPermissionCheckEnabled = load(PluginSettings.ENABLE_PERMISSION_CHECK); isPermissionCheckEnabled = load(PluginSettings.ENABLE_PERMISSION_CHECK);
isTeleportToSpawnEnabled = load(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN); isTeleportToSpawnEnabled = load(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN);
isSessionsEnabled = load(PluginSettings.SESSIONS_ENABLED);
getSessionTimeout = configFile.getInt("settings.sessions.timeout", 10);
isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS); isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS);
isRemoveSpeedEnabled = load(RestrictionSettings.REMOVE_SPEED);
isSaveQuitLocationEnabled = load(RestrictionSettings.SAVE_QUIT_LOCATION); isSaveQuitLocationEnabled = load(RestrictionSettings.SAVE_QUIT_LOCATION);
getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP); getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP);
getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1); getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1);
@ -63,7 +56,6 @@ public final class Settings {
reloadSupport = configFile.getBoolean("Security.ReloadCommand.useReloadCommandSupport", true); reloadSupport = configFile.getBoolean("Security.ReloadCommand.useReloadCommandSupport", true);
defaultWorld = configFile.getString("Purge.defaultWorld", "world"); defaultWorld = configFile.getString("Purge.defaultWorld", "world");
noTeleport = load(RestrictionSettings.NO_TELEPORT); noTeleport = load(RestrictionSettings.NO_TELEPORT);
crazyloginFileName = configFile.getString("Converter.CrazyLogin.fileName", "accounts.db");
} }
/** /**

View File

@ -12,6 +12,9 @@ import java.util.Set;
import static fr.xephi.authme.AuthMeMatchers.equalToHash; import static fr.xephi.authme.AuthMeMatchers.equalToHash;
import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData; import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData;
import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation; import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
@ -170,6 +173,22 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(dataSource.getPassword("user"), equalToHash("new_hash")); assertThat(dataSource.getPassword("user"), equalToHash("new_hash"));
} }
@Test
public void shouldUpdatePasswordWithPlayerAuth() {
// given
DataSource dataSource = getDataSource("salt");
PlayerAuth bobbyAuth = PlayerAuth.builder().name("bobby").password(new HashedPassword("tt", "cc")).build();
PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").password(new HashedPassword("tt", "cc")).build();
// when
boolean response1 = dataSource.updatePassword(bobbyAuth);
boolean response2 = dataSource.updatePassword(invalidAuth);
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getPassword("bobby"), equalToHash("tt", "cc"));
}
@Test @Test
public void shouldRemovePlayerAuth() { public void shouldRemovePlayerAuth() {
// given // given
@ -321,4 +340,49 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89")); assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89"));
} }
@Test
public void shouldGetRecordsToPurge() {
// given
DataSource dataSource = getDataSource();
// 1453242857 -> user, 1449136800 -> bobby
// when
Set<String> records1 = dataSource.getRecordsToPurge(1450000000);
Set<String> records2 = dataSource.getRecordsToPurge(1460000000);
// then
assertThat(records1, contains("bobby"));
assertThat(records2, containsInAnyOrder("bobby", "user"));
// check that the entry was not deleted because of running this command
assertThat(dataSource.isAuthAvailable("bobby"), equalTo(true));
}
@Test
public void shouldPerformOperationsOnIsLoggedColumnSuccessfully() {
DataSource dataSource = getDataSource();
// on startup no one should be marked as logged
assertThat(dataSource.getLoggedPlayers(), empty());
// Mark user as logged
dataSource.setLogged("user");
// non-existent user should not break database
dataSource.setLogged("does-not-exist");
assertThat(dataSource.isLogged("user"), equalTo(true));
assertThat(dataSource.isLogged("bobby"), equalTo(false));
// Set bobby logged and unlog user
dataSource.setLogged("bobby");
dataSource.setUnlogged("user");
assertThat(dataSource.getLoggedPlayers(),
contains(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")));
// Set both as logged (even if Bobby already is logged)
dataSource.setLogged("user");
dataSource.setLogged("bobby");
dataSource.purgeLogged();
assertThat(dataSource.isLogged("user"), equalTo(false));
assertThat(dataSource.getLoggedPlayers(), empty());
}
} }

View File

@ -6,6 +6,7 @@ import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
@ -60,7 +61,6 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
@Before @Before
public void initializeConnectionAndTable() throws SQLException { public void initializeConnectionAndTable() throws SQLException {
silentClose(hikariSource);
HikariConfig config = new HikariConfig(); HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.setConnectionTestQuery("VALUES 1"); config.setConnectionTestQuery("VALUES 1");
@ -77,6 +77,11 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
hikariSource = ds; hikariSource = ds;
} }
@After
public void closeConnection() {
silentClose(hikariSource);
}
@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);

View File

@ -1,11 +1,14 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
@ -17,6 +20,8 @@ import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.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;
@ -61,7 +66,6 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
@Before @Before
public void initializeConnectionAndTable() throws SQLException { public void initializeConnectionAndTable() throws SQLException {
silentClose(con);
Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:"); Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
try (Statement st = connection.createStatement()) { try (Statement st = connection.createStatement()) {
st.execute("DROP TABLE IF EXISTS authme"); st.execute("DROP TABLE IF EXISTS authme");
@ -72,6 +76,55 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
con = connection; con = connection;
} }
@After
public void closeConnection() {
silentClose(con);
}
@Test
public void shouldSetUpTableIfMissing() throws SQLException {
// given
Statement st = con.createStatement();
// table is absent
st.execute("DROP TABLE authme");
SQLite sqLite = new SQLite(settings, con);
// when
sqLite.setup();
// then
// Save some player to verify database is operational
sqLite.saveAuth(PlayerAuth.builder().name("Name").build());
assertThat(sqLite.getAllAuths(), hasSize(1));
}
@Test
public void shouldCreateMissingColumns() throws SQLException {
// given
Statement st = con.createStatement();
// drop table and create one with only some of the columns: SQLite doesn't support ALTER TABLE t DROP COLUMN c
st.execute("DROP TABLE authme");
st.execute("CREATE TABLE authme ("
+ "id bigint, "
+ "username varchar(255) unique, "
+ "password varchar(255) not null, "
+ "primary key (id));");
SQLite sqLite = new SQLite(settings, con);
// when
sqLite.setup();
// then
// Save some player to verify database is operational
sqLite.saveAuth(PlayerAuth.builder().name("Name").build());
assertThat(sqLite.getAllAuths(), hasSize(1));
}
@Test
public void shouldCreate2() throws SQLException {
con.createStatement().execute("SELECT 1 from authme");
}
@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);