From 9d2ed0317e5c1d3246c465ca570ed9e5ca243f02 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 09:31:03 +0000 Subject: [PATCH] [Unit Tests] Expand coverage for RageSpike, scope providers, and RewardDistributionGranter - RageSpike: Add tests for getMaxTier, getMaxVerticalVelocity, getDatabaseName, getSkillKey, route accessors, tier-specific config override paths for getDamage/getVelocity, and getYamlDocument - SinglePlayerQuestScopeProvider: Add JDBC-mocked tests for resolveActiveQuestUUIDs and loadScope(Connection, UUID) - PermissionQuestScopeProvider: Add JDBC-mocked tests for resolveActiveQuestUUIDs with online player permission checks, partial permission filtering, empty results, and loadScope - RewardDistributionGranter: Add offline player path tests verifying no immediate grant occurs and mixed online/offline behavior; centralize TrackingRewardType.GRANTED_TO cleanup in @BeforeEach Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_011EnkL9KPHk6ogbvPdv4ySc --- .../ability/impl/swords/RageSpikeTest.java | 216 ++++++++++--- .../RewardDistributionGranterTest.java | 192 +++++++---- .../PermissionQuestScopeProviderTest.java | 302 +++++++++++++++--- .../SinglePlayerQuestScopeProviderTest.java | 195 ++++++++--- 4 files changed, 717 insertions(+), 188 deletions(-) diff --git a/src/test/java/us/eunoians/mcrpg/ability/impl/swords/RageSpikeTest.java b/src/test/java/us/eunoians/mcrpg/ability/impl/swords/RageSpikeTest.java index d8cc53570..4c57c1037 100644 --- a/src/test/java/us/eunoians/mcrpg/ability/impl/swords/RageSpikeTest.java +++ b/src/test/java/us/eunoians/mcrpg/ability/impl/swords/RageSpikeTest.java @@ -6,23 +6,25 @@ import dev.dejvokep.boostedyaml.route.Route; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import us.eunoians.mcrpg.McRPGBaseTest; import us.eunoians.mcrpg.configuration.FileManager; import us.eunoians.mcrpg.configuration.FileType; +import us.eunoians.mcrpg.configuration.file.localization.LocalizationKey; import us.eunoians.mcrpg.configuration.file.skill.SwordsConfigFile; import us.eunoians.mcrpg.registry.manager.McRPGManagerKey; +import us.eunoians.mcrpg.skill.impl.swords.Swords; +import us.eunoians.mcrpg.util.McRPGMethods; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Tests for {@link RageSpike} tier-dependent configuration value resolution. - *

- * Verifies that {@code getDamage} and {@code getVelocity} correctly evaluate formulas - * through the {@link com.diamonddagger590.mccore.parser.Parser} and handle plain - * integer literals. + * Tests for {@link RageSpike} tier-dependent configuration value resolution, + * simple accessors, and route/key identity methods. */ class RageSpikeTest extends McRPGBaseTest { @@ -40,50 +42,184 @@ void setUp() { rageSpike = new RageSpike(mcRPG); } - @Test - @DisplayName("Given a damage formula, when getDamage is called with a tier, then the formula is evaluated") - void getDamage_evaluatesFormulaWithTier() { - int tier = 4; - Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); - Route damageAllTiers = Route.addTo(allTiersRoute, "damage"); - Route tierRoute = Route.addTo( - Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "damage"); + @Nested + @DisplayName("getDamage") + class GetDamage { - when(swordsConfig.contains(tierRoute)).thenReturn(false); - when(swordsConfig.getString(damageAllTiers)).thenReturn("2*tier"); + @Test + @DisplayName("Given a damage formula in all-tiers, when getDamage is called, then the formula is evaluated with the tier variable") + void getDamage_evaluatesFormulaWithTier() { + int tier = 4; + Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); + Route damageAllTiers = Route.addTo(allTiersRoute, "damage"); + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "damage"); - // tier=4 → 2*4 = 8.0 - assertEquals(8.0, rageSpike.getDamage(tier), 0.001); + when(swordsConfig.contains(tierRoute)).thenReturn(false); + when(swordsConfig.getString(damageAllTiers)).thenReturn("2*tier"); + + assertEquals(8.0, rageSpike.getDamage(tier), 0.001); + } + + @Test + @DisplayName("Given a plain integer literal in YAML, when getDamage is called, then it returns the literal value") + void getDamage_returnsLiteralValue_whenGivenPlainInteger() { + int tier = 1; + Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); + Route damageAllTiers = Route.addTo(allTiersRoute, "damage"); + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "damage"); + + when(swordsConfig.contains(tierRoute)).thenReturn(false); + when(swordsConfig.getString(damageAllTiers)).thenReturn("5"); + + assertEquals(5.0, rageSpike.getDamage(tier), 0.001); + } + + @Test + @DisplayName("Given a tier-specific override, when getDamage is called, then the tier-specific value is used") + void getDamage_usesTierSpecificOverride_whenPresent() { + int tier = 3; + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "damage"); + + when(swordsConfig.contains(tierRoute)).thenReturn(true); + when(swordsConfig.getString(tierRoute)).thenReturn("12"); + + assertEquals(12.0, rageSpike.getDamage(tier), 0.001); + } } - @Test - @DisplayName("Given a plain integer literal in YAML, when getDamage is called, then it returns the literal value") - void getDamage_returnsLiteralValue_whenGivenPlainInteger() { - int tier = 1; - Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); - Route damageAllTiers = Route.addTo(allTiersRoute, "damage"); - Route tierRoute = Route.addTo( - Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "damage"); + @Nested + @DisplayName("getVelocity") + class GetVelocity { + + @Test + @DisplayName("Given a velocity formula in all-tiers, when getVelocity is called, then the formula is evaluated") + void getVelocity_evaluatesFormulaWithTier() { + int tier = 4; + Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); + Route velocityAllTiers = Route.addTo(allTiersRoute, "velocity"); + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "velocity"); - when(swordsConfig.contains(tierRoute)).thenReturn(false); - when(swordsConfig.getString(damageAllTiers)).thenReturn("5"); + when(swordsConfig.contains(tierRoute)).thenReturn(false); + when(swordsConfig.getString(velocityAllTiers, "5")).thenReturn("tier+2"); - assertEquals(5.0, rageSpike.getDamage(tier), 0.001); + // tier=4 → 4+2 = 6, distinct from the default of 5 + assertEquals(6, rageSpike.getVelocity(tier)); + } + + @Test + @DisplayName("Given a tier-specific override, when getVelocity is called, then the override is used") + void getVelocity_usesTierSpecificOverride_whenPresent() { + int tier = 2; + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "velocity"); + + when(swordsConfig.contains(tierRoute)).thenReturn(true); + when(swordsConfig.getString(tierRoute)).thenReturn("10"); + + assertEquals(10, rageSpike.getVelocity(tier)); + } + + @Test + @DisplayName("Given no config value, when getVelocity is called, then the default of 5 is used") + void getVelocity_usesDefault_whenNoConfigValue() { + int tier = 1; + Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); + Route velocityAllTiers = Route.addTo(allTiersRoute, "velocity"); + Route tierRoute = Route.addTo( + Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "velocity"); + + when(swordsConfig.contains(tierRoute)).thenReturn(false); + when(swordsConfig.getString(velocityAllTiers, "5")).thenReturn("5"); + + assertEquals(5, rageSpike.getVelocity(tier)); + } } - @Test - @DisplayName("Given a velocity formula, when getVelocity is called with a tier, then the formula is evaluated") - void getVelocity_evaluatesFormulaWithTier() { - int tier = 3; - Route allTiersRoute = Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "all-tiers"); - Route velocityAllTiers = Route.addTo(allTiersRoute, "velocity"); - Route tierRoute = Route.addTo( - Route.addTo(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, "tier-" + tier), "velocity"); + @Nested + @DisplayName("getMaxVerticalVelocity") + class GetMaxVerticalVelocity { + + @Test + @DisplayName("Given a configured max vertical velocity, when getMaxVerticalVelocity is called, then it returns the configured value") + void getMaxVerticalVelocity_returnsConfiguredValue() { + when(swordsConfig.getDouble(SwordsConfigFile.RAGE_SPIKE_MAX_VERTICAL_VELOCITY, 0.3)).thenReturn(0.5); + + assertEquals(0.5, rageSpike.getMaxVerticalVelocity(), 0.001); + } + + @Test + @DisplayName("Given no config value, when getMaxVerticalVelocity is called, then the default 0.3 is returned") + void getMaxVerticalVelocity_returnsDefault_whenNotConfigured() { + when(swordsConfig.getDouble(SwordsConfigFile.RAGE_SPIKE_MAX_VERTICAL_VELOCITY, 0.3)).thenReturn(0.3); + + assertEquals(0.3, rageSpike.getMaxVerticalVelocity(), 0.001); + } + } + + @Nested + @DisplayName("getMaxTier") + class GetMaxTier { + + @Test + @DisplayName("Given a configured amount of tiers, when getMaxTier is called, then the configured value is returned") + void getMaxTier_returnsConfiguredValue() { + when(swordsConfig.getInt(SwordsConfigFile.RAGE_SPIKE_AMOUNT_OF_TIERS)).thenReturn(5); + + assertEquals(5, rageSpike.getMaxTier()); + } + } + + @Nested + @DisplayName("Identity and routes") + class IdentityAndRoutes { + + @Test + @DisplayName("getAbilityKey returns rage_spike key under mcrpg namespace") + void getAbilityKey_returnsRageSpikeKey() { + assertEquals(RageSpike.RAGE_SPIKE_KEY, rageSpike.getAbilityKey()); + assertEquals("mcrpg", rageSpike.getAbilityKey().getNamespace()); + assertEquals("rage_spike", rageSpike.getAbilityKey().getKey()); + } + + @Test + @DisplayName("getDatabaseName returns rage_spike") + void getDatabaseName_returnsRageSpike() { + assertEquals("rage_spike", rageSpike.getDatabaseName()); + } + + @Test + @DisplayName("getSkillKey returns Swords key") + void getSkillKey_returnsSwordsKey() { + assertEquals(Swords.SWORDS_KEY, rageSpike.getSkillKey()); + } + + @Test + @DisplayName("getAbilityEnabledRoute returns the rage spike enabled route") + void getAbilityEnabledRoute_returnsRageSpikeEnabledRoute() { + assertEquals(SwordsConfigFile.RAGE_SPIKE_ENABLED, rageSpike.getAbilityEnabledRoute()); + } + + @Test + @DisplayName("getDisplayItemRoute returns the rage spike display item header route") + void getDisplayItemRoute_returnsRageSpikeDisplayItemRoute() { + assertEquals(LocalizationKey.RAGE_SPIKE_DISPLAY_ITEM_HEADER, rageSpike.getDisplayItemRoute()); + } - when(swordsConfig.contains(tierRoute)).thenReturn(false); - when(swordsConfig.getString(velocityAllTiers, "5")).thenReturn("tier+2"); + @Test + @DisplayName("getAbilityTierConfigurationRoute returns the rage spike tier config header") + void getAbilityTierConfigurationRoute_returnsRageSpikeTierConfigHeader() { + assertEquals(SwordsConfigFile.RAGE_SPIKE_CONFIGURATION_HEADER, rageSpike.getAbilityTierConfigurationRoute()); + } - // tier=3 → 3+2 = 5 - assertEquals(5, rageSpike.getVelocity(tier)); + @Test + @DisplayName("getYamlDocument returns the swords config from FileManager") + void getYamlDocument_returnsSwordsConfig() { + assertNotNull(rageSpike.getYamlDocument()); + assertEquals(swordsConfig, rageSpike.getYamlDocument()); + } } } diff --git a/src/test/java/us/eunoians/mcrpg/quest/board/distribution/RewardDistributionGranterTest.java b/src/test/java/us/eunoians/mcrpg/quest/board/distribution/RewardDistributionGranterTest.java index 0eb2acaa5..70442aec7 100644 --- a/src/test/java/us/eunoians/mcrpg/quest/board/distribution/RewardDistributionGranterTest.java +++ b/src/test/java/us/eunoians/mcrpg/quest/board/distribution/RewardDistributionGranterTest.java @@ -1,13 +1,23 @@ package us.eunoians.mcrpg.quest.board.distribution; +import com.diamonddagger590.mccore.database.Database; +import com.diamonddagger590.mccore.registry.RegistryAccess; +import com.diamonddagger590.mccore.registry.RegistryKey; +import dev.dejvokep.boostedyaml.YamlDocument; import dev.dejvokep.boostedyaml.block.implementation.Section; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockbukkit.mockbukkit.entity.PlayerMock; import us.eunoians.mcrpg.McRPGBaseTest; +import us.eunoians.mcrpg.configuration.FileManager; +import us.eunoians.mcrpg.configuration.FileType; +import us.eunoians.mcrpg.configuration.file.MainConfigFile; +import us.eunoians.mcrpg.database.McRPGDatabaseManager; import us.eunoians.mcrpg.quest.reward.QuestRewardType; +import us.eunoians.mcrpg.registry.manager.McRPGManagerKey; import us.eunoians.mcrpg.util.McRPGMethods; import java.util.ArrayList; @@ -15,11 +25,17 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -public class RewardDistributionGranterTest extends McRPGBaseTest { +class RewardDistributionGranterTest extends McRPGBaseTest { private static final NamespacedKey QUEST_KEY = new NamespacedKey(McRPGMethods.getMcRPGNamespace(), "test_quest"); @@ -27,6 +43,7 @@ public class RewardDistributionGranterTest extends McRPGBaseTest { @org.junit.jupiter.api.BeforeEach void setUp() { + TrackingRewardType.GRANTED_TO.clear(); granter = new RewardDistributionGranter(mcRPG); } @@ -77,81 +94,142 @@ public void grant(@org.jetbrains.annotations.NotNull Player player) { } } - @DisplayName("Online player receives reward immediately via grant()") - @Test - void onlinePlayer_rewardGrantedImmediately() { - TrackingRewardType.GRANTED_TO.clear(); - PlayerMock player = server.addPlayer(); - UUID playerUUID = player.getUniqueId(); + @Nested + @DisplayName("Online player grants") + class OnlinePlayerGrants { - var reward = new TrackingRewardType(100); - Map> resolved = Map.of(playerUUID, List.of(reward)); + @DisplayName("Online player receives reward immediately via grant()") + @Test + void onlinePlayer_rewardGrantedImmediately() { + PlayerMock player = server.addPlayer(); + UUID playerUUID = player.getUniqueId(); - granter.grant(resolved, QUEST_KEY); + var reward = new TrackingRewardType(100); + Map> resolved = Map.of(playerUUID, List.of(reward)); - assertEquals(1, TrackingRewardType.GRANTED_TO.size()); - assertEquals(playerUUID, TrackingRewardType.GRANTED_TO.get(0)); - } + granter.grant(resolved, QUEST_KEY); - @DisplayName("Empty rewards map results in no-op") - @Test - void emptyRewardsMap_noOp() { - TrackingRewardType.GRANTED_TO.clear(); - Map> resolved = Map.of(); + assertEquals(1, TrackingRewardType.GRANTED_TO.size()); + assertEquals(playerUUID, TrackingRewardType.GRANTED_TO.get(0)); + } - granter.grant(resolved, QUEST_KEY); + @DisplayName("Multiple rewards for one player are all granted") + @Test + void multipleRewards_allGranted() { + PlayerMock player = server.addPlayer(); + UUID playerUUID = player.getUniqueId(); - assertTrue(TrackingRewardType.GRANTED_TO.isEmpty()); - } + var r1 = new TrackingRewardType(100); + var r2 = new TrackingRewardType(200); + var r3 = new TrackingRewardType(300); + Map> resolved = Map.of(playerUUID, List.of(r1, r2, r3)); - @DisplayName("Player with empty reward list results in no grant call") - @Test - void playerWithEmptyRewardList_noGrant() { - TrackingRewardType.GRANTED_TO.clear(); - PlayerMock player = server.addPlayer(); + granter.grant(resolved, QUEST_KEY); - Map> resolved = Map.of(player.getUniqueId(), List.of()); + assertEquals(3, TrackingRewardType.GRANTED_TO.size()); + assertTrue(TrackingRewardType.GRANTED_TO.stream().allMatch(uuid -> uuid.equals(playerUUID))); + } + + @DisplayName("Multiple online players each receive their own rewards") + @Test + void multipleOnlinePlayers_eachReceivesOwn() { + PlayerMock player1 = server.addPlayer(); + PlayerMock player2 = server.addPlayer(); - granter.grant(resolved, QUEST_KEY); + var reward = new TrackingRewardType(500); + Map> resolved = Map.of( + player1.getUniqueId(), List.of(reward), + player2.getUniqueId(), List.of(reward) + ); - assertTrue(TrackingRewardType.GRANTED_TO.isEmpty()); + granter.grant(resolved, QUEST_KEY); + + assertEquals(2, TrackingRewardType.GRANTED_TO.size()); + assertTrue(TrackingRewardType.GRANTED_TO.contains(player1.getUniqueId())); + assertTrue(TrackingRewardType.GRANTED_TO.contains(player2.getUniqueId())); + } } - @DisplayName("Multiple rewards for one player are all granted") - @Test - void multipleRewards_allGranted() { - TrackingRewardType.GRANTED_TO.clear(); - PlayerMock player = server.addPlayer(); - UUID playerUUID = player.getUniqueId(); + @Nested + @DisplayName("Edge cases") + class EdgeCases { + + @DisplayName("Empty rewards map results in no-op") + @Test + void emptyRewardsMap_noOp() { + Map> resolved = Map.of(); - var r1 = new TrackingRewardType(100); - var r2 = new TrackingRewardType(200); - var r3 = new TrackingRewardType(300); - Map> resolved = Map.of(playerUUID, List.of(r1, r2, r3)); + granter.grant(resolved, QUEST_KEY); - granter.grant(resolved, QUEST_KEY); + assertTrue(TrackingRewardType.GRANTED_TO.isEmpty()); + } - assertEquals(3, TrackingRewardType.GRANTED_TO.size()); - assertTrue(TrackingRewardType.GRANTED_TO.stream().allMatch(uuid -> uuid.equals(playerUUID))); + @DisplayName("Player with empty reward list results in no grant call") + @Test + void playerWithEmptyRewardList_noGrant() { + PlayerMock player = server.addPlayer(); + + Map> resolved = Map.of(player.getUniqueId(), List.of()); + + granter.grant(resolved, QUEST_KEY); + + assertTrue(TrackingRewardType.GRANTED_TO.isEmpty()); + } } - @DisplayName("Multiple online players each receive their own rewards") - @Test - void multipleOnlinePlayers_eachReceivesOwn() { - TrackingRewardType.GRANTED_TO.clear(); - PlayerMock player1 = server.addPlayer(); - PlayerMock player2 = server.addPlayer(); + @Nested + @DisplayName("Offline player path") + class OfflinePlayerPath { + + private void registerMockDatabaseManager() { + FileManager fileManager = RegistryAccess.registryAccess() + .registry(RegistryKey.MANAGER) + .manager(McRPGManagerKey.FILE); + YamlDocument mainConfig = mock(YamlDocument.class); + when(fileManager.getFile(FileType.MAIN_CONFIG)).thenReturn(mainConfig); + when(mainConfig.getInt(MainConfigFile.QUEST_PENDING_REWARDS_EXPIRY_DAYS, 30)).thenReturn(30); + + Database mockDatabase = mock(Database.class); + ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); + when(mockDatabase.getDatabaseExecutorService()).thenReturn(executor); + + McRPGDatabaseManager mockDbManager = mock(McRPGDatabaseManager.class); + when(mockDbManager.getDatabase()).thenReturn(mockDatabase); + RegistryAccess.registryAccess().registry(RegistryKey.MANAGER).register(mockDbManager); + } + + @Test + @DisplayName("Given an offline player UUID, when grant is called, then no immediate reward grant occurs") + void offlinePlayer_noImmediateGrant() { + registerMockDatabaseManager(); + UUID offlinePlayerUUID = UUID.randomUUID(); - var reward = new TrackingRewardType(500); - Map> resolved = Map.of( - player1.getUniqueId(), List.of(reward), - player2.getUniqueId(), List.of(reward) - ); + var reward = new TrackingRewardType(100); + Map> resolved = Map.of(offlinePlayerUUID, List.of(reward)); - granter.grant(resolved, QUEST_KEY); + granter.grant(resolved, QUEST_KEY); - assertEquals(2, TrackingRewardType.GRANTED_TO.size()); - assertTrue(TrackingRewardType.GRANTED_TO.contains(player1.getUniqueId())); - assertTrue(TrackingRewardType.GRANTED_TO.contains(player2.getUniqueId())); + assertTrue(TrackingRewardType.GRANTED_TO.isEmpty(), + "Offline player should not receive an immediate grant"); + } + + @Test + @DisplayName("Given a mix of online and offline players, when grant is called, then only the online player receives immediate grants") + void mixedOnlineOffline_onlyOnlineReceivesImmediateGrant() { + registerMockDatabaseManager(); + PlayerMock onlinePlayer = server.addPlayer(); + UUID offlinePlayerUUID = UUID.randomUUID(); + + var reward = new TrackingRewardType(100); + Map> resolved = Map.of( + onlinePlayer.getUniqueId(), List.of(reward), + offlinePlayerUUID, List.of(reward) + ); + + granter.grant(resolved, QUEST_KEY); + + assertEquals(1, TrackingRewardType.GRANTED_TO.size()); + assertEquals(onlinePlayer.getUniqueId(), TrackingRewardType.GRANTED_TO.get(0)); + } } } diff --git a/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/PermissionQuestScopeProviderTest.java b/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/PermissionQuestScopeProviderTest.java index 53f1294a2..4f3c372cb 100644 --- a/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/PermissionQuestScopeProviderTest.java +++ b/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/PermissionQuestScopeProviderTest.java @@ -1,87 +1,283 @@ package us.eunoians.mcrpg.quest.impl.scope.impl; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.entity.PlayerMock; import us.eunoians.mcrpg.McRPGBaseTest; import us.eunoians.mcrpg.expansion.McRPGExpansion; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class PermissionQuestScopeProviderTest extends McRPGBaseTest { - @DisplayName("getKey returns permission_scope key") - @Test - void getKey_returnsPermissionScopeKey() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - assertEquals(PermissionQuestScope.PERMISSION_SCOPE_KEY, provider.getKey()); - assertEquals("mcrpg:permission_scope", provider.getKey().toString()); - } + @Nested + @DisplayName("Identity") + class Identity { + + @DisplayName("getKey returns permission_scope key") + @Test + void getKey_returnsPermissionScopeKey() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + assertEquals(PermissionQuestScope.PERMISSION_SCOPE_KEY, provider.getKey()); + assertEquals("mcrpg:permission_scope", provider.getKey().toString()); + } - @DisplayName("getExpansionKey returns McRPG expansion key") - @Test - void getExpansionKey_returnsMcRPGExpansionKey() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - assertTrue(provider.getExpansionKey().isPresent()); - assertEquals(McRPGExpansion.EXPANSION_KEY, provider.getExpansionKey().orElseThrow()); + @DisplayName("getExpansionKey returns McRPG expansion key") + @Test + void getExpansionKey_returnsMcRPGExpansionKey() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + assertTrue(provider.getExpansionKey().isPresent()); + assertEquals(McRPGExpansion.EXPANSION_KEY, provider.getExpansionKey().orElseThrow()); + } } - @DisplayName("withPermissionNode returns this for chaining") - @Test - void withPermissionNode_returnsThisForChaining() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - PermissionQuestScopeProvider result = provider.withPermissionNode("mcrpg.quest.vip"); - assertEquals(provider, result); + @Nested + @DisplayName("withPermissionNode") + class WithPermissionNode { + + @DisplayName("withPermissionNode returns this for chaining") + @Test + void withPermissionNode_returnsThisForChaining() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + PermissionQuestScopeProvider result = provider.withPermissionNode("mcrpg.quest.vip"); + assertEquals(provider, result); + } } - @DisplayName("createNewScope returns scope with correct quest UUID and permission") - @Test - void createNewScope_returnsScope_withCorrectFields() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - provider.withPermissionNode("mcrpg.quest.test"); - UUID questUUID = UUID.randomUUID(); + @Nested + @DisplayName("createNewScope") + class CreateNewScope { - PermissionQuestScope scope = provider.createNewScope(questUUID); + @DisplayName("createNewScope returns scope with correct quest UUID and permission") + @Test + void createNewScope_returnsScope_withCorrectFields() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + provider.withPermissionNode("mcrpg.quest.test"); + UUID questUUID = UUID.randomUUID(); - assertNotNull(scope); - assertEquals(questUUID, scope.getQuestUUID()); - assertEquals("mcrpg.quest.test", scope.getPermissionNode().orElseThrow()); - } + PermissionQuestScope scope = provider.createNewScope(questUUID); + + assertNotNull(scope); + assertEquals(questUUID, scope.getQuestUUID()); + assertEquals("mcrpg.quest.test", scope.getPermissionNode().orElseThrow()); + } + + @DisplayName("createNewScope throws when permission not set") + @Test + void createNewScope_throwsIllegalStateException_whenPermissionNotSet() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + assertThrows(IllegalStateException.class, () -> provider.createNewScope(UUID.randomUUID())); + } + + @DisplayName("createNewScope returns valid scope") + @Test + void createNewScope_returnsValidScope() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + provider.withPermissionNode("mcrpg.quest.test"); + + PermissionQuestScope scope = provider.createNewScope(UUID.randomUUID()); + + assertTrue(scope.isScopeValid()); + } + + @DisplayName("Each createNewScope call returns a distinct scope") + @Test + void createNewScope_returnsDistinctScopes() { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + provider.withPermissionNode("mcrpg.quest.test"); + UUID questUUID1 = UUID.randomUUID(); + UUID questUUID2 = UUID.randomUUID(); + + PermissionQuestScope scope1 = provider.createNewScope(questUUID1); + PermissionQuestScope scope2 = provider.createNewScope(questUUID2); - @DisplayName("createNewScope throws when permission not set") - @Test - void createNewScope_throwsIllegalStateException_whenPermissionNotSet() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - assertThrows(IllegalStateException.class, () -> provider.createNewScope(UUID.randomUUID())); + assertEquals(questUUID1, scope1.getQuestUUID()); + assertEquals(questUUID2, scope2.getQuestUUID()); + } } - @DisplayName("createNewScope returns valid scope") - @Test - void createNewScope_returnsValidScope() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - provider.withPermissionNode("mcrpg.quest.test"); + @Nested + @DisplayName("resolveActiveQuestUUIDs") + class ResolveActiveQuestUUIDs { + + @Test + @DisplayName("Given active permission quests and an online player with matching permission, when resolveActiveQuestUUIDs is called, then matching quest UUIDs are returned") + void resolveActiveQuestUUIDs_returnsMatching_whenOnlinePlayerHasPermission() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + PlayerMock playerMock = server.addPlayer(); + UUID playerUUID = playerMock.getUniqueId(); + UUID questUUID = UUID.randomUUID(); + String permNode = "mcrpg.quest.vip"; + + playerMock.addAttachment(mcRPG, permNode, true); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true, false); + when(rs.getString("quest_uuid")).thenReturn(questUUID.toString()); + when(rs.getString("permission_node")).thenReturn(permNode); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); + + assertEquals(1, result.size()); + assertEquals(questUUID, result.get(0)); + } + + @Test + @DisplayName("Given active permission quests and an online player without the permission, when resolveActiveQuestUUIDs is called, then empty list is returned") + void resolveActiveQuestUUIDs_returnsEmpty_whenOnlinePlayerLacksPermission() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + PlayerMock playerMock = server.addPlayer(); + UUID playerUUID = playerMock.getUniqueId(); + UUID questUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true, false); + when(rs.getString("quest_uuid")).thenReturn(questUUID.toString()); + when(rs.getString("permission_node")).thenReturn("mcrpg.quest.vip"); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); + + assertTrue(result.isEmpty()); + } - PermissionQuestScope scope = provider.createNewScope(UUID.randomUUID()); + @Test + @DisplayName("Given no active permission quests in the database, when resolveActiveQuestUUIDs is called, then empty list is returned") + void resolveActiveQuestUUIDs_returnsEmpty_whenNoActiveQuests() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + UUID playerUUID = UUID.randomUUID(); - assertTrue(scope.isScopeValid()); + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(false); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); + + assertTrue(result.isEmpty()); + } + + @Test + @DisplayName("Given multiple active quests, when player has permission for only one, then only that quest UUID is returned") + void resolveActiveQuestUUIDs_filtersCorrectly_whenPlayerHasPartialPermissions() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + PlayerMock playerMock = server.addPlayer(); + UUID playerUUID = playerMock.getUniqueId(); + UUID questWithPerm = UUID.randomUUID(); + UUID questWithoutPerm = UUID.randomUUID(); + + playerMock.addAttachment(mcRPG, "mcrpg.quest.vip", true); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("quest_uuid")).thenReturn(questWithPerm.toString(), questWithoutPerm.toString()); + when(rs.getString("permission_node")).thenReturn("mcrpg.quest.vip", "mcrpg.quest.admin"); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); + + assertEquals(1, result.size()); + assertEquals(questWithPerm, result.get(0)); + } } - @DisplayName("Each createNewScope call returns a distinct scope") - @Test - void createNewScope_returnsDistinctScopes() { - PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); - provider.withPermissionNode("mcrpg.quest.test"); - UUID questUUID1 = UUID.randomUUID(); - UUID questUUID2 = UUID.randomUUID(); + @Nested + @DisplayName("loadScope(Connection, UUID)") + class LoadScope { + + @Test + @DisplayName("Given a stored permission scope, when loadScope is called, then the scope has the correct permission node") + void loadScope_returnsScopeWithPermission_whenStoredInDatabase() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + String permNode = "mcrpg.quest.vip"; + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true); + when(rs.getString("permission_node")).thenReturn(permNode); + + PermissionQuestScope scope = provider.loadScope(connection, questUUID); + + assertNotNull(scope); + assertEquals(questUUID, scope.getQuestUUID()); + assertTrue(scope.getPermissionNode().isPresent()); + assertEquals(permNode, scope.getPermissionNode().orElseThrow()); + } + + @Test + @DisplayName("Given a stored permission scope, when loadScope is called, then the quest UUID is passed to the DAO") + void loadScope_passesQuestUUIDToDAO() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true); + when(rs.getString("permission_node")).thenReturn("mcrpg.quest.test"); + + provider.loadScope(connection, questUUID); + + verify(ps).setString(1, questUUID.toString()); + } + + @Test + @DisplayName("Given a loaded scope, when isScopeValid is called, then it returns true") + void loadScope_returnsValidScope() throws SQLException { + PermissionQuestScopeProvider provider = new PermissionQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true); + when(rs.getString("permission_node")).thenReturn("mcrpg.quest.test"); - PermissionQuestScope scope1 = provider.createNewScope(questUUID1); - PermissionQuestScope scope2 = provider.createNewScope(questUUID2); + PermissionQuestScope scope = provider.loadScope(connection, questUUID); - assertEquals(questUUID1, scope1.getQuestUUID()); - assertEquals(questUUID2, scope2.getQuestUUID()); + assertTrue(scope.isScopeValid()); + } } } diff --git a/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/SinglePlayerQuestScopeProviderTest.java b/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/SinglePlayerQuestScopeProviderTest.java index 6cdf36332..48e7f387a 100644 --- a/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/SinglePlayerQuestScopeProviderTest.java +++ b/src/test/java/us/eunoians/mcrpg/quest/impl/scope/impl/SinglePlayerQuestScopeProviderTest.java @@ -1,68 +1,187 @@ package us.eunoians.mcrpg.quest.impl.scope.impl; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import us.eunoians.mcrpg.McRPGBaseTest; import us.eunoians.mcrpg.expansion.McRPGExpansion; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class SinglePlayerQuestScopeProviderTest extends McRPGBaseTest { - @DisplayName("getKey returns single_player_scope key") - @Test - void getKey_returnsSinglePlayerScopeKey() { - SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); - assertEquals(SinglePlayerQuestScopeProvider.KEY, provider.getKey()); - assertEquals("mcrpg:single_player_scope", provider.getKey().toString()); - } + @Nested + @DisplayName("Identity") + class Identity { + + @DisplayName("getKey returns single_player_scope key") + @Test + void getKey_returnsSinglePlayerScopeKey() { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + assertEquals(SinglePlayerQuestScopeProvider.KEY, provider.getKey()); + assertEquals("mcrpg:single_player_scope", provider.getKey().toString()); + } - @DisplayName("getExpansionKey returns McRPG expansion key") - @Test - void getExpansionKey_returnsMcRPGExpansionKey() { - SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); - assertTrue(provider.getExpansionKey().isPresent()); - assertEquals(McRPGExpansion.EXPANSION_KEY, provider.getExpansionKey().orElseThrow()); + @DisplayName("getExpansionKey returns McRPG expansion key") + @Test + void getExpansionKey_returnsMcRPGExpansionKey() { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + assertTrue(provider.getExpansionKey().isPresent()); + assertEquals(McRPGExpansion.EXPANSION_KEY, provider.getExpansionKey().orElseThrow()); + } } - @DisplayName("createNewScope returns scope with correct quest UUID") - @Test - void createNewScope_returnsScope_withCorrectQuestUUID() { - SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); - UUID questUUID = UUID.randomUUID(); + @Nested + @DisplayName("createNewScope") + class CreateNewScope { + + @DisplayName("createNewScope returns scope with correct quest UUID") + @Test + void createNewScope_returnsScope_withCorrectQuestUUID() { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + + SinglePlayerQuestScope scope = provider.createNewScope(questUUID); + + assertNotNull(scope); + assertEquals(questUUID, scope.getQuestUUID()); + } + + @DisplayName("createNewScope returns invalid scope (no player set)") + @Test + void createNewScope_returnsInvalidScope() { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + + SinglePlayerQuestScope scope = provider.createNewScope(UUID.randomUUID()); - SinglePlayerQuestScope scope = provider.createNewScope(questUUID); + assertTrue(scope.getPlayerInScope().isEmpty()); + assertTrue(scope.getCurrentPlayersInScope().isEmpty()); + } - assertNotNull(scope); - assertEquals(questUUID, scope.getQuestUUID()); + @DisplayName("Each createNewScope call returns a distinct scope") + @Test + void createNewScope_returnsDistinctScopes() { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID questUUID1 = UUID.randomUUID(); + UUID questUUID2 = UUID.randomUUID(); + + SinglePlayerQuestScope scope1 = provider.createNewScope(questUUID1); + SinglePlayerQuestScope scope2 = provider.createNewScope(questUUID2); + + assertEquals(questUUID1, scope1.getQuestUUID()); + assertEquals(questUUID2, scope2.getQuestUUID()); + } } - @DisplayName("createNewScope returns invalid scope (no player set)") - @Test - void createNewScope_returnsInvalidScope() { - SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + @Nested + @DisplayName("resolveActiveQuestUUIDs") + class ResolveActiveQuestUUIDs { + + @Test + @DisplayName("Given active quests in the database, when resolveActiveQuestUUIDs is called, then the quest UUIDs are returned") + void resolveActiveQuestUUIDs_returnsQuestUUIDs_whenActiveQuestsExist() throws SQLException { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID playerUUID = UUID.randomUUID(); + UUID questUUID1 = UUID.randomUUID(); + UUID questUUID2 = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("quest_uuid")).thenReturn(questUUID1.toString(), questUUID2.toString()); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); - SinglePlayerQuestScope scope = provider.createNewScope(UUID.randomUUID()); + assertEquals(2, result.size()); + assertTrue(result.contains(questUUID1)); + assertTrue(result.contains(questUUID2)); + verify(ps).setString(1, playerUUID.toString()); + } - assertTrue(scope.getPlayerInScope().isEmpty()); - assertTrue(scope.getCurrentPlayersInScope().isEmpty()); + @Test + @DisplayName("Given no active quests, when resolveActiveQuestUUIDs is called, then an empty list is returned") + void resolveActiveQuestUUIDs_returnsEmptyList_whenNoActiveQuests() throws SQLException { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID playerUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(false); + + List result = provider.resolveActiveQuestUUIDs(playerUUID, connection); + + assertTrue(result.isEmpty()); + } } - @DisplayName("Each createNewScope call returns a distinct scope") - @Test - void createNewScope_returnsDistinctScopes() { - SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); - UUID questUUID1 = UUID.randomUUID(); - UUID questUUID2 = UUID.randomUUID(); + @Nested + @DisplayName("loadScope(Connection, UUID)") + class LoadScope { + + @Test + @DisplayName("Given a stored scope, when loadScope is called, then the scope has the correct player set") + void loadScope_returnsScopeWithPlayer_whenStoredInDatabase() throws SQLException { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + UUID playerUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true); + when(rs.getString("player_uuid")).thenReturn(playerUUID.toString()); + + SinglePlayerQuestScope scope = provider.loadScope(connection, questUUID); + + assertNotNull(scope); + assertEquals(questUUID, scope.getQuestUUID()); + assertTrue(scope.getPlayerInScope().isPresent()); + assertEquals(playerUUID, scope.getPlayerInScope().orElseThrow()); + } + + @Test + @DisplayName("Given a stored scope, when loadScope is called, then the quest UUID parameter is passed to the DAO") + void loadScope_passesQuestUUIDToDAO() throws SQLException { + SinglePlayerQuestScopeProvider provider = new SinglePlayerQuestScopeProvider(); + UUID questUUID = UUID.randomUUID(); + UUID playerUUID = UUID.randomUUID(); + + Connection connection = mock(Connection.class); + PreparedStatement ps = mock(PreparedStatement.class); + ResultSet rs = mock(ResultSet.class); + + when(connection.prepareStatement(anyString())).thenReturn(ps); + when(ps.executeQuery()).thenReturn(rs); + when(rs.next()).thenReturn(true); + when(rs.getString("player_uuid")).thenReturn(playerUUID.toString()); - SinglePlayerQuestScope scope1 = provider.createNewScope(questUUID1); - SinglePlayerQuestScope scope2 = provider.createNewScope(questUUID2); + provider.loadScope(connection, questUUID); - assertEquals(questUUID1, scope1.getQuestUUID()); - assertEquals(questUUID2, scope2.getQuestUUID()); + verify(ps).setString(1, questUUID.toString()); + } } }