From 4399087824bfd1069b3478d7a4483f90bb719836 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 12 Jul 2026 09:04:15 -0700 Subject: [PATCH] Address Copilot review on the 1.8.0 release PR (#421) - ChallengesAddon.allLoaded(): reset levelProvided=false when the Level add-on is absent, so isLevelProvided() can't be stale-true and TryToComplete.rewardIslandLevel() can't NPE on a null level add-on. - EditChallengePanel: sort the required-biome list before rendering so the admin GUI description is stable (the backing set is unordered). - ChallengesPanel.createToggleUndeployedButton(): guard the template description with !isBlank() to avoid inserting empty lore lines, matching the other button builders. - ChallengesAddonTest: verify placeholder registration with atLeastOnce() instead of times(13) so the tests don't break whenever a placeholder is added or removed. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01DfEq1X2sy57G8nq2Cd23ba --- .../world/bentobox/challenges/ChallengesAddon.java | 1 + .../challenges/panel/admin/EditChallengePanel.java | 7 +++++-- .../challenges/panel/user/ChallengesPanel.java | 2 +- .../world/bentobox/challenges/ChallengesAddonTest.java | 10 ++++++---- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index 5c7d6723..39c9c530 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -234,6 +234,7 @@ public void allLoaded() this.log("Challenges Addon hooked into Level addon."); }, () -> { this.levelAddon = null; + this.levelProvided = false; this.logWarning("Level add-on not found so level challenges will not work!"); }); diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java index 0a624e52..aaa32a78 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -962,8 +962,11 @@ private PanelItem createBiomeRequirementButton() { description.add(this.user.getTranslation(reference + "none")); } else { description.add(this.user.getTranslation(reference + Constants.TITLE_KEY)); - requirements.getRequiredBiomes().forEach(biomeKey -> description.add( - this.user.getTranslation(reference + "list", "[biome]", Utils.prettifyBiome(biomeKey)))); + // Sort so the biome list renders in a stable order (the underlying set is unordered). + requirements.getRequiredBiomes().stream() + .sorted(Comparator.comparing(Utils::prettifyBiome)) + .forEach(biomeKey -> description.add( + this.user.getTranslation(reference + "list", "[biome]", Utils.prettifyBiome(biomeKey)))); } description.add(""); diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java index 93493efe..597bbc02 100644 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesPanel.java @@ -714,7 +714,7 @@ private PanelItem createToggleUndeployedButton(@NonNull ItemTemplateRecord templ builder.name(this.user.getTranslation(this.world, template.title())); } - if (template.description() != null) + if (template.description() != null && !template.description().isBlank()) { builder.description(this.user.getTranslation(this.world, template.description())); } diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index 3b22e472..2a5e9a2d 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -377,9 +377,10 @@ void testPlaceholderCompletedPercentRegistered() { addon.onEnable(); - // Verify that the completed_percent placeholder was registered + // Capture every registered placeholder name; assert the one under test is present without + // pinning the exact total (which changes whenever any placeholder is added or removed). ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - verify(phm, org.mockito.Mockito.times(13)).registerPlaceholder(any(GameModeAddon.class), nameCaptor.capture(), any()); + verify(phm, org.mockito.Mockito.atLeastOnce()).registerPlaceholder(any(GameModeAddon.class), nameCaptor.capture(), any()); List names = nameCaptor.getAllValues(); assertTrue(names.contains("challenges_completed_percent"), @@ -398,9 +399,10 @@ void testPlaceholderLatestLevelCompletedPercentRegistered() { addon.onEnable(); - // Verify that the latest_level_completed_percent placeholder was registered + // Capture every registered placeholder name; assert the one under test is present without + // pinning the exact total (which changes whenever any placeholder is added or removed). ArgumentCaptor nameCaptor = ArgumentCaptor.forClass(String.class); - verify(phm, org.mockito.Mockito.times(13)).registerPlaceholder(any(GameModeAddon.class), nameCaptor.capture(), any()); + verify(phm, org.mockito.Mockito.atLeastOnce()).registerPlaceholder(any(GameModeAddon.class), nameCaptor.capture(), any()); List names = nameCaptor.getAllValues(); assertTrue(names.contains("challenges_latest_level_completed_percent"),