Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.diamonddagger590.mccore;

import com.diamonddagger590.mccore.registry.RegistryAccess;
import com.diamonddagger590.mccore.testing.RegistryResetExtension;
import com.diamonddagger590.mccore.testing.TestCorePlugin;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockbukkit.mockbukkit.MockBukkit;
import org.mockbukkit.mockbukkit.ServerMock;

import java.lang.reflect.Field;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;

class CorePluginMockBukkitTest {

private ServerMock server;
private TestCorePlugin plugin;

@BeforeEach
void setUp() {
server = MockBukkit.mock();
plugin = MockBukkit.load(TestCorePlugin.class);
RegistryResetExtension.setupRegistry();
}

@AfterEach
void tearDown() throws Exception {
RegistryResetExtension.resetRegistry();
MockBukkit.unmock();
Field instanceField = CorePlugin.class.getDeclaredField("instance");
instanceField.setAccessible(true);
instanceField.set(null, null);
}

@Test
@DisplayName("Given a loaded plugin, when onEnable has run, then getInstance returns the plugin")
void getInstance_returnsPlugin_afterOnEnable() {
assertSame(plugin, CorePlugin.getInstance());
}

@Test
@DisplayName("Given a loaded plugin, when getMiniMessage is called, then returns non-null MiniMessage")
void getMiniMessage_returnsNonNull_afterOnEnable() {
MiniMessage miniMessage = plugin.getMiniMessage();
assertNotNull(miniMessage);
}

@Test
@DisplayName("Given a loaded plugin, when getMiniMessage is called, then returns the standard MiniMessage instance")
void getMiniMessage_returnsStandardInstance_afterOnEnable() {
MiniMessage miniMessage = plugin.getMiniMessage();
assertSame(MiniMessage.miniMessage(), miniMessage);
}

@Test
@DisplayName("Given a loaded plugin, when registryAccess is called, then returns the RegistryAccess singleton")
void registryAccess_returnsSingleton_afterOnEnable() {
RegistryAccess fromPlugin = plugin.registryAccess();
assertNotNull(fromPlugin);
assertSame(RegistryAccess.registryAccess(), fromPlugin);
}

@Test
@DisplayName("Given a loaded plugin, when onDisable is called, then no exception is thrown")
void onDisable_doesNotThrow_whenCalled() {
assertDoesNotThrow(() -> plugin.onDisable());
}

@Test
@DisplayName("Given a loaded plugin, when getTimeProvider is called, then returns non-null TimeProvider")
void getTimeProvider_returnsNonNull_afterOnEnable() {
assertNotNull(plugin.getTimeProvider());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -396,6 +397,63 @@ void initializeDatabase_configuresDataSourceWithConnectionDetails_whenDriverIsRe
}
}

@Test
@DisplayName("Given a configured data source that throws SQLException, when getConnection is called, then wraps in RuntimeException")
void getConnection_throwsRuntimeException_whenDataSourceThrowsSQLException() {
RegistryResetExtension.setupRegistry();
try {
DriverRegistry driverRegistry = new DriverRegistry();
RegistryAccess.registryAccess().register(driverRegistry);

DatabaseDriver mockDriver = mock(DatabaseDriver.class);
when(mockDriver.tryDriver()).thenReturn(true);
when(mockDriver.getDriverType()).thenReturn(DatabaseDriverType.SQLITE);
when(mockDriver.getDatabaseDriverClass()).thenReturn("org.sqlite.JDBC");
when(mockDriver.getConnectionUrl(any())).thenReturn("jdbc:sqlite::memory:");
when(mockDriver.getDataSourceProperties()).thenReturn(List.of());
driverRegistry.register(mockDriver);

when(mockPlugin.registryAccess()).thenReturn(RegistryAccess.registryAccess());

TestDatabase testDb = new TestDatabase(mockPlugin, DatabaseDriverType.SQLITE);

HikariDataSource mockDataSource = mock(HikariDataSource.class);
try {
when(mockDataSource.getConnection()).thenThrow(new SQLException("Pool exhausted"));
} catch (SQLException e) {
throw new RuntimeException(e);
}

try {
var field = Database.class.getDeclaredField("dataSource");
field.setAccessible(true);
field.set(testDb, mockDataSource);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}

RuntimeException ex = assertThrows(RuntimeException.class, testDb::getConnection);
assertInstanceOf(SQLException.class, ex.getCause());
assertEquals("Pool exhausted", ex.getCause().getMessage());

testDb.getDatabaseExecutorService().shutdownNow();
} finally {
RegistryResetExtension.resetRegistry();
}
}

@Test
@DisplayName("Given getTables throws SQLException inside tableExists lambda, when checking tableExists, then returns false")
void tableExists_returnsFalse_whenGetTablesThrowsSQLException() throws SQLException {
Connection mockConnection = mock(Connection.class);
DatabaseMetaData mockMetaData = mock(DatabaseMetaData.class);

when(mockConnection.getMetaData()).thenReturn(mockMetaData);
when(mockMetaData.getTables(null, null, null, new String[]{"TABLE"})).thenThrow(new SQLException("getTables failed"));

assertFalse(database.tableExists(mockConnection, "any_table"));
}

static class TestDatabase extends Database {

private static final Credentials CREDENTIALS = new Credentials(
Expand Down
107 changes: 107 additions & 0 deletions src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.diamonddagger590.mccore.gui;

import com.diamonddagger590.mccore.CorePlugin;
import com.diamonddagger590.mccore.event.gui.CoreGuiOpenEvent;
import com.diamonddagger590.mccore.player.CorePlayer;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.util.Optional;
Expand All @@ -19,6 +24,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
Expand Down Expand Up @@ -330,4 +336,105 @@ void trackPlayerGui_firesEvent_always() {

verify(mockPluginManager).callEvent(any());
}

@SuppressWarnings("unchecked")
private <T extends Gui<CorePlayer> & KeyedGui> T createMockKeyedGui(@NotNull NamespacedKey key) {
T gui = (T) mock(Gui.class, org.mockito.Mockito.withSettings().extraInterfaces(KeyedGui.class));
when(gui.getUUID()).thenReturn(UUID.randomUUID());
when(((KeyedGui) gui).getGuiKey()).thenReturn(Optional.of(key));
return gui;
}

@Test
@DisplayName("Given a KeyedGui, when trackPlayerGui, then CoreGuiOpenEvent contains the gui key")
void trackPlayerGui_eventContainsGuiKey_whenGuiIsKeyed() {
UUID playerUUID = UUID.randomUUID();
NamespacedKey expectedKey = new NamespacedKey("test", "my_gui");
Gui<CorePlayer> gui = createMockKeyedGui(expectedKey);

guiManager.trackPlayerGui(playerUUID, gui);

ArgumentCaptor<CoreGuiOpenEvent> captor = ArgumentCaptor.forClass(CoreGuiOpenEvent.class);
verify(mockPluginManager).callEvent(captor.capture());
CoreGuiOpenEvent event = captor.getValue();

assertEquals(playerUUID, event.getPlayerUUID());
assertTrue(event.getGuiKey().isPresent());
assertEquals(expectedKey, event.getGuiKey().get());
}

@Test
@DisplayName("Given a non-KeyedGui, when trackPlayerGui, then CoreGuiOpenEvent has empty gui key")
void trackPlayerGui_eventHasEmptyKey_whenGuiIsNotKeyed() {
UUID playerUUID = UUID.randomUUID();
Gui<CorePlayer> gui = createMockGui();

guiManager.trackPlayerGui(playerUUID, gui);

ArgumentCaptor<CoreGuiOpenEvent> captor = ArgumentCaptor.forClass(CoreGuiOpenEvent.class);
verify(mockPluginManager).callEvent(captor.capture());
CoreGuiOpenEvent event = captor.getValue();

assertFalse(event.getGuiKey().isPresent());
}

@Test
@DisplayName("Given a gui with multiple online viewers, when refreshGui, then updates inventory for all")
void refreshGui_updatesAllViewers_whenMultipleOnline() {
UUID player1UUID = UUID.randomUUID();
UUID player2UUID = UUID.randomUUID();
UUID player3UUID = UUID.randomUUID();
Gui<CorePlayer> gui = createMockGui();
Player onlinePlayer1 = mock(Player.class);
Player onlinePlayer2 = mock(Player.class);
Player onlinePlayer3 = mock(Player.class);

guiManager.trackPlayerGui(player1UUID, gui);
guiManager.trackPlayerGui(player2UUID, gui);
guiManager.trackPlayerGui(player3UUID, gui);

when(mockServer.getPlayer(player1UUID)).thenReturn(onlinePlayer1);
when(mockServer.getPlayer(player2UUID)).thenReturn(onlinePlayer2);
when(mockServer.getPlayer(player3UUID)).thenReturn(onlinePlayer3);

guiManager.refreshGui(gui);

verify(onlinePlayer1).updateInventory();
verify(onlinePlayer2).updateInventory();
verify(onlinePlayer3).updateInventory();
}

@Test
@DisplayName("Given a gui with mixed online/offline viewers, when refreshGui, then only updates online viewers")
void refreshGui_updatesOnlyOnlineViewers_whenMixedOnlineOffline() {
UUID onlineUUID = UUID.randomUUID();
UUID offlineUUID = UUID.randomUUID();
Gui<CorePlayer> gui = createMockGui();
Player onlinePlayer = mock(Player.class);

guiManager.trackPlayerGui(onlineUUID, gui);
guiManager.trackPlayerGui(offlineUUID, gui);

when(mockServer.getPlayer(onlineUUID)).thenReturn(onlinePlayer);
when(mockServer.getPlayer(offlineUUID)).thenReturn(null);

guiManager.refreshGui(gui);

verify(onlinePlayer).updateInventory();
}

@Test
@DisplayName("Given trackPlayerGui fired event, then event carries the correct gui instance")
void trackPlayerGui_eventCarriesGuiInstance_always() {
UUID playerUUID = UUID.randomUUID();
Gui<CorePlayer> gui = createMockGui();

guiManager.trackPlayerGui(playerUUID, gui);

ArgumentCaptor<CoreGuiOpenEvent> captor = ArgumentCaptor.forClass(CoreGuiOpenEvent.class);
verify(mockPluginManager).callEvent(captor.capture());
CoreGuiOpenEvent event = captor.getValue();

assertSame(gui, event.getGui());
}
}
Loading