diff --git a/src/main/java/fr/xephi/authme/converter/RakamakConverter.java b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java index f8892e393..9cf6749fd 100644 --- a/src/main/java/fr/xephi/authme/converter/RakamakConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java @@ -82,15 +82,14 @@ public class RakamakConverter implements Converter { .realName(playerName) .ip(ip) .password(psw) - .lastLogin(System.currentTimeMillis()) + .lastLogin(0) .build(); database.saveAuth(auth); } ConsoleLogger.info("Rakamak database has been imported correctly"); sender.sendMessage("Rakamak database has been imported correctly"); } catch (IOException ex) { - ConsoleLogger.warning(ex.getMessage()); - sender.sendMessage("Can't open the rakamak database file! Does it exist?"); + ConsoleLogger.logException("Can't open the rakamak database file! Does it exist?", ex); } } } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index d6e74b803..661e3fad9 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -877,8 +877,7 @@ public class MySQL implements DataSource { * @param metaData lastlogin column meta data * @throws SQLException */ - @VisibleForTesting - protected void migrateLastLoginColumn(Connection con, DatabaseMetaData metaData) throws SQLException { + private void migrateLastLoginColumn(Connection con, DatabaseMetaData metaData) throws SQLException { final int columnType; try (ResultSet rs = metaData.getColumns(null, null, tableName, col.LAST_LOGIN)) { if (!rs.next()) { diff --git a/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java b/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java index 31957cdb9..b189fd071 100644 --- a/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java +++ b/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java @@ -6,9 +6,9 @@ import fr.xephi.authme.util.StringUtils; /** * Exception thrown when a verification has failed. */ -@SuppressWarnings("serial") public class FailedVerificationException extends Exception { + private static final long serialVersionUID = 3903242223297960699L; private final MessageKey reason; private final String[] args; diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index 148635675..fb736fa24 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -225,7 +225,7 @@ public class PlayerListener implements Listener { // Get the auth later as this may cause the single session check to fail // Slow stuff - final PlayerAuth auth = dataSource.getAuth(player.getName()); + final PlayerAuth auth = dataSource.getAuth(name); final boolean isAuthAvailable = (auth != null); final String lowerName = name.toLowerCase(); onJoinVerifier.checkAntibot(lowerName, isAuthAvailable); diff --git a/src/main/java/fr/xephi/authme/listener/ServerListener.java b/src/main/java/fr/xephi/authme/listener/ServerListener.java index 80081b90a..e3cbb813a 100644 --- a/src/main/java/fr/xephi/authme/listener/ServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/ServerListener.java @@ -49,9 +49,7 @@ public class ServerListener implements Listener { } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { spawnLoader.unloadEssentialsSpawn(); ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking"); - } - - if (pluginName.equalsIgnoreCase("ProtocolLib")) { + } else if ("ProtocolLib".equalsIgnoreCase(pluginName)) { protocolLibService.disable(); ConsoleLogger.warning("ProtocolLib has been disabled, unhooking packet adapters!"); } @@ -77,9 +75,7 @@ public class ServerListener implements Listener { pluginHooks.tryHookToCombatPlus(); } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { spawnLoader.loadEssentialsSpawn(); - } - - if (pluginName.equalsIgnoreCase("ProtocolLib")) { + } else if ("ProtocolLib".equalsIgnoreCase(pluginName)) { protocolLibService.setup(); } } diff --git a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java index d1198e7e4..0b7b00845 100644 --- a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.listener; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityInteractEvent; @@ -9,6 +10,8 @@ import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.projectiles.ProjectileSource; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -17,7 +20,9 @@ import org.mockito.runners.MockitoJUnitRunner; import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed; import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -190,4 +195,56 @@ public class EntityListenerTest { verifyZeroInteractions(event); } + @Test + public void shouldAllowProjectileLaunchFromNonHuman() { + // given + Projectile projectile = mock(Projectile.class); + ProjectileSource source = mock(ProjectileSource.class); + given(projectile.getShooter()).willReturn(source); + ProjectileLaunchEvent event = mock(ProjectileLaunchEvent.class); + given(event.getEntity()).willReturn(projectile); + + // when + listener.onProjectileLaunch(event); + + // then + verifyZeroInteractions(listenerService); + verify(event, never()).setCancelled(anyBoolean()); + } + + @Test + public void shouldAllowProjectileLaunchFromAuthedHuman() { + // given + Projectile projectile = mock(Projectile.class); + Player player = mock(Player.class); + given(projectile.getShooter()).willReturn(player); + ProjectileLaunchEvent event = mock(ProjectileLaunchEvent.class); + given(event.getEntity()).willReturn(projectile); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onProjectileLaunch(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(event, never()).setCancelled(anyBoolean()); + } + + @Test + public void shouldRejectProjectileLaunchFromUnauthed() { + // given + Projectile projectile = mock(Projectile.class); + Player player = mock(Player.class); + given(projectile.getShooter()).willReturn(player); + ProjectileLaunchEvent event = mock(ProjectileLaunchEvent.class); + given(event.getEntity()).willReturn(projectile); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onProjectileLaunch(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(event).setCancelled(true); + } } diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index aaa93db2e..35179e6e7 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.listener; import fr.xephi.authme.AntiBot; +import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; @@ -25,17 +26,19 @@ import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerShearEntityEvent; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -44,12 +47,16 @@ import java.util.List; import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -271,9 +278,6 @@ public class PlayerListenerTest { } @Test - @Ignore - // TODO ljacqu 20160804: Fix assertion that recipient is removed from list - // Somehow getRecipient() at the end still has all three initial users public void shouldHideChatFromUnauthed() { // given given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); @@ -290,7 +294,28 @@ public class PlayerListenerTest { verify(listenerService).shouldCancelEvent(event.getPlayer()); // message sender + 3 recipients = 4 verify(listenerService, times(4)).shouldCancelEvent(any(Player.class)); - assertThat(event.getRecipients(), contains(recipients.get(1), recipients.get(0))); + verify(event, never()).setCancelled(anyBoolean()); + assertThat(event.getRecipients(), contains(recipients.get(1), recipients.get(2))); + } + + @Test + public void shouldCancelChatEventForNoRemainingRecipients() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + AsyncPlayerChatEvent event = newAsyncChatEvent(); + given(listenerService.shouldCancelEvent(any(Player.class))).willReturn(true); + given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(true); + + // when + listener.onPlayerChat(event); + + // then + verify(listenerService).shouldCancelEvent(event.getPlayer()); + // message sender + 3 recipients = 4 + verify(listenerService, times(4)).shouldCancelEvent(any(Player.class)); + verify(event).setCancelled(true); + assertThat(event.getRecipients(), empty()); } @Test @@ -364,7 +389,7 @@ public class PlayerListenerTest { } @Test - public void shouldTeleportPlayerToSpawn() { + public void shouldTeleportPlayerInDifferentWorldToSpawn() { // given given(settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)).willReturn(true); given(settings.getProperty(RestrictionSettings.ALLOWED_MOVEMENT_RADIUS)).willReturn(20); @@ -390,6 +415,188 @@ public class PlayerListenerTest { verifyNoModifyingCalls(event); } + @Test + public void shouldAllowMovementWithinRadius() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)).willReturn(true); + given(settings.getProperty(RestrictionSettings.ALLOWED_MOVEMENT_RADIUS)).willReturn(12); + World world = mock(World.class); + Player player = mock(Player.class); + given(player.getWorld()).willReturn(world); + Location from = new Location(world, 200, 70, 200); + Location to = new Location(world, 199, 69, 201); + PlayerMoveEvent event = spy(new PlayerMoveEvent(player, from, to)); + given(player.getLocation()).willReturn(from); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); + // sqrt(10^2 + 2^2 + 4^2) = 11 < 12 (allowed movement radius) + Location spawn = new Location(world, 190, 72, 204); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + + // when + listener.onPlayerMove(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(player, never()).teleport(any(Location.class)); + verify(spawnLoader).getSpawnLocation(player); + verifyNoModifyingCalls(event); + } + + @Test + public void shouldRejectMovementOutsideOfRadius() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)).willReturn(true); + given(settings.getProperty(RestrictionSettings.ALLOWED_MOVEMENT_RADIUS)).willReturn(12); + World world = mock(World.class); + Player player = mock(Player.class); + given(player.getWorld()).willReturn(world); + Location from = new Location(world, 200, 70, 200); + Location to = new Location(world, 199, 69, 201); + PlayerMoveEvent event = spy(new PlayerMoveEvent(player, from, to)); + given(player.getLocation()).willReturn(from); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); + // sqrt(15^2 + 2^2 + 4^2) = 16 > 12 (allowed movement radius) + Location spawn = new Location(world, 185, 72, 204); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + + // when + listener.onPlayerMove(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(player).teleport(spawn); + verify(spawnLoader).getSpawnLocation(player); + verifyNoModifyingCalls(event); + } + + @Test + public void shouldHandlePlayerJoining() { + // given + Player player = mock(Player.class); + PlayerJoinEvent event = new PlayerJoinEvent(player, "join message"); + + // when + listener.onPlayerJoin(event); + + // then + verify(teleportationService).teleportNewPlayerToFirstSpawn(player); + verify(management).performJoin(player); + } + + @Test + public void shouldNotInterfereWithUnrestrictedUser() { + // given + String name = "Player01"; + Player player = mockPlayerWithName(name); + PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", null)); + given(validationService.isUnrestricted(name)).willReturn(true); + + // when + listener.onPlayerLogin(event); + + // then + verify(validationService).isUnrestricted(name); + verifyNoModifyingCalls(event); + verifyZeroInteractions(onJoinVerifier); + } + + @Test + public void shouldStopHandlingForFullServer() { + // given + String name = "someone"; + Player player = mockPlayerWithName(name); + PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", null)); + given(validationService.isUnrestricted(name)).willReturn(false); + given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(true); + + // when + listener.onPlayerLogin(event); + + // then + verify(validationService).isUnrestricted(name); + verify(onJoinVerifier, only()).refusePlayerForFullServer(event); + verifyNoModifyingCalls(event); + } + + @Test + public void shouldStopHandlingEventForBadResult() { + // given + String name = "someone"; + Player player = mockPlayerWithName(name); + PlayerLoginEvent event = new PlayerLoginEvent(player, "", null); + event.setResult(PlayerLoginEvent.Result.KICK_BANNED); + event = spy(event); + given(validationService.isUnrestricted(name)).willReturn(false); + given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false); + + // when + listener.onPlayerLogin(event); + + // then + verify(validationService).isUnrestricted(name); + verify(onJoinVerifier, only()).refusePlayerForFullServer(event); + verifyNoModifyingCalls(event); + } + + @Test + public void shouldPerformAllJoinVerificationsSuccessfully() throws FailedVerificationException { + // given + String name = "someone"; + Player player = mockPlayerWithName(name); + String ip = "12.34.56.78"; + PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", mockAddrWithIp(ip))); + given(validationService.isUnrestricted(name)).willReturn(false); + given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false); + PlayerAuth auth = PlayerAuth.builder().name(name).build(); + given(dataSource.getAuth(name)).willReturn(auth); + + // when + listener.onPlayerLogin(event); + + // then + verify(validationService).isUnrestricted(name); + verify(onJoinVerifier).refusePlayerForFullServer(event); + verify(onJoinVerifier).checkSingleSession(name); + verify(onJoinVerifier).checkIsValidName(name); + verify(onJoinVerifier).checkAntibot(name, true); + verify(onJoinVerifier).checkKickNonRegistered(true); + verify(onJoinVerifier).checkNameCasing(player, auth); + verify(onJoinVerifier).checkPlayerCountry(true, ip); + verify(antiBot).handlePlayerJoin(player); + verify(teleportationService).teleportOnJoin(player); + verifyNoModifyingCalls(event); + } + + @Test + public void shouldAbortPlayerJoinForInvalidName() throws FailedVerificationException { + // given + String name = "inval!dName"; + Player player = mockPlayerWithName(name); + PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", null)); + given(validationService.isUnrestricted(name)).willReturn(false); + given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false); + FailedVerificationException exception = new FailedVerificationException( + MessageKey.INVALID_NAME_CHARACTERS, "[a-z]"); + doThrow(exception).when(onJoinVerifier).checkIsValidName(name); + String message = "Invalid characters!"; + given(messages.retrieveSingle(exception.getReason(), exception.getArgs())).willReturn(message); + + // when + listener.onPlayerLogin(event); + + // then + verify(validationService).isUnrestricted(name); + verify(onJoinVerifier).refusePlayerForFullServer(event); + verify(onJoinVerifier).checkSingleSession(name); + verify(onJoinVerifier).checkIsValidName(name); + // Check that we don't talk with the data source before performing checks that don't require it + verifyZeroInteractions(dataSource); + verify(event).setKickMessage(message); + verify(event).setResult(PlayerLoginEvent.Result.KICK_OTHER); + } + private static Player mockPlayerWithName(String name) { Player player = mock(Player.class); given(player.getName()).willReturn(name); @@ -429,4 +636,16 @@ public class PlayerListenerTest { verifyNoMoreInteractions(event); } + private static void verifyNoModifyingCalls(PlayerLoginEvent event) { + verify(event, atLeast(0)).getResult(); + verify(event, atLeast(0)).getAddress(); + verifyNoMoreInteractions(event); + } + + private static InetAddress mockAddrWithIp(String ip) { + InetAddress addr = mock(InetAddress.class); + given(addr.getHostAddress()).willReturn(ip); + return addr; + } + } diff --git a/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java b/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java new file mode 100644 index 000000000..0c1686a11 --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java @@ -0,0 +1,178 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.listener.protocollib.ProtocolLibService; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.SpawnLoader; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.event.server.PluginEvent; +import org.bukkit.plugin.Plugin; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Test for {@link ServerListener}. + */ +@RunWith(MockitoJUnitRunner.class) +public class ServerListenerTest { + + private static final String ESSENTIALS = "Essentials"; + private static final String ESSENTIALS_SPAWN = "EssentialsSpawn"; + private static final String MULTIVERSE = "Multiverse-Core"; + private static final String COMBAT_TAG = "CombatTagPlus"; + private static final String PROTOCOL_LIB = "ProtocolLib"; + + @InjectMocks + private ServerListener serverListener; + + @Mock + private PermissionsManager permissionsManager; + + @Mock + private PluginHooks pluginHooks; + + @Mock + private ProtocolLibService protocolLibService; + + @Mock + private SpawnLoader spawnLoader; + + @BeforeClass + public static void initLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldForwardPluginNameOnEnable() { + checkEnableHandling(ESSENTIALS, new Runnable() { + @Override + public void run() { + verify(pluginHooks).tryHookToEssentials(); + } + }); + checkEnableHandling(ESSENTIALS_SPAWN, new Runnable() { + @Override + public void run() { + verify(spawnLoader).loadEssentialsSpawn(); + } + }); + checkEnableHandling(MULTIVERSE, new Runnable() { + @Override + public void run() { + verify(pluginHooks).tryHookToMultiverse(); + } + }); + checkEnableHandling(COMBAT_TAG, new Runnable() { + @Override + public void run() { + verify(pluginHooks).tryHookToCombatPlus(); + } + }); + checkEnableHandling(PROTOCOL_LIB, new Runnable() { + @Override + public void run() { + verify(protocolLibService).setup(); + } + }); + checkEnableHandling("UnknownPlugin", new Runnable() { + @Override + public void run() { + // nothing + } + }); + } + + @Test + public void shouldForwardPluginNameOnDisable() { + checkDisableHandling(ESSENTIALS, new Runnable() { + @Override + public void run() { + verify(pluginHooks).unhookEssentials(); + } + }); + checkDisableHandling(ESSENTIALS_SPAWN, new Runnable() { + @Override + public void run() { + verify(spawnLoader).unloadEssentialsSpawn(); + } + }); + checkDisableHandling(MULTIVERSE, new Runnable() { + @Override + public void run() { + verify(pluginHooks).unhookMultiverse(); + } + }); + checkDisableHandling(COMBAT_TAG, new Runnable() { + @Override + public void run() { + verify(pluginHooks).unhookCombatPlus(); + } + }); + checkDisableHandling(PROTOCOL_LIB, new Runnable() { + @Override + public void run() { + verify(protocolLibService).disable(); + } + }); + checkDisableHandling("UnknownPlugin", new Runnable() { + @Override + public void run() { + // nothing + } + }); + } + + @Test + public void shouldHandlePluginWithNullName() { + PluginEnableEvent enableEvent = mock(PluginEnableEvent.class); + given(enableEvent.getPlugin()).willReturn(null); + serverListener.onPluginEnable(enableEvent); + verifyNoMoreInteractionsAndReset(); + + PluginDisableEvent disableEvent = mock(PluginDisableEvent.class); + given(disableEvent.getPlugin()).willReturn(null); + serverListener.onPluginDisable(disableEvent); + verifyNoMoreInteractionsAndReset(); + } + + private void checkEnableHandling(String pluginName, Runnable verifier) { + PluginEnableEvent event = mockEventWithPluginName(PluginEnableEvent.class, pluginName); + serverListener.onPluginEnable(event); + verifier.run(); + verify(permissionsManager).onPluginEnable(pluginName); + verifyNoMoreInteractionsAndReset(); + } + + private void checkDisableHandling(String pluginName, Runnable verifier) { + PluginDisableEvent event = mockEventWithPluginName(PluginDisableEvent.class, pluginName); + serverListener.onPluginDisable(event); + verifier.run(); + verify(permissionsManager).onPluginDisable(pluginName); + verifyNoMoreInteractionsAndReset(); + } + + private void verifyNoMoreInteractionsAndReset() { + verifyNoMoreInteractions(permissionsManager, pluginHooks, protocolLibService, spawnLoader); + reset(permissionsManager, pluginHooks, protocolLibService, spawnLoader); + } + + private static T mockEventWithPluginName(Class eventClass, String name) { + T event = mock(eventClass); + Plugin plugin = mock(Plugin.class); + given(plugin.getName()).willReturn(name); + given(event.getPlugin()).willReturn(plugin); + return event; + } +}