From bb52e0120c46e22a39843c32f4b5a6faa1f128ed Mon Sep 17 00:00:00 2001 From: ljacqu Date: Wed, 3 Aug 2016 22:11:48 +0200 Subject: [PATCH] Write first unit tests for PlayerListener --- .../xephi/authme/listener/PlayerListener.java | 2 +- .../authme/listener/ListenerTestUtils.java | 71 +++++++++ .../authme/listener/PlayerListenerTest.java | 144 ++++++++++++++++++ 3 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java create mode 100644 src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index c0f851a2..ae29a5fa 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -210,7 +210,7 @@ public class PlayerListener implements Listener { public void onPlayerLogin(PlayerLoginEvent event) { final Player player = event.getPlayer(); final String name = player.getName(); - if (validationService.isUnrestricted(player.getName())) { + if (validationService.isUnrestricted(name)) { return; } else if (onJoinVerifier.refusePlayerForFullServer(event)) { return; diff --git a/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java b/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java new file mode 100644 index 00000000..4a863863 --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java @@ -0,0 +1,71 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.ReflectionTestUtils; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityEvent; +import org.bukkit.event.player.PlayerEvent; + +import java.lang.reflect.Method; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Utilities for testing AuthMe listener classes. + */ +public final class ListenerTestUtils { + + private ListenerTestUtils() { + } + + public static + void checkEventIsCanceledForUnauthed(Listener listener, ListenerService listenerService, Class clazz) { + Method handlerMethod = findMethod(listener, clazz); + + T event = mock(clazz); + mockShouldCancel(true, listenerService, event); + ReflectionTestUtils.invokeMethod(handlerMethod, listener, event); + verify(event).setCancelled(true); + + event = mock(clazz); + mockShouldCancel(false, listenerService, event); + ReflectionTestUtils.invokeMethod(handlerMethod, listener, event); + verifyZeroInteractions(event); + } + + private static void mockShouldCancel(boolean result, ListenerService listenerService, Event event) { + if (event instanceof PlayerEvent) { + given(listenerService.shouldCancelEvent((PlayerEvent) event)).willReturn(result); + } else if (event instanceof EntityEvent) { + given(listenerService.shouldCancelEvent((EntityEvent) event)).willReturn(result); + } else { + throw new IllegalStateException("Found event with unsupported type: " + event.getClass()); + } + } + + private static Method findMethod(Listener listener, Class paramType) { + Method matchingMethod = null; + for (Method method : listener.getClass().getMethods()) { + if (method.isAnnotationPresent(EventHandler.class)) { + Class[] parameters = method.getParameterTypes(); + if (parameters.length == 1 && parameters[0] == paramType) { + if (matchingMethod == null) { + matchingMethod = method; + } else { + throw new IllegalStateException("Found multiple eligible methods for " + paramType); + } + } + } + } + if (matchingMethod == null) { + throw new IllegalStateException("Found no matching method for " + paramType); + } + return matchingMethod; + } + +} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java new file mode 100644 index 00000000..49301016 --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -0,0 +1,144 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.AntiBot; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.Management; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.BukkitService; +import fr.xephi.authme.util.TeleportationService; +import fr.xephi.authme.util.ValidationService; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +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.PlayerKickEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link PlayerListener}. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlayerListenerTest { + + @InjectMocks + private PlayerListener listener; + + @Mock + private Settings settings; + @Mock + private Messages m; + @Mock + private DataSource dataSource; + @Mock + private AntiBot antiBot; + @Mock + private Management management; + @Mock + private BukkitService bukkitService; + @Mock + private SpawnLoader spawnLoader; + @Mock + private OnJoinVerifier onJoinVerifier; + @Mock + private ListenerService listenerService; + @Mock + private TeleportationService teleportationService; + @Mock + private ValidationService validationService; + + /** + * #831: If a player is kicked because of "logged in from another location", the kick + * should be CANCELED when single session is enabled. + */ + @Test + public void shouldCancelKick() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true); + Player player = mock(Player.class); + PlayerKickEvent event = new PlayerKickEvent(player, "You logged in from another location", ""); + + // when + listener.onPlayerKick(event); + + // then + assertThat(event.isCancelled(), equalTo(true)); + verifyZeroInteractions(player, management, antiBot); + } + + @Test + public void shouldNotCancelKick() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(false); + String name = "Bobby"; + Player player = mockPlayerWithName(name); + PlayerKickEvent event = new PlayerKickEvent(player, "You logged in from another location", ""); + given(antiBot.wasPlayerKicked(name)).willReturn(false); + + // when + listener.onPlayerKick(event); + + // then + assertThat(event.isCancelled(), equalTo(false)); + verify(antiBot).wasPlayerKicked(name); + verify(management).performQuit(player); + } + + @Test + public void shouldNotCancelOrdinaryKick() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true); + String name = "Bobby"; + Player player = mockPlayerWithName(name); + PlayerKickEvent event = new PlayerKickEvent(player, "No longer desired here!", ""); + given(antiBot.wasPlayerKicked(name)).willReturn(true); + + // when + listener.onPlayerKick(event); + + // then + assertThat(event.isCancelled(), equalTo(false)); + verify(antiBot).wasPlayerKicked(name); + verifyZeroInteractions(management); + } + + @Test + public void shouldHandleSimpleCancelableEvents() { + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerShearEntityEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerFishEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerBedEnterEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerDropItemEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, EntityDamageByEntityEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerItemConsumeEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerPickupItemEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractEntityEvent.class); + } + + private static Player mockPlayerWithName(String name) { + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + return player; + } + +}