diff --git a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java new file mode 100644 index 000000000..d1198e7e4 --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java @@ -0,0 +1,193 @@ +package fr.xephi.authme.listener; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityInteractEvent; +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.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.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link EntityListener}. + */ +@RunWith(MockitoJUnitRunner.class) +public class EntityListenerTest { + + @InjectMocks + private EntityListener listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldHandleSimpleEvents() { + checkEventIsCanceledForUnauthed(listener, listenerService, EntityTargetEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, FoodLevelChangeEvent.class); + checkEventIsCanceledForUnauthed(listener, listenerService, EntityShootBowEvent.class); + } + + @Test + public void shouldCancelEntityInteractEvent() { + // given + EntityInteractEvent event = mock(EntityInteractEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(true); + + // when + listener.onLowestEntityInteract(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verify(event).setCancelled(true); + } + + @Test + public void shouldNotCancelEntityInteractEvent() { + // given + EntityInteractEvent event = mock(EntityInteractEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(false); + + // when + listener.onLowestEntityInteract(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verifyZeroInteractions(event); + } + + @Test + public void shouldCancelEntityInteractEventHighest() { + // given + EntityInteractEvent event = mock(EntityInteractEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(true); + + // when + listener.onEntityInteract(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verify(event).setCancelled(true); + } + + @Test + public void shouldNotCancelEntityInteractEventHighest() { + // given + EntityInteractEvent event = mock(EntityInteractEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(false); + + // when + listener.onEntityInteract(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verifyZeroInteractions(event); + } + + @Test + public void shouldCancelRegainHealthEvent() { + // given + EntityRegainHealthEvent event = mock(EntityRegainHealthEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(true); + + // when + listener.entityRegainHealthEvent(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verify(event).setCancelled(true); + verify(event).setAmount(0); + } + + @Test + public void shouldNotCancelRegainedHealth() { + // given + EntityRegainHealthEvent event = mock(EntityRegainHealthEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(false); + + // when + listener.entityRegainHealthEvent(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verifyZeroInteractions(event); + } + + @Test + public void shouldCancelEntityDamageByEntityEvent() { + // given + EntityDamageByEntityEvent event = mock(EntityDamageByEntityEvent.class); + Entity player = mock(Player.class); + given(event.getDamager()).willReturn(player); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onAttack(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(event).setCancelled(true); + } + + @Test + public void shouldNotCancelEntityDamageByEntityEvent() { + // given + EntityDamageByEntityEvent event = mock(EntityDamageByEntityEvent.class); + Entity player = mock(Player.class); + given(event.getDamager()).willReturn(player); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onAttack(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(event, only()).getDamager(); + } + + @Test + public void shouldCancelEntityDamageEvent() { + // given + EntityDamageEvent event = mock(EntityDamageEvent.class); + Entity entity = mock(Entity.class); + given(event.getEntity()).willReturn(entity); + given(listenerService.shouldCancelEvent(event)).willReturn(true); + + // when + listener.onDamage(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verify(event).setCancelled(true); + verify(event).setDamage(0); + verify(entity).setFireTicks(0); + } + + @Test + public void shouldNotCancelEntityDamageEvent() { + // given + EntityDamageEvent event = mock(EntityDamageEvent.class); + given(listenerService.shouldCancelEvent(event)).willReturn(false); + + // when + listener.onDamage(event); + + // then + verify(listenerService).shouldCancelEvent(event); + verifyZeroInteractions(event); + } + +} diff --git a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java index 1a1125c87..aef01ed61 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java @@ -53,7 +53,7 @@ public final class ListenerConsistencyTest { } @Test - public void shouldNotHaveMultipleHandlersForSameEvent() { + public void shouldNotHaveMultipleMethodsWithSameName() { Set events = new HashSet<>(); for (Class listener : LISTENERS) { for (Method method : listener.getDeclaredMethods()) { diff --git a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java index 172768582..bc78db2f3 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java @@ -225,8 +225,7 @@ public class ListenerServiceTest { } /** - * Test implementation of {@link PlayerEvent} (necessary because - * {@link PlayerEvent#getPlayer()} is declared final). + * Test implementation of {@link PlayerEvent}. */ private static final class TestPlayerEvent extends PlayerEvent { public TestPlayerEvent(Player player) { diff --git a/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java b/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java index 4a8638632..cdc07cce8 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerTestUtils.java @@ -23,6 +23,18 @@ public final class ListenerTestUtils { private ListenerTestUtils() { } + /** + * Tests a simple event handler that checks with the {@link ListenerService} + * if the event should be canceled or not. This method tests that the event is + * canceled when the service says so and the other way around. Do not use this + * method if the handler method has additional behavior. + * + * + * @param listener the listener to test + * @param listenerService the listener service mock + * @param clazz the event class to test the handler method for + * @param the event type + */ public static void checkEventIsCanceledForUnauthed(Listener listener, ListenerService listenerService, Class clazz) { Method handlerMethod = findMethod(listener, clazz); @@ -38,6 +50,14 @@ public final class ListenerTestUtils { verifyZeroInteractions(event); } + /** + * Mocks, based on the given event, the correct method in {@link ListenerService} to return + * the provided {@code result}. + * + * @param result the result the service should return + * @param listenerService the service to mock + * @param event the event + */ private static void mockShouldCancel(boolean result, ListenerService listenerService, Event event) { if (event instanceof PlayerEvent) { given(listenerService.shouldCancelEvent((PlayerEvent) event)).willReturn(result); @@ -48,7 +68,15 @@ public final class ListenerTestUtils { } } - private static Method findMethod(Listener listener, Class paramType) { + /** + * Returns the method in the listener that takes the given event type as parameter. + * + * @param listener the listener to scan + * @param paramType the event type + * @return the mapped method + * @throws IllegalStateException if there is not exactly one method with the given event type as parameter + */ + private static Method findMethod(Listener listener, Class paramType) { Method matchingMethod = null; for (Method method : listener.getClass().getMethods()) { if (method.isAnnotationPresent(EventHandler.class)) { diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java new file mode 100644 index 000000000..980f9dabb --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java @@ -0,0 +1,27 @@ +package fr.xephi.authme.listener; + +import org.bukkit.event.player.PlayerEditBookEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Test for {@link PlayerListener16}. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlayerListener16Test { + + @InjectMocks + private PlayerListener16 listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldCancelEvent() { + ListenerTestUtils.checkEventIsCanceledForUnauthed(listener, listenerService, PlayerEditBookEvent.class); + } + +} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java new file mode 100644 index 000000000..79466a31d --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java @@ -0,0 +1,27 @@ +package fr.xephi.authme.listener; + +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Test for {@link PlayerListener18}. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlayerListener18Test { + + @InjectMocks + private PlayerListener18 listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldCancelEvent() { + ListenerTestUtils.checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractAtEntityEvent.class); + } + +} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 49301016a..cfbea94d1 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -2,17 +2,22 @@ package fr.xephi.authme.listener; import fr.xephi.authme.AntiBot; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; 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.HooksSettings; 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.Server; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; @@ -21,18 +26,31 @@ 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.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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed; +import static org.hamcrest.Matchers.contains; 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.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; /** @@ -47,7 +65,7 @@ public class PlayerListenerTest { @Mock private Settings settings; @Mock - private Messages m; + private Messages messages; @Mock private DataSource dataSource; @Mock @@ -135,10 +153,173 @@ public class PlayerListenerTest { checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractEntityEvent.class); } + @Test + public void shouldAllowEssentialsMotd() { + // given + given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(true); + PlayerCommandPreprocessEvent event = mockCommandEvent("/MOTD"); + + // when + listener.onPlayerCommandPreprocess(event); + + // then + verify(event, only()).getMessage(); + verifyZeroInteractions(listenerService, messages); + } + + @Test + public void shouldNotStopAllowedCommand() { + // given + given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(true); + given(settings.getProperty(RestrictionSettings.ALLOW_COMMANDS)) + .willReturn(Arrays.asList("/plugins", "/mail", "/msg")); + PlayerCommandPreprocessEvent event = mockCommandEvent("/Mail send test Test"); + + // when + listener.onPlayerCommandPreprocess(event); + + // then + verify(event, only()).getMessage(); + verifyZeroInteractions(listenerService, messages); + } + + @Test + public void shouldNotCancelEventForAuthenticatedPlayer() { + // given + given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(false); + given(settings.getProperty(RestrictionSettings.ALLOW_COMMANDS)).willReturn(Collections.emptyList()); + Player player = playerWithMockedServer(); + // PlayerCommandPreprocessEvent#getPlayer is final, so create a spy instead of a mock + PlayerCommandPreprocessEvent event = spy(new PlayerCommandPreprocessEvent(player, "/hub")); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onPlayerCommandPreprocess(event); + + // then + verify(event).getMessage(); + verifyNoMoreInteractions(event); + verify(listenerService).shouldCancelEvent(player); + verifyZeroInteractions(messages); + } + + @Test + public void shouldCancelCommandEvent() { + // given + given(settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)).willReturn(false); + given(settings.getProperty(RestrictionSettings.ALLOW_COMMANDS)).willReturn(Arrays.asList("/spawn", "/help")); + Player player = playerWithMockedServer(); + PlayerCommandPreprocessEvent event = spy(new PlayerCommandPreprocessEvent(player, "/hub")); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onPlayerCommandPreprocess(event); + + // then + verify(listenerService).shouldCancelEvent(player); + verify(event).setCancelled(true); + verify(messages).send(player, MessageKey.DENIED_COMMAND); + } + + @Test + public void shouldAllowChat() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(true); + AsyncPlayerChatEvent event = mock(AsyncPlayerChatEvent.class); + + // when + listener.onPlayerChat(event); + + // then + verifyZeroInteractions(event, listenerService, messages); + } + + @Test + public void shouldCancelChatForUnauthedPlayer() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + AsyncPlayerChatEvent event = newAsyncChatEvent(); + given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(true); + + // when + listener.onPlayerChat(event); + + // then + verify(listenerService).shouldCancelEvent(event.getPlayer()); + verify(event).setCancelled(true); + verify(messages).send(event.getPlayer(), MessageKey.DENIED_CHAT); + } + + @Test + public void shouldSendChatToEveryone() { + // given + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + AsyncPlayerChatEvent event = newAsyncChatEvent(); + given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(false); + + // when + listener.onPlayerChat(event); + + // then + verify(listenerService).shouldCancelEvent(event.getPlayer()); + verifyZeroInteractions(event, messages); + } + + @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); + AsyncPlayerChatEvent event = newAsyncChatEvent(); + given(listenerService.shouldCancelEvent(event.getPlayer())).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(true); + List recipients = new ArrayList<>(event.getRecipients()); + given(listenerService.shouldCancelEvent(recipients.get(0))).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)); + assertThat(event.getRecipients(), contains(recipients.get(1), recipients.get(0))); + } + private static Player mockPlayerWithName(String name) { Player player = mock(Player.class); given(player.getName()).willReturn(name); return player; } + /** + * {@link PlayerCommandPreprocessEvent} gets the list of online players from the player's server. + * This method creates a Player mock with all necessary mocked behavior. + * + * @return Player mock + */ + @SuppressWarnings("unchecked") + private static Player playerWithMockedServer() { + Server server = mock(Server.class); + given(server.getOnlinePlayers()).willReturn(Collections.EMPTY_LIST); + Player player = mock(Player.class); + given(player.getServer()).willReturn(server); + return player; + } + + private static PlayerCommandPreprocessEvent mockCommandEvent(String message) { + PlayerCommandPreprocessEvent commandEvent = mock(PlayerCommandPreprocessEvent.class); + given(commandEvent.getMessage()).willReturn(message); + return commandEvent; + } + + private static AsyncPlayerChatEvent newAsyncChatEvent() { + Player player = mock(Player.class); + List recipients = Arrays.asList(mock(Player.class), mock(Player.class), mock(Player.class)); + return spy(new AsyncPlayerChatEvent(true, player, "Test message", new HashSet<>(recipients))); + } + }