Skip to content

Commit abcef29

Browse files
tastybentoclaude
andcommitted
Stop ChallengesAddonTest from poisoning Tag for the rest of the JVM
Copilot was right that my earlier `Tag.LEAVES`-in-PanelTestHelper patch didn't actually fix anything — it just touched a Tag constant that was already broken. The real flake is upstream: ChallengesAddonTest installs `Mockito.mockStatic(Bukkit.class)` *without* MockBukkit, then runs code during `addon.onLoad()` that triggers `org.bukkit.Tag.<clinit>`. With Bukkit's static methods returning null stubs at that moment, every Tag constant is permanently set to null for the JVM — there is no way to re-initialize an interface's static fields once <clinit> has run. Any test later in the suite that creates an ItemStack then walks: ItemType.<clinit> -> Registry.ITEM.getOrThrow("acacia_hanging_sign") -> RegistryMock.loadIfEmpty -> ItemTypeMock.from(...) -> Class.forName("BlockStateMetaMock") -> BlockStateMetaMock.<clinit> -> MaterialTags.<clinit> -> Objects.requireNonNull(Tag.ALL_SIGNS) // null -> NPE The NPE wraps as ExceptionInInitializerError, RegistryMock catches and swallows part of it, and `acacia_hanging_sign` is missing from the registry forever after — hence the cascade of 286 errors on CI but a clean local run, where the test order happens to put a properly- MockBukkit-bracketed test first. Reproduced locally with `mvn test -Dtest=ChallengesAddonTest, CommonPagedPanelTest -Dsurefire.runOrder=alphabetical`. Fix: install MockBukkit briefly at the very top of ChallengesAddonTest.setUp() and prime Tag while it's active, then unmock immediately. Tag's static constants are now real MaterialTagMock instances and stay valid for the rest of the JVM, regardless of what subsequent tests do with Bukkit. As a side effect, this also clears up ChallengesAddonTest's own pre-existing "Settings is null" failures when it runs early in the suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e459a31 commit abcef29

1 file changed

Lines changed: 12 additions & 0 deletions

File tree

src/test/java/world/bentobox/challenges/ChallengesAddonTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@
4242
import org.junit.jupiter.api.AfterEach;
4343
import org.junit.jupiter.api.BeforeEach;
4444
import org.junit.jupiter.api.Test;
45+
import org.mockbukkit.mockbukkit.MockBukkit;
4546
import org.mockito.Mock;
4647
import org.mockito.MockedStatic;
4748
import org.mockito.Mockito;
4849
import org.mockito.MockitoAnnotations;
4950
import org.mockito.stubbing.Answer;
5051

52+
import world.bentobox.challenges.panel.PanelTestHelper;
53+
5154
import world.bentobox.bentobox.BentoBox;
5255
import world.bentobox.bentobox.Settings;
5356
import world.bentobox.bentobox.api.addons.Addon;
@@ -101,6 +104,15 @@ public class ChallengesAddonTest {
101104
@BeforeEach
102105
public void setUp() throws Exception {
103106
closeable = MockitoAnnotations.openMocks(this);
107+
// Force Bukkit's Tag.<clinit> to run against a real MockBukkit ServerMock before
108+
// we install the Mockito static mock below. Without this, Tag.<clinit> can later
109+
// fire while Bukkit is statically mocked, permanently null-ing every Tag constant
110+
// for the JVM and corrupting any subsequent test that creates an ItemStack
111+
// (e.g. CommonPagedPanelTest -> ItemType.<clinit> -> MaterialTags.<clinit> ->
112+
// Objects.requireNonNull(Tag.ALL_SIGNS) -> NPE).
113+
MockBukkit.mock();
114+
PanelTestHelper.primeBukkitRegistry();
115+
MockBukkit.unmock();
104116
// Set up plugin
105117
WhiteBox.setInternalState(BentoBox.class, "instance", plugin);
106118
when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());

0 commit comments

Comments
 (0)