|
@@ -0,0 +1,252 @@
|
|
|
+package com.gmail.nossr50.events.items;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
|
|
+import static org.mockito.Mockito.atLeastOnce;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+import org.bukkit.block.Block;
|
|
|
+import org.bukkit.block.BlockState;
|
|
|
+import org.bukkit.entity.Item;
|
|
|
+import org.bukkit.entity.Player;
|
|
|
+import org.bukkit.event.HandlerList;
|
|
|
+import org.bukkit.event.block.BlockDropItemEvent;
|
|
|
+import org.bukkit.inventory.ItemStack;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
+import org.junit.jupiter.api.Nested;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+
|
|
|
+class McMMOModifyBlockDropItemEventTest {
|
|
|
+ private BlockDropItemEvent blockDropItemEvent;
|
|
|
+ private Item itemEntity;
|
|
|
+ private ItemStack itemStack;
|
|
|
+ private Player player;
|
|
|
+ private Block block;
|
|
|
+ private BlockState blockState;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ // Mocks for delegate passthroughs
|
|
|
+ player = mock(Player.class, RETURNS_DEEP_STUBS);
|
|
|
+ block = mock(Block.class, RETURNS_DEEP_STUBS);
|
|
|
+ blockState = mock(BlockState.class, RETURNS_DEEP_STUBS);
|
|
|
+
|
|
|
+ // Primary Bukkit event mock
|
|
|
+ blockDropItemEvent = mock(BlockDropItemEvent.class, RETURNS_DEEP_STUBS);
|
|
|
+ when(blockDropItemEvent.getPlayer()).thenReturn(player);
|
|
|
+ when(blockDropItemEvent.getBlock()).thenReturn(block);
|
|
|
+ when(blockDropItemEvent.getBlockState()).thenReturn(blockState);
|
|
|
+
|
|
|
+ // Item + ItemStack mock
|
|
|
+ itemStack = mock(ItemStack.class);
|
|
|
+ when(itemStack.getAmount()).thenReturn(3); // original count
|
|
|
+ itemEntity = mock(Item.class);
|
|
|
+ when(itemEntity.getItemStack()).thenReturn(itemStack);
|
|
|
+ }
|
|
|
+
|
|
|
+ private McMMOModifyBlockDropItemEvent newEvent(int bonus) {
|
|
|
+ return new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, bonus);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("Constructor & validation")
|
|
|
+ class ConstructorValidation {
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void ctorNullEventThrows() {
|
|
|
+ assertThrows(NullPointerException.class,
|
|
|
+ () -> new McMMOModifyBlockDropItemEvent(null, itemEntity, 1));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void ctorNullItemThrows() {
|
|
|
+ assertThrows(NullPointerException.class,
|
|
|
+ () -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, null, 1));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void ctorZeroBonusThrows() {
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, 0));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void ctorNegativeBonusThrows() {
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, -5));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void ctorSetsOriginalsAndModifiedCorrectly() {
|
|
|
+ // original amount = 3, bonus = 2
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(2);
|
|
|
+ assertEquals(3, ev.getOriginalItemStackQuantity());
|
|
|
+ assertEquals(2, ev.getOriginalBonusAmountToAdd());
|
|
|
+ assertEquals(5, ev.getModifiedItemStackQuantity());
|
|
|
+ assertFalse(ev.isCancelled());
|
|
|
+ assertFalse(ev.isEffectivelyNoBonus());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("Cancellable contract")
|
|
|
+ class Cancellation {
|
|
|
+ @Test
|
|
|
+ void cancelAndUncancel() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertFalse(ev.isCancelled());
|
|
|
+ ev.setCancelled(true);
|
|
|
+ assertTrue(ev.isCancelled());
|
|
|
+ ev.setCancelled(false);
|
|
|
+ assertFalse(ev.isCancelled());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("Delta & absolute quantity semantics")
|
|
|
+ class DeltaAndAbsolute {
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void getBonusAmountToAddReflectsDifferenceFromOriginal() {
|
|
|
+ // original 3, bonus 4 => modified 7
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(4);
|
|
|
+ assertEquals(4, ev.getBonusAmountToAdd());
|
|
|
+ assertEquals(7, ev.getModifiedItemStackQuantity());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void setBonusAmountToAddUpdatesModifiedQuantity() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(2); // original 3 -> modified 5
|
|
|
+ ev.setBonusAmountToAdd(10); // new modified should be 13
|
|
|
+ assertEquals(13, ev.getModifiedItemStackQuantity());
|
|
|
+ assertEquals(10, ev.getBonusAmountToAdd());
|
|
|
+ assertFalse(ev.isEffectivelyNoBonus());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void setBonusAmountToAddNegativeThrows() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertThrows(IllegalArgumentException.class, () -> ev.setBonusAmountToAdd(-1));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void setModifiedItemStackQuantityEqualToOriginalIsNoBonus() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(2); // 3 -> 5
|
|
|
+ ev.setModifiedItemStackQuantity(3); // back to original => no bonus
|
|
|
+ assertEquals(3, ev.getModifiedItemStackQuantity());
|
|
|
+ assertEquals(0, ev.getBonusAmountToAdd());
|
|
|
+ assertTrue(ev.isEffectivelyNoBonus());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void setModifiedItemStackQuantityLessThanOriginalThrows() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertThrows(IllegalArgumentException.class, () -> ev.setModifiedItemStackQuantity(2)); // original is 3
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void setModifiedItemStackQuantityGreaterThanOriginalUpdatesBonus() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1); // original 3 -> modified 4
|
|
|
+ ev.setModifiedItemStackQuantity(12);
|
|
|
+ assertEquals(12, ev.getModifiedItemStackQuantity());
|
|
|
+ assertEquals(9, ev.getBonusAmountToAdd()); // 12 - 3
|
|
|
+ assertFalse(ev.isEffectivelyNoBonus());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("Delegate passthroughs")
|
|
|
+ class Delegates {
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void getPlayerPassthrough() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertSame(player, ev.getPlayer());
|
|
|
+ verify(blockDropItemEvent, atLeastOnce()).getPlayer();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void getBlockPassthrough() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertSame(block, ev.getBlock());
|
|
|
+ verify(blockDropItemEvent, atLeastOnce()).getBlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void getBlockStatePassthrough() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertSame(blockState, ev.getBlockState());
|
|
|
+ verify(blockDropItemEvent, atLeastOnce()).getBlockState();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void getItemReturnsOriginalItemEntity() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ assertSame(itemEntity, ev.getItem());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("HandlerList plumbing")
|
|
|
+ class HandlerListTests {
|
|
|
+ @Test
|
|
|
+ void handlerList_isNonNull_andShared() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(1);
|
|
|
+ HandlerList fromInstance = ev.getHandlers();
|
|
|
+ HandlerList fromStatic = McMMOModifyBlockDropItemEvent.getHandlerList();
|
|
|
+ assertNotNull(fromInstance);
|
|
|
+ assertNotNull(fromStatic);
|
|
|
+ // Bukkit convention: same static instance
|
|
|
+ assertSame(fromStatic, fromInstance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Nested
|
|
|
+ @DisplayName("Object contracts")
|
|
|
+ class ObjectContracts {
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void toStringContainsKeyFields() {
|
|
|
+ McMMOModifyBlockDropItemEvent ev = newEvent(2);
|
|
|
+ String s = ev.toString();
|
|
|
+ assertNotNull(s);
|
|
|
+ assertTrue(s.contains("originalBonusAmountToAdd=2"));
|
|
|
+ assertTrue(s.contains("modifiedItemStackQuantity=5"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void equalsAndHashCodeReflectState() {
|
|
|
+ // Same inputs => equal (mocks are same instances)
|
|
|
+ McMMOModifyBlockDropItemEvent a = newEvent(2);
|
|
|
+ McMMOModifyBlockDropItemEvent b = newEvent(2);
|
|
|
+ assertEquals(a, b);
|
|
|
+ assertEquals(a.hashCode(), b.hashCode());
|
|
|
+
|
|
|
+ // Change cancellation and modified quantity => not equal
|
|
|
+ McMMOModifyBlockDropItemEvent c = newEvent(2);
|
|
|
+ c.setCancelled(true);
|
|
|
+ assertNotEquals(a, c);
|
|
|
+
|
|
|
+ McMMOModifyBlockDropItemEvent d = newEvent(2);
|
|
|
+ d.setModifiedItemStackQuantity(99);
|
|
|
+ assertNotEquals(a, d);
|
|
|
+
|
|
|
+ // Different underlying mocks => not equal
|
|
|
+ BlockDropItemEvent otherEvent = mock(BlockDropItemEvent.class, RETURNS_DEEP_STUBS);
|
|
|
+ when(otherEvent.getPlayer()).thenReturn(player);
|
|
|
+ when(otherEvent.getBlock()).thenReturn(block);
|
|
|
+ when(otherEvent.getBlockState()).thenReturn(blockState);
|
|
|
+
|
|
|
+ ItemStack otherStack = mock(ItemStack.class);
|
|
|
+ when(otherStack.getAmount()).thenReturn(3);
|
|
|
+ Item otherItem = mock(Item.class);
|
|
|
+ when(otherItem.getItemStack()).thenReturn(otherStack);
|
|
|
+
|
|
|
+ McMMOModifyBlockDropItemEvent e = new McMMOModifyBlockDropItemEvent(otherEvent, otherItem, 2);
|
|
|
+ assertNotEquals(a, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|