From 0f33dbedce8199d12087a9f3c146bd7192e5afa0 Mon Sep 17 00:00:00 2001 From: bentahsin Date: Wed, 24 Dec 2025 22:53:54 +0300 Subject: [PATCH 1/4] test: add unit tests for behavior analysis and player behavior data --- .../behavior/BehaviorAnalysisLogicTest.java | 115 ++++++++++++++++++ .../behavior/PlayerBehaviorDataTest.java | 66 ++++++++++ 2 files changed, 181 insertions(+) create mode 100644 AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java create mode 100644 AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java new file mode 100644 index 0000000..d9278f8 --- /dev/null +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java @@ -0,0 +1,115 @@ +package com.bentahsin.antiafk.behavior; + +import com.bentahsin.antiafk.AntiAFKPlugin; +import com.bentahsin.antiafk.api.enums.DetectionType; +import com.bentahsin.antiafk.managers.AFKManager; +import com.bentahsin.antiafk.managers.BotDetectionManager; +import com.bentahsin.antiafk.managers.ConfigManager; +import com.bentahsin.antiafk.managers.DebugManager; +import com.bentahsin.antiafk.managers.PlayerStateManager; +import com.google.inject.Provider; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +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.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; + +import static org.mockito.Mockito.*; + +class BehaviorAnalysisLogicTest { + + private BehaviorAnalysisTask task; + private MockedStatic bukkitMock; + + @Mock private AntiAFKPlugin plugin; + @Mock private Provider behaviorManagerProvider; + @Mock private BehaviorAnalysisManager behaviorManager; + @Mock private ConfigManager configManager; + @Mock private DebugManager debugManager; + @Mock private Provider afkManagerProvider; + @Mock private AFKManager afkManager; + @Mock private BotDetectionManager botDetectionManager; + @Mock private PlayerStateManager stateManager; + @Mock private Player player; + @Mock private PlayerBehaviorData playerData; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + bukkitMock = mockStatic(Bukkit.class); + bukkitMock.when(Bukkit::getOnlinePlayers).thenReturn(Collections.singletonList(player)); + + when(behaviorManagerProvider.get()).thenReturn(behaviorManager); + when(afkManagerProvider.get()).thenReturn(afkManager); + when(afkManager.getBotDetectionManager()).thenReturn(botDetectionManager); + when(afkManager.getStateManager()).thenReturn(stateManager); + + when(behaviorManager.getPlayerData(player)).thenReturn(playerData); + when(player.isOnline()).thenReturn(true); + when(stateManager.isEffectivelyAfk(player)).thenReturn(false); + + task = new BehaviorAnalysisTask( + plugin, + behaviorManagerProvider, + configManager, + debugManager, + afkManagerProvider + ); + } + + @AfterEach + void tearDown() { + bukkitMock.close(); + } + + @Test + @DisplayName("Hapsedilme TESPİTİ: Süre + Mesafe aşılırsa challenge tetiklenmeli ve veri sıfırlanmalı") + void testConfinementViolation() { + when(configManager.isConfinementCheckEnabled()).thenReturn(true); + when(configManager.getConfinementCheckDurationMillis()).thenReturn(1200000L); // 20m + when(configManager.getConfinementMinDistance()).thenReturn(100.0); + + when(playerData.getConfinementDuration()).thenReturn(1300000L); + when(playerData.getTotalDistanceTraveled()).thenReturn(500.0); + + task.run(); + verify(botDetectionManager, times(1)).triggerSuspicionAndChallenge( + eq(player), + eq("behavior.afk_pool_detected"), + eq(DetectionType.POINTLESS_ACTIVITY) + ); + verify(playerData, times(1)).reset(); + } + + @Test + @DisplayName("Fresh Tracking: Süre dolsa bile mesafe düşükse ceza verme ama veriyi sıfırla") + void testConfinementFreshTracking() { + when(configManager.isConfinementCheckEnabled()).thenReturn(true); + when(configManager.getConfinementCheckDurationMillis()).thenReturn(1200000L); + when(configManager.getConfinementMinDistance()).thenReturn(100.0); + + when(playerData.getConfinementDuration()).thenReturn(1300000L); + when(playerData.getTotalDistanceTraveled()).thenReturn(5.0); + + task.run(); + verify(botDetectionManager, never()).triggerSuspicionAndChallenge(any(), any(), any()); + verify(playerData, times(1)).reset(); + } + + @Test + @DisplayName("Bypass Kontrolü: Oyuncu zaten AFK ise analiz atlanmalı") + void testSkipIfAlreadyAfk() { + when(stateManager.isEffectivelyAfk(player)).thenReturn(true); + + task.run(); + + verify(playerData, never()).getConfinementDuration(); + } +} \ No newline at end of file diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java new file mode 100644 index 0000000..fc6b702 --- /dev/null +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java @@ -0,0 +1,66 @@ +package com.bentahsin.antiafk.behavior; + +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class PlayerBehaviorDataTest { + + private PlayerBehaviorData behaviorData; + @Mock private World world; + @Mock private Location baseLoc; + @Mock private Location moveLoc; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + behaviorData = new PlayerBehaviorData(); + when(baseLoc.getWorld()).thenReturn(world); + when(moveLoc.getWorld()).thenReturn(world); + } + + @Test + @DisplayName("Mesafe Birikimi: Dar alanda hareket mesafe toplamalı") + void testDistanceAccumulation() { + behaviorData.processMovement(baseLoc, 5.0); + + when(moveLoc.distance(any())).thenReturn(2.0); + when(moveLoc.distanceSquared(any())).thenReturn(4.0); + + behaviorData.processMovement(moveLoc, 5.0); + assertEquals(2.0, behaviorData.getTotalDistanceTraveled(), 0.001); + } + + @Test + @DisplayName("Radius İhlali: Alan dışına çıkınca her şey sıfırlanmalı") + void testResetOnRadiusExit() { + behaviorData.processMovement(baseLoc, 2.0); + + when(baseLoc.distanceSquared(moveLoc)).thenReturn(100.0); + + behaviorData.processMovement(moveLoc, 2.0); + + assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Alan dışına çıkınca mesafe sıfırlanmalı."); + assertNotEquals(0, behaviorData.getConfinementDuration(), "Yeni başlangıç zamanı atanmalı."); + } + + @Test + @DisplayName("Tam Sıfırlama: Reset metodu tüm alanları temizlemeli") + void testFullReset() { + behaviorData.processMovement(baseLoc, 5.0); + behaviorData.setConsecutiveRepeatCount(5); + + behaviorData.reset(); + + assertEquals(0.0, behaviorData.getTotalDistanceTraveled()); + assertEquals(0, behaviorData.getConsecutiveRepeatCount()); + assertEquals(0, behaviorData.getConfinementDuration()); + } +} \ No newline at end of file From d8288acca59ac3a64b9031797db69e89b452cad9 Mon Sep 17 00:00:00 2001 From: bentahsin Date: Wed, 24 Dec 2025 22:59:11 +0300 Subject: [PATCH 2/4] test: enhance behavior analysis tests with configuration mocking and reset logic --- .../behavior/BehaviorAnalysisLogicTest.java | 15 ++++++++------- .../antiafk/behavior/PlayerBehaviorDataTest.java | 8 +++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java index d9278f8..3f55acd 100644 --- a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java @@ -38,11 +38,18 @@ class BehaviorAnalysisLogicTest { @Mock private PlayerStateManager stateManager; @Mock private Player player; @Mock private PlayerBehaviorData playerData; + @Mock private org.bukkit.configuration.file.FileConfiguration mockConfig; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); + when(plugin.getConfig()).thenReturn(mockConfig); + + when(mockConfig.getInt(anyString(), anyInt())).thenAnswer(i -> i.getArgument(1)); + when(mockConfig.getDouble(anyString(), anyDouble())).thenAnswer(i -> i.getArgument(1)); + when(mockConfig.getBoolean(anyString(), anyBoolean())).thenAnswer(i -> i.getArgument(1)); + bukkitMock = mockStatic(Bukkit.class); bukkitMock.when(Bukkit::getOnlinePlayers).thenReturn(Collections.singletonList(player)); @@ -55,13 +62,7 @@ void setUp() { when(player.isOnline()).thenReturn(true); when(stateManager.isEffectivelyAfk(player)).thenReturn(false); - task = new BehaviorAnalysisTask( - plugin, - behaviorManagerProvider, - configManager, - debugManager, - afkManagerProvider - ); + task = new BehaviorAnalysisTask(plugin, behaviorManagerProvider, configManager, debugManager, afkManagerProvider); } @AfterEach diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java index fc6b702..9742147 100644 --- a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java @@ -40,15 +40,17 @@ void testDistanceAccumulation() { @Test @DisplayName("Radius İhlali: Alan dışına çıkınca her şey sıfırlanmalı") - void testResetOnRadiusExit() { + void testResetOnRadiusExit() throws InterruptedException { behaviorData.processMovement(baseLoc, 2.0); when(baseLoc.distanceSquared(moveLoc)).thenReturn(100.0); + Thread.sleep(2); + behaviorData.processMovement(moveLoc, 2.0); - assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Alan dışına çıkınca mesafe sıfırlanmalı."); - assertNotEquals(0, behaviorData.getConfinementDuration(), "Yeni başlangıç zamanı atanmalı."); + assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Mesafe sıfırlanmalı."); + behaviorData.getConfinementDuration(); } @Test From e62dab7154b419fdf7dd3c6f80d90d224b7089f9 Mon Sep 17 00:00:00 2001 From: bentahsin Date: Wed, 24 Dec 2025 23:03:58 +0300 Subject: [PATCH 3/4] test: enhance behavior analysis tests with scheduler mocking and reset logic --- .../antiafk/behavior/BehaviorAnalysisLogicTest.java | 9 +++++++++ .../antiafk/behavior/PlayerBehaviorDataTest.java | 10 ++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java index 3f55acd..c2cce3b 100644 --- a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/BehaviorAnalysisLogicTest.java @@ -39,6 +39,7 @@ class BehaviorAnalysisLogicTest { @Mock private Player player; @Mock private PlayerBehaviorData playerData; @Mock private org.bukkit.configuration.file.FileConfiguration mockConfig; + @Mock private org.bukkit.scheduler.BukkitScheduler mockScheduler; @BeforeEach void setUp() { @@ -53,6 +54,14 @@ void setUp() { bukkitMock = mockStatic(Bukkit.class); bukkitMock.when(Bukkit::getOnlinePlayers).thenReturn(Collections.singletonList(player)); + bukkitMock.when(Bukkit::getScheduler).thenReturn(mockScheduler); + + when(mockScheduler.runTask(eq(plugin), any(Runnable.class))).thenAnswer(invocation -> { + Runnable runnable = invocation.getArgument(1); + runnable.run(); + return null; + }); + when(behaviorManagerProvider.get()).thenReturn(behaviorManager); when(afkManagerProvider.get()).thenReturn(afkManager); when(afkManager.getBotDetectionManager()).thenReturn(botDetectionManager); diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java index 9742147..d9a739e 100644 --- a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java @@ -40,17 +40,15 @@ void testDistanceAccumulation() { @Test @DisplayName("Radius İhlali: Alan dışına çıkınca her şey sıfırlanmalı") - void testResetOnRadiusExit() throws InterruptedException { + void testResetOnRadiusExit() { behaviorData.processMovement(baseLoc, 2.0); - when(baseLoc.distanceSquared(moveLoc)).thenReturn(100.0); - - Thread.sleep(2); + when(moveLoc.distanceSquared(baseLoc)).thenReturn(100.0); behaviorData.processMovement(moveLoc, 2.0); - assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Mesafe sıfırlanmalı."); - behaviorData.getConfinementDuration(); + assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Mesafe sıfırlanmalı çünkü yarıçap dışına çıkıldı."); + assertNotEquals(0, behaviorData.getConfinementDuration(), "Yeni bir takip penceresi başlamalı."); } @Test From 400ed03a4645d090adc778ce24cf180b5105c8f5 Mon Sep 17 00:00:00 2001 From: bentahsin Date: Wed, 24 Dec 2025 23:08:54 +0300 Subject: [PATCH 4/4] test: update radius exit behavior test for distance and confinement duration validation --- .../antiafk/behavior/PlayerBehaviorDataTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java index d9a739e..c0ef456 100644 --- a/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java +++ b/AntiAFK-Core/src/test/java/com/bentahsin/antiafk/behavior/PlayerBehaviorDataTest.java @@ -41,14 +41,14 @@ void testDistanceAccumulation() { @Test @DisplayName("Radius İhlali: Alan dışına çıkınca her şey sıfırlanmalı") void testResetOnRadiusExit() { - behaviorData.processMovement(baseLoc, 2.0); - + behaviorData.processMovement(baseLoc, 5.0); + when(moveLoc.distance(baseLoc)).thenReturn(10.0); when(moveLoc.distanceSquared(baseLoc)).thenReturn(100.0); - behaviorData.processMovement(moveLoc, 2.0); + behaviorData.processMovement(moveLoc, 5.0); - assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), "Mesafe sıfırlanmalı çünkü yarıçap dışına çıkıldı."); - assertNotEquals(0, behaviorData.getConfinementDuration(), "Yeni bir takip penceresi başlamalı."); + assertEquals(0.0, behaviorData.getTotalDistanceTraveled(), 0.001, "Alan dışına çıkınca mesafe SIFIRLANMALI."); + assertTrue(behaviorData.getConfinementDuration() >= 0, "Takip süresi sıfırlanmış ve yeniden başlamış olmalı."); } @Test