From e4026928cdf911540a216347d7b1d77499474608 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 8 Jun 2019 23:01:20 -0700 Subject: [PATCH] Completely redid the MySQL tests and added MariaDB tests --- .../mariadb/MariaDBDatabaseHandler.java | 2 +- .../database/mysql/MySQLDatabaseHandler.java | 2 +- .../mariadb/MariaDBDatabaseHandlerTest.java | 477 +++++++++++++++++ .../mysql/MySQLDatabaseHandlerTest.java | 506 ++++++++++++++---- 4 files changed, 875 insertions(+), 112 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandlerTest.java diff --git a/src/main/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandler.java index 8dfe2e701..87a1169ba 100644 --- a/src/main/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandler.java @@ -234,7 +234,7 @@ public class MariaDBDatabaseHandler extends AbstractJSONDatabaseHandler { public void deleteObject(T instance) { // Null check if (instance == null) { - plugin.logError("MySQL database request to delete a null. "); + plugin.logError("MariaDB database request to delete a null."); return; } if (!(instance instanceof DataObject)) { diff --git a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java index 9bade5ab7..55033a0cf 100644 --- a/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandler.java @@ -235,7 +235,7 @@ public class MySQLDatabaseHandler extends AbstractJSONDatabaseHandler { public void deleteObject(T instance) { // Null check if (instance == null) { - plugin.logError("MySQL database request to delete a null. "); + plugin.logError("MySQL database request to delete a null."); return; } if (!(instance instanceof DataObject)) { diff --git a/src/test/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandlerTest.java b/src/test/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandlerTest.java new file mode 100644 index 000000000..29809c5db --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/database/mariadb/MariaDBDatabaseHandlerTest.java @@ -0,0 +1,477 @@ +package world.bentobox.bentobox.database.mariadb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { Bukkit.class, BentoBox.class, Util.class }) +public class MariaDBDatabaseHandlerTest { + + private static final String JSON = "{\n" + + " \"deleted\": false,\n" + + " \"uniqueId\": \"xyz\",\n" + + " \"range\": 0,\n" + + " \"protectionRange\": 0,\n" + + " \"maxEverProtectionRange\": 0,\n" + + " \"createdDate\": 0,\n" + + " \"updatedDate\": 0,\n" + + " \"members\": {},\n" + + " \"spawn\": false,\n" + + " \"purgeProtected\": false,\n" + + " \"flags\": {},\n" + + " \"history\": [],\n" + + " \"levelHandicap\": 0,\n" + + " \"spawnPoint\": {},\n" + + " \"doNotLoad\": false\n" + + "}"; + private MariaDBDatabaseHandler handler; + private Island instance; + private String UNIQUE_ID = "xyz"; + @Mock + private MySQLDatabaseConnector dbConn; + @Mock + private BentoBox plugin; + @Mock + private BukkitScheduler sch; + @Mock + private PluginManager pluginManager; + @Mock + private Connection connection; + @Mock + private PreparedStatement ps; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Setup plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(plugin.isEnabled()).thenReturn(true); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); + + // Plugin Manager + pluginManager = mock(PluginManager.class); + when(Bukkit.getPluginManager()).thenReturn(pluginManager); + + // MySQLDatabaseConnector + when(dbConn.createConnection()).thenReturn(connection); + + // Queries + when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps); + when(connection.createStatement()).thenReturn(ps); + ResultSet rs = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(rs); + when(ps.executeQuery(Mockito.anyString())).thenReturn(rs); + + // Instance to save + instance = new Island(); + instance.setUniqueId(UNIQUE_ID); + handler = new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn); + + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsNoConnection() throws SQLException { + when(connection.createStatement()).thenThrow(new SQLException("no connection")); + handler.loadObjects(); + verify(plugin).logError("Could not load objects no connection"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjects() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.size() == 3); + assertEquals("xyz", objects.get(2).getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsBadJSON() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn("sfdasfasdfsfd"); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.isEmpty()); + verify(plugin, Mockito.times(3)).logError("Could not load object java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenThrow(new SQLException("SQL error")); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.isEmpty()); + verify(plugin).logError("Could not load objects SQL error"); + + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}. + */ + @Test + public void testLoadObjectNoConnection() throws SQLException { + when(connection.prepareStatement(Mockito.anyString())).thenThrow(new SQLException("no connection")); + handler.loadObject("abc"); + verify(plugin).logError("Could not load object abc no connection"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObject() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + when(resultSet.next()).thenReturn(true); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"abc\""); + verify(resultSet).next(); + assertNotNull(object); + assertEquals("xyz", object.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObjectBadJSON() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn("afdsaf"); + when(resultSet.next()).thenReturn(true); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + assertNull(object); + verify(plugin).logError("Could not load object abc java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObjectError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + when(resultSet.next()).thenThrow(new SQLException("SQL Exception")); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + assertNull(object); + verify(plugin).logError("Could not load object abc SQL Exception"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}. + */ + @Test + public void testSaveObjectNull() { + handler.saveObject(null); + verify(plugin).logError(eq("MySQL database request to store a null. ")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}. + */ + @Test + public void testSaveObjectNotDataObject() { + @SuppressWarnings("rawtypes") + MariaDBDatabaseHandler h = new MariaDBDatabaseHandler(plugin, List.class, dbConn); + h.saveObject(Collections.singletonList("test")); + verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}. + * @throws SQLException + */ + @Test + public void testSaveObject() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + handler.saveObject(instance); + verify(ps).execute(); + verify(ps).setString(1, JSON); + verify(ps).setString(2, "{\n" + + " \"deleted\": false,\n" + + " \"uniqueId\": \"xyz\",\n" + + " \"range\": 0,\n" + + " \"protectionRange\": 0,\n" + + " \"maxEverProtectionRange\": 0,\n" + + " \"createdDate\": 0,\n" + + " \"updatedDate\": 0,\n" + + " \"members\": {},\n" + + " \"spawn\": false,\n" + + " \"purgeProtected\": false,\n" + + " \"flags\": {},\n" + + " \"history\": [],\n" + + " \"levelHandicap\": 0,\n" + + " \"spawnPoint\": {},\n" + + " \"doNotLoad\": false\n" + + "}"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}. + * @throws SQLException + */ + @Test + public void testSaveObjectFail() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + when(ps.execute()).thenThrow(new SQLException("fail!")); + handler.saveObject(instance); + verify(plugin).logError(eq("Could not save object world.bentobox.bentobox.database.objects.Island fail!")); + + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}. + */ + @Test + public void testDeleteObjectNull() { + handler.deleteObject(null); + verify(plugin).logError(eq("MariaDB database request to delete a null.")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}. + */ + @Test + public void testDeleteObjectIncorrectType() { + @SuppressWarnings("rawtypes") + MariaDBDatabaseHandler h = new MariaDBDatabaseHandler(plugin, List.class, dbConn); + h.deleteObject(Collections.singletonList("test")); + verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}. + * @throws SQLException + */ + @Test + public void testDeleteObject() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + handler.deleteObject(instance); + verify(ps).execute(); + verify(ps).setString(1, "\"xyz\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsNot() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + assertFalse(handler.objectExists("hello")); + //verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsFalse() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getBoolean(eq(1))).thenReturn(false); + assertFalse(handler.objectExists("hello")); + //verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExists() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getBoolean(eq(1))).thenReturn(true); + assertTrue(handler.objectExists("hello")); + //verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenThrow(new SQLException("error")); + handler.objectExists("hello"); + verify(plugin).logError(eq("Could not check if key exists in database! hello error")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#close()}. + * @throws SQLException + */ + @Test + public void testClose() throws SQLException { + handler.close(); + verify(connection).close(); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#close()}. + * @throws SQLException + */ + @Test + public void testCloseError() throws SQLException { + Mockito.doThrow(new SQLException("error")).when(connection).close(); + handler.close(); + verify(plugin).logError(eq("Could not close database for some reason")); + } + + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteID(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testDeleteID() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + handler.deleteID("abc123"); + verify(ps).execute(); + verify(ps).setString(1, "\"abc123\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteID(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testDeleteIDError() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + when(ps.execute()).thenThrow(new SQLException("fail!")); + handler.deleteID("abc123"); + verify(plugin).logError(eq("Could not delete object world.bentobox.bentobox.database.objects.Island abc123 fail!")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + */ + @Test + public void testMariaDBDatabaseHandlerBadPassword() { + when(dbConn.createConnection()).thenReturn(null); + new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn); + verify(plugin).logError("Are the settings in config.yml correct?"); + verify(pluginManager).disablePlugin(plugin); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + * @throws SQLException + */ + @Test + public void testMariaDBDatabaseHandlerCreateSchema() throws SQLException { + verify(dbConn).createConnection(); + //verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + * @throws SQLException + */ + @Test + public void testMariaDBDatabaseHandlerSchemaFail() throws SQLException { + when(ps.executeUpdate()).thenThrow(new SQLException("oh no!")); + handler = new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn); + verify(plugin).logError("Problem trying to create schema for data object world.bentobox.bentobox.database.objects.Island oh no!"); + + } + +} diff --git a/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java b/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java index 181e647ee..3b4a985c7 100644 --- a/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java +++ b/src/test/java/world/bentobox/bentobox/database/mysql/MySQLDatabaseHandlerTest.java @@ -1,5 +1,10 @@ package world.bentobox.bentobox.database.mysql; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -9,20 +14,13 @@ import static org.mockito.Mockito.when; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Logger; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.inventory.ItemFactory; import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,153 +32,441 @@ import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.Settings; -import world.bentobox.bentobox.api.flags.Flag; +import world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector; import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.database.objects.Players; -import world.bentobox.bentobox.lists.Flags; -import world.bentobox.bentobox.managers.FlagsManager; -import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.util.Util; +/** + * @author tastybento + * + */ @RunWith(PowerMockRunner.class) @PrepareForTest( { Bukkit.class, BentoBox.class, Util.class }) public class MySQLDatabaseHandlerTest { + private static final String JSON = "{\n" + + " \"deleted\": false,\n" + + " \"uniqueId\": \"xyz\",\n" + + " \"range\": 0,\n" + + " \"protectionRange\": 0,\n" + + " \"maxEverProtectionRange\": 0,\n" + + " \"createdDate\": 0,\n" + + " \"updatedDate\": 0,\n" + + " \"members\": {},\n" + + " \"spawn\": false,\n" + + " \"purgeProtected\": false,\n" + + " \"flags\": {},\n" + + " \"history\": [],\n" + + " \"levelHandicap\": 0,\n" + + " \"spawnPoint\": {},\n" + + " \"doNotLoad\": false\n" + + "}"; private MySQLDatabaseHandler handler; private Island instance; private String UNIQUE_ID = "xyz"; + @Mock private MySQLDatabaseConnector dbConn; - private World world; @Mock - private Location location; + private BentoBox plugin; @Mock - static BentoBox plugin = mock(BentoBox.class); - private static IslandWorldManager iwm; - + private BukkitScheduler sch; + @Mock + private PluginManager pluginManager; + @Mock + private Connection connection; + @Mock + private PreparedStatement ps; + /** + * @throws java.lang.Exception + */ @Before public void setUp() throws Exception { - Server server = mock(Server.class); - world = mock(World.class); - when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); - when(server.getWorld("world")).thenReturn(world); - when(server.getVersion()).thenReturn("BSB_Mocking"); - - PluginManager pluginManager = mock(PluginManager.class); - when(server.getPluginManager()).thenReturn(pluginManager); - - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); - - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - + // Setup plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(plugin.isEnabled()).thenReturn(true); - Settings settings = mock(Settings.class); + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(sch); - when(plugin.getSettings()).thenReturn(settings); + // Plugin Manager + pluginManager = mock(PluginManager.class); + when(Bukkit.getPluginManager()).thenReturn(pluginManager); - iwm = mock(IslandWorldManager.class); - when(iwm.getDeathsMax(any())).thenReturn(10); - when(plugin.getIWM()).thenReturn(iwm); - - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - dbConn = mock(MySQLDatabaseConnector.class); - Connection connection = mock(Connection.class); + // MySQLDatabaseConnector when(dbConn.createConnection()).thenReturn(connection); - PreparedStatement ps = mock(PreparedStatement.class); + + // Queries when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps); - Statement statement = mock(Statement.class); - when(connection.createStatement()).thenReturn(statement); + when(connection.createStatement()).thenReturn(ps); ResultSet rs = mock(ResultSet.class); when(ps.executeQuery()).thenReturn(rs); - when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); + when(ps.executeQuery(Mockito.anyString())).thenReturn(rs); + + // Instance to save instance = new Island(); instance.setUniqueId(UNIQUE_ID); handler = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); - PowerMockito.mockStatic(Util.class); - when(Util.sameWorld(any(), any())).thenReturn(true); + } - // Flags - FlagsManager fm = mock(FlagsManager.class); - when(plugin.getFlagsManager()).thenReturn(fm); - when(fm.getFlags()).thenReturn(new ArrayList<>()); + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsNoConnection() throws SQLException { + when(connection.createStatement()).thenThrow(new SQLException("no connection")); + handler.loadObjects(); + verify(plugin).logError("Could not load objects no connection"); + } - // Location - when(location.getWorld()).thenReturn(world); - when(location.getBlockX()).thenReturn(0); - when(location.getBlockY()).thenReturn(0); - when(location.getBlockZ()).thenReturn(0); + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjects() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.size() == 3); + assertEquals("xyz", objects.get(2).getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsBadJSON() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn("sfdasfasdfsfd"); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.isEmpty()); + verify(plugin, Mockito.times(3)).logError("Could not load object java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}. + * @throws SQLException + */ + @Test + public void testLoadObjectsError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenThrow(new SQLException("SQL error")); + // Three islands + when(resultSet.next()).thenReturn(true, true, true, false); + when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet); + List objects = handler.loadObjects(); + verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`"); + assertTrue(objects.isEmpty()); + verify(plugin).logError("Could not load objects SQL error"); } + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}. + */ @Test - public void testSaveNullObject() { + public void testLoadObjectNoConnection() throws SQLException { + when(connection.prepareStatement(Mockito.anyString())).thenThrow(new SQLException("no connection")); + handler.loadObject("abc"); + verify(plugin).logError("Could not load object abc no connection"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObject() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + when(resultSet.next()).thenReturn(true); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"abc\""); + verify(resultSet).next(); + assertNotNull(object); + assertEquals("xyz", object.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObjectBadJSON() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn("afdsaf"); + when(resultSet.next()).thenReturn(true); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + assertNull(object); + verify(plugin).logError("Could not load object abc java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testLoadObjectError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.getString(any())).thenReturn(JSON); + when(resultSet.next()).thenThrow(new SQLException("SQL Exception")); + when(ps.executeQuery()).thenReturn(resultSet); + Island object = handler.loadObject("abc"); + assertNull(object); + verify(plugin).logError("Could not load object abc SQL Exception"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}. + */ + @Test + public void testSaveObjectNull() { handler.saveObject(null); verify(plugin).logError(eq("MySQL database request to store a null. ")); } + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}. + */ @Test - public void testSaveObject() { + public void testSaveObjectNotDataObject() { + @SuppressWarnings("rawtypes") + MySQLDatabaseHandler h = new MySQLDatabaseHandler(plugin, List.class, dbConn); + h.saveObject(Collections.singletonList("test")); + verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}. + * @throws SQLException + */ + @Test + public void testSaveObject() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); handler.saveObject(instance); + verify(ps).execute(); + verify(ps).setString(1, JSON); + verify(ps).setString(2, "{\n" + + " \"deleted\": false,\n" + + " \"uniqueId\": \"xyz\",\n" + + " \"range\": 0,\n" + + " \"protectionRange\": 0,\n" + + " \"maxEverProtectionRange\": 0,\n" + + " \"createdDate\": 0,\n" + + " \"updatedDate\": 0,\n" + + " \"members\": {},\n" + + " \"spawn\": false,\n" + + " \"purgeProtected\": false,\n" + + " \"flags\": {},\n" + + " \"history\": [],\n" + + " \"levelHandicap\": 0,\n" + + " \"spawnPoint\": {},\n" + + " \"doNotLoad\": false\n" + + "}"); } + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}. + * @throws SQLException + */ @Test - public void testSaveObject2() { - BentoBox plugin = mock(BentoBox.class); - Settings settings = mock(Settings.class); - when(plugin.getSettings()).thenReturn(settings); - when(iwm.getDeathsMax(any())).thenReturn(10); - Players players = new Players(); - players.setUniqueId(UUID.randomUUID().toString()); - players.setDeaths(world, 23); - - players.setHomeLocation(location); - players.setHomeLocation(location, 1); - players.setHomeLocation(location, 2); - Map map = new HashMap<>(); - map.put(location, 324L); - players.setLocale("sdfsd"); - players.setPlayerName("name"); - players.setPlayerUUID(UUID.randomUUID()); - players.setResets(world, 3); - - - MySQLDatabaseHandler h = new MySQLDatabaseHandler<>(plugin, Players.class, dbConn); - h.saveObject(players); + public void testSaveObjectFail() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + when(ps.execute()).thenThrow(new SQLException("fail!")); + handler.saveObject(instance); + verify(plugin).logError(eq("Could not save object world.bentobox.bentobox.database.objects.Island fail!")); } + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}. + */ @Test - public void testSaveObject3() { - Island island = new Island(); - island.setUniqueId(UNIQUE_ID); - island.setCenter(location); - Map flags = new HashMap<>(); - for (Flag fl : Flags.values()) { - flags.put(fl, 100); - } - island.setFlags(flags); - island.setLevelHandicap(10); - Map members = new HashMap<>(); - for (int i = 0; i < 10; i++) { - members.put(UUID.randomUUID(), i); - } - island.setMembers(members); - island.setName("ytasdgfsdfg"); - island.setOwner(UUID.randomUUID()); - island.setProtectionRange(100); - island.setPurgeProtected(true); - island.setRange(100); - island.setSpawn(true); - island.setSpawnPoint(Environment.NORMAL, location); - island.setWorld(world); + public void testDeleteObjectNull() { + handler.deleteObject(null); + verify(plugin).logError(eq("MySQL database request to delete a null.")); + } - MySQLDatabaseHandler ih = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); - ih.saveObject(island); + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}. + */ + @Test + public void testDeleteObjectIncorrectType() { + @SuppressWarnings("rawtypes") + MySQLDatabaseHandler h = new MySQLDatabaseHandler(plugin, List.class, dbConn); + h.deleteObject(Collections.singletonList("test")); + verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}. + * @throws SQLException + */ + @Test + public void testDeleteObject() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + handler.deleteObject(instance); + verify(ps).execute(); + verify(ps).setString(1, "\"xyz\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsNot() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + assertFalse(handler.objectExists("hello")); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsFalse() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getBoolean(eq(1))).thenReturn(false); + assertFalse(handler.objectExists("hello")); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExists() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getBoolean(eq(1))).thenReturn(true); + assertTrue(handler.objectExists("hello")); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + verify(ps).executeQuery(); + verify(ps).setString(1, "\"hello\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testObjectExistsError() throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenThrow(new SQLException("error")); + handler.objectExists("hello"); + verify(plugin).logError(eq("Could not check if key exists in database! hello error")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#close()}. + * @throws SQLException + */ + @Test + public void testClose() throws SQLException { + handler.close(); + verify(connection).close(); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#close()}. + * @throws SQLException + */ + @Test + public void testCloseError() throws SQLException { + Mockito.doThrow(new SQLException("error")).when(connection).close(); + handler.close(); + verify(plugin).logError(eq("Could not close database for some reason")); + } + + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testDeleteID() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + handler.deleteID("abc123"); + verify(ps).execute(); + verify(ps).setString(1, "\"abc123\""); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}. + * @throws SQLException + */ + @Test + public void testDeleteIDError() throws SQLException { + // Disable plugin + when(plugin.isEnabled()).thenReturn(false); + when(ps.execute()).thenThrow(new SQLException("fail!")); + handler.deleteID("abc123"); + verify(plugin).logError(eq("Could not delete object world.bentobox.bentobox.database.objects.Island abc123 fail!")); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + */ + @Test + public void testMySQLDatabaseHandlerBadPassword() { + when(dbConn.createConnection()).thenReturn(null); + new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); + verify(plugin).logError("Are the settings in config.yml correct?"); + verify(pluginManager).disablePlugin(plugin); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + * @throws SQLException + */ + @Test + public void testMySQLDatabaseHandlerCreateSchema() throws SQLException { + verify(dbConn).createConnection(); + verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); + } + + /** + * Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}. + * @throws SQLException + */ + @Test + public void testMySQLDatabaseHandlerSchemaFail() throws SQLException { + when(ps.executeUpdate()).thenThrow(new SQLException("oh no!")); + handler = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); + verify(plugin).logError("Problem trying to create schema for data object world.bentobox.bentobox.database.objects.Island oh no!"); }