Skip to content

[Tests] Add unit tests for ManagerKeyImpl, PluginHookKeyImpl, StatisticCacheKey, CoreGuiOpenEvent#42

Open
DiamondDagger590 wants to merge 1 commit into
developfrom
claude/stoic-cori-mewxcb
Open

[Tests] Add unit tests for ManagerKeyImpl, PluginHookKeyImpl, StatisticCacheKey, CoreGuiOpenEvent#42
DiamondDagger590 wants to merge 1 commit into
developfrom
claude/stoic-cori-mewxcb

Conversation

@DiamondDagger590

@DiamondDagger590 DiamondDagger590 commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • ManagerKeyImpl (10 tests): Covers factory method create(), managerClass() getter, record accessor manager(), equality/hashCode for same and different classes, toString output, self-equality, and null comparison
  • PluginHookKeyImpl (10 tests): Covers factory method create(), hookClass() getter, record accessor clazz(), equality/hashCode for same and different classes, toString output, self-equality, and null comparison
  • StatisticCacheKey (12 tests): Covers record accessors (uuid(), key()), equality for matching fields, inequality when UUID differs / NamespacedKey differs / both differ, hashCode consistency, toString output, self-equality, null comparison, and HashMap key behavior (lookup by equal key succeeds, lookup by different key returns null)
  • CoreGuiOpenEvent (10 tests): Covers constructor with all fields, getPlayerUUID(), getGui() instance identity, getGuiKey() returning present Optional when key is non-null, getGuiKey() returning empty Optional when key is null, getHandlers() / getHandlerList() non-null and identity, KeyedGui integration (with and without key), and events with different player UUIDs

Test counts

Class Tests
ManagerKeyImpl 10
PluginHookKeyImpl 10
StatisticCacheKey 12
CoreGuiOpenEvent 10
Total new 42

All 713 tests pass (671 existing + 42 new).

Test plan

🤖 Generated with Claude Code

https://claude.ai/code/session_01KddWqLYU2aDVveN7douCYe


Generated by Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded test coverage for core event handling, registry management, plugin hooks, and caching mechanisms to improve system reliability and stability.

…ey, CoreGuiOpenEvent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddWqLYU2aDVveN7douCYe
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Four new JUnit 5 test classes are added with no production code changes: CoreGuiOpenEventTest, ManagerKeyImplTest, PluginHookKeyImplTest, and StatisticCacheKeyTest. Each class defines local stub/helper types and covers accessor correctness, equals/hashCode contracts, toString content, and edge-case behavior.

Changes

New unit test coverage

Layer / File(s) Summary
CoreGuiOpenEvent tests
src/test/java/com/diamonddagger590/mccore/event/gui/CoreGuiOpenEventTest.java
Adds stub Gui/KeyedGui implementations and a NamespacedKey helper, then tests player UUID and GUI instance retrieval, getGuiKey() optional presence for keyed/non-keyed/null cases, HandlerList non-nullity and static identity, and UUID preservation across multiple event instances.
ManagerKeyImpl and PluginHookKeyImpl tests
src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerKeyImplTest.java, src/test/java/com/diamonddagger590/mccore/registry/plugin/PluginHookKeyImplTest.java
Tests ManagerKeyImpl creation, managerClass/manager accessors, equals/hashCode for same and different manager classes, toString content, and self/null equality. Tests PluginHookKeyImpl factory, hookClass/clazz accessors, value-semantic equality and hash contracts, and toString class name content.
StatisticCacheKey tests
src/test/java/com/diamonddagger590/mccore/statistic/cache/StatisticCacheKeyTest.java
Adds fixed UUID/NamespacedKey fixtures and validates UUID and key accessors, equals across matching/non-matching UUID and key combinations (including self and null), hashCode consistency, toString UUID content, and HashMap key lookup correctness.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically summarizes the main change: adding unit tests for four classes (ManagerKeyImpl, PluginHookKeyImpl, StatisticCacheKey, CoreGuiOpenEvent).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/stoic-cori-mewxcb

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

Extensibility Review

Breaking change risk: NONE — this diff adds only test classes under src/test/java; no production source files are modified.

No extensibility concerns found.

All four files introduced by this diff are test-only (src/test/java/…) and are never compiled into the McCore artifact consumed by downstream plugins. They exercise existing public types (CoreGuiOpenEvent, ManagerKeyImpl, PluginHookKeyImpl, StatisticCacheKey) without altering any public API, abstract contract, method signature, registry key, or database migration. There are no changes to production interfaces, abstract classes, the gui/ package, the database/ package, or any registry constants that could affect binary or behavioral compatibility for plugin consumers.

@github-actions

Copy link
Copy Markdown

Testing Review

I'll systematically apply every checklist item to the three new test files and the production classes they exercise.


File-by-File Analysis

CoreGuiOpenEventTest.java

CONCERN: NamespacedKey is instantiated via the deprecated two-string constructor without MockBukkit running.
WHY: NamespacedKey(String, String) is a Bukkit API class. In many MockBukkit versions the constructor works without a server, but it is still a Bukkit type. More critically, several Gui/KeyedGui/Slot interfaces (getInventory(), getSlot()) are Bukkit-bound. The test compiles and runs only because every Bukkit-touching method throws UnsupportedOperationException in the stubs — but the test file sits in src/test/java/ with zero MockBukkit setup. If any future assertion needs a real Inventory or ItemStack, the test will silently pass until it explodes. There is no @BeforeEach/@AfterEach guard asserting MockBukkit is absent, and no comment documenting the deliberate choice.
WHERE: CoreGuiOpenEventTest / CoreGuiOpenEvent


CONCERN: event_reflectsGuiKey_whenKeyedGuiIsUsed and event_hasEmptyGuiKey_whenKeyedGuiHasNoKey duplicate getGuiKey_returnsPresentOptional_whenKeyIsProvided and getGuiKey_returnsEmptyOptional_whenKeyIsNull with only a StubKeyedGui wrapper added; neither tests any KeyedGui-specific behavior beyond what the plain-StubGui tests already cover.
WHY: These two tests add no new assertion paths — the KeyedGui path in CoreGuiOpenEvent (if one exists) is not exercised; the event constructor just stores whatever NamespacedKey is passed to it regardless of whether the GUI implements KeyedGui. The tests do not verify that the event reads the key from the GUI via KeyedGui.getGuiKey(); they merely re-assert that a key passed to the constructor comes back from getGuiKey(). If CoreGuiOpenEvent has a constructor overload that accepts a KeyedGui and extracts the key itself, that path is untested.
WHERE: CoreGuiOpenEventTest (lines ~107–125)


CONCERN: No test covers the isCancelled / setCancelled contract if CoreGuiOpenEvent implements Cancellable.
WHY: Bukkit events commonly implement Cancellable. If CoreGuiOpenEvent does, the cancel/uncancel/default-uncancelled cycle is a non-trivial behavior path (>3 lines of inherited contract) with no test.
WHERE: CoreGuiOpenEventTest / CoreGuiOpenEvent


CONCERN: No test passes a null Gui to the constructor.
WHY: The constructor receives @NotNull Gui<CorePlayer> gui — but the null-input edge case is untested. If the constructor stores it without null-checking, downstream callers will get a NullPointerException at getGui(). The checklist requires null-input edge cases for every non-trivial public method.
WHERE: CoreGuiOpenEventTest / CoreGuiOpenEvent


CONCERN: No test passes a null UUID to the constructor.
WHY: Same null-input edge-case gap as above — getPlayerUUID() would return null, which callers likely do not expect.
WHERE: CoreGuiOpenEventTest / CoreGuiOpenEvent


ManagerKeyImplTest.java

CONCERN: Both inner stub classes call super(null), passing null to Manager's constructor. If Manager dereferences the plugin reference during construction (e.g., in an initializer block), every test in this file will throw a NullPointerException at construction time, silently invalidating the entire suite.
WHY: The test relies on implementation details of Manager's constructor. There is no comment or assertion documenting that null is a valid construction argument, nor is there a test that verifies the key's behavior is independent of the manager instance.
WHERE: ManagerKeyImplTest (lines ~13–22) / Manager


CONCERN: No test exercises ManagerRegistry look-up using a ManagerKeyImpl as the map key — the most important real-world use of equals/hashCode correctness.
WHY: The tests verify structural record equality in isolation but do not confirm that the key participates correctly in the registry's map (e.g., registerManager followed by getManager returning the same instance). The checklist flags non-trivial logic (registry lookup) with no corresponding integration test.
WHERE: ManagerKeyImplTest / ManagerRegistry (production class, untested here)


CONCERN: toString_containsClassName_whenCalled uses assertEquals(true, str.contains("TestManagerA")) instead of assertTrue(str.contains("TestManagerA")).
WHY: This is a structural test quality issue: assertEquals(true, expr) produces an unhelpful failure message ("expected: but was: ") compared to assertTrue(expr, message). While this does not cause a false positive, it makes failures harder to diagnose and indicates the test was not written with assertion semantics in mind. Same pattern recurs in PluginHookKeyImplTest and StatisticCacheKeyTest.
WHERE: ManagerKeyImplTest line ~85, PluginHookKeyImplTest line ~85, StatisticCacheKeyTest line ~95


CONCERN: No test covers ManagerKeyImpl.create(null) (null class reference).
WHY: Null-input edge case required by the checklist for every non-trivial public method. If create wraps a record constructor with no null guard, managerClass() would return null and any map using this key could corrupt registry state.
WHERE: ManagerKeyImplTest / ManagerKeyImpl


PluginHookKeyImplTest.java

CONCERN: Same super(null) concern as ManagerKeyImplTestPluginHook's constructor receives null and may dereference it.
WHY: Identical structural risk: if PluginHook touches the plugin in its constructor body, every test in this file fails at instantiation.
WHERE: PluginHookKeyImplTest (lines ~13–22) / PluginHook


CONCERN: No test covers PluginHookRegistry round-trip: register a hook, look it up by PluginHookKeyImpl, assert the same instance is returned.
WHY: The registry is the primary consumer of equals/hashCode. Testing the key in isolation without testing its role in the registry leaves the most critical integration path uncovered.
WHERE: PluginHookKeyImplTest / PluginHookRegistry (untested)


CONCERN: No test covers PluginHookKeyImpl.create(null).
WHY: Null-input edge case gap — mirrors the ManagerKeyImpl issue.
WHERE: PluginHookKeyImplTest / PluginHookKeyImpl


StatisticCacheKeyTest.java

CONCERN: NamespacedKey is constructed via the deprecated two-arg constructor without a Bukkit server mock, and the class is declared as a plain JUnit test with no MockBukkit setup. This is actually the correct approach if NamespacedKey can be constructed standalone — but this assumption must be validated: if a future Bukkit version requires a server for NamespacedKey construction, every test here silently breaks.
WHY: The test should either (a) use MockBukkit to guarantee a stable Bukkit environment, or (b) document explicitly (in a comment or @Tag) that it relies on NamespacedKey's no-server constructor remaining valid. Currently it does neither.
WHERE: StatisticCacheKeyTest / StatisticCacheKey


CONCERN: No test covers new StatisticCacheKey(null, KEY_KILLS) or new StatisticCacheKey(UUID_A, null).
WHY: Null-input edge cases are required by the checklist. A null UUID or null key used in a HashMap will produce NullPointerException during hashCode() unless the record explicitly handles it. These are the two most likely causes of cache corruption in production.
WHERE: StatisticCacheKeyTest / StatisticCacheKey


CONCERN: cacheKey_returnsNull_whenMapKeyDoesNotMatch uses assertEquals(null, map.get(key2)) instead of assertNull(map.get(key2)).
WHY: Same structural test quality issue as the assertTrue/assertEquals(true,...) pattern — assertEquals(null, expr) produces a less meaningful failure message than assertNull(expr, message).
WHERE: StatisticCacheKeyTest line ~124


CONCERN: The StatisticCache class itself (which presumably uses StatisticCacheKey as a map key) has no tests covering put/get/evict/invalidate behavior.
WHY: Testing the key record in isolation does not verify the cache's eviction policy, TTL behavior, or concurrent-access semantics. If StatisticCache has non-trivial logic (>3 lines), the checklist requires a corresponding test.
WHERE: No test file for StatisticCache is present in the diff.


Cross-Cutting Concerns

CONCERN: No src/testFixtures/java/ additions accompany these tests, yet StubGui, StubKeyedGui, and the stub Manager/PluginHook subclasses are exactly the kind of shared fixtures McRPG downstream repos would need.
WHY: The checklist requires shared fixtures to live in src/testFixtures/java/. Currently they are private static inner classes in individual test files, making them unreachable by McRPG integration tests.
WHERE: CoreGuiOpenEventTest, ManagerKeyImplTest, PluginHookKeyImplTest


CONCERN: None of the three test files contain any TimeProvider usage, and none of the tested production classes appear to use time. This checklist item is satisfied by absence — no violation.
WHERE: N/A


CONCERN: None of the three test files spin up MockBukkit, yet CoreGuiOpenEventTest and StatisticCacheKeyTest use NamespacedKey. If a test calls zero Bukkit APIs beyond constructing a NamespacedKey, it should be a plain JUnit test — which these files are. However, CoreGuiOpenEvent is a Bukkit Event subclass, meaning its HandlerList registration is a Bukkit mechanism. Testing that mechanism without MockBukkit means the HandlerList static state is shared across all test runs in the JVM, potentially leaking across test classes.
WHY: HandlerList static state is never reset between test classes, which can cause interference if other test suites register handlers.
WHERE: CoreGuiOpenEventTest


Summary

Production files changed: CoreGuiOpenEvent, ManagerKeyImpl, PluginHookKeyImpl, StatisticCacheKey
(inferred from test package structure; no production-side diff lines were included)

Test files present: CoreGuiOpenEventTest.java, ManagerKeyImplTest.java, PluginHookKeyImplTest.java, StatisticCacheKeyTest.java

Coverage gaps:

  • Null UUID / null GUI / null key constructor inputs untested across all three event/key test files
  • ManagerRegistry and PluginHookRegistry round-trip (register → look up) untested
  • StatisticCache put/get/evict behavior untested
  • CoreGuiOpenEvent Cancellable contract (if applicable) untested
  • KeyedGui-specific constructor path in CoreGuiOpenEvent untested (key extraction from GUI vs. explicit parameter)
  • ManagerKeyImpl.create(null) and PluginHookKeyImpl.create(null) untested
  • Shared stubs not promoted to src/testFixtures/java/
  • assertEquals(true, ...) / assertEquals(null, ...) used where assertTrue / assertNull are structurally correct
  • HandlerList static state not cleaned up between test classes in CoreGuiOpenEventTest

@github-actions

Copy link
Copy Markdown

Security Review

No security concerns found.

The diff contains exclusively test code (src/test/java/...). All four new test files exercise value-object equality/hashing, event accessor correctness, and map key behavior using hard-coded UUID literals, compile-time NamespacedKey constants, and stub implementations. No production DAO methods, DDL builders, MiniMessage deserialization paths, ChatResponse handlers, Slot.onClick() implementations, or permission-gated operations are introduced or modified.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/test/java/com/diamonddagger590/mccore/event/gui/CoreGuiOpenEventTest.java`:
- Around line 57-144: Add two new test methods to the CoreGuiOpenEventTest class
to cover null-input edge cases for the CoreGuiOpenEvent constructor. Create one
test that passes a null playerUUID parameter to the CoreGuiOpenEvent constructor
to verify it throws an exception or handles null appropriately, and create
another test that passes a null gui parameter to verify the expected behavior
for that required input. These tests should follow the same naming and assertion
patterns as the existing test methods in the class.

In
`@src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerKeyImplTest.java`:
- Around line 25-30: The ManagerKeyImpl.create() method is annotated with
`@NotNull` on its parameter, but there is no test verifying the behavior when null
is passed. Add a new test method that passes null as the argument to
ManagerKeyImpl.create() and assert that it throws the expected exception (such
as NullPointerException or IllegalArgumentException). This test should follow
the same naming convention and DisplayName pattern as the existing
create_returnsNonNullKey_whenGivenManagerClass test to ensure null-input edge
cases are properly covered per coding guidelines.

In
`@src/test/java/com/diamonddagger590/mccore/registry/plugin/PluginHookKeyImplTest.java`:
- Around line 25-30: Add a new test method to the PluginHookKeyImplTest class
that tests the null-input edge case for the PluginHookKeyImpl.create() factory
method. The test should pass null as the hook class parameter and verify that
the method throws the expected exception (such as NullPointerException or
IllegalArgumentException) that aligns with the `@NotNull` contract on the class
parameter. Use an appropriate assertion like assertThrows to capture and
validate the exception behavior.

In
`@src/test/java/com/diamonddagger590/mccore/statistic/cache/StatisticCacheKeyTest.java`:
- Around line 27-127: Add two test methods to verify null-input edge cases for
the StatisticCacheKey constructor. Create a test that attempts to instantiate
StatisticCacheKey with a null uuid parameter while providing a valid key, and
another test that attempts to instantiate it with a valid uuid but a null key
parameter. These tests should verify that the constructor properly rejects null
inputs and throws an appropriate exception (such as NullPointerException or
IllegalArgumentException) to enforce the non-null contract for both the uuid and
key fields.
- Around line 76-80: The test method hashCode_differs_whenKeysAreNotEqual()
incorrectly asserts that unequal StatisticCacheKey objects must have different
hash codes, which violates Java's hashCode contract. Java only requires that
equal objects have equal hash codes, not the reverse. Replace this test with one
that validates the actual contract: create two StatisticCacheKey objects with
identical parameters (same UUID and KEY values), then use assertEquals() to
assert that their hash codes are equal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a519f5ba-f1c1-448a-8ff9-944900b7693d

📥 Commits

Reviewing files that changed from the base of the PR and between 17bed82 and 5ea7801.

📒 Files selected for processing (4)
  • src/test/java/com/diamonddagger590/mccore/event/gui/CoreGuiOpenEventTest.java
  • src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerKeyImplTest.java
  • src/test/java/com/diamonddagger590/mccore/registry/plugin/PluginHookKeyImplTest.java
  • src/test/java/com/diamonddagger590/mccore/statistic/cache/StatisticCacheKeyTest.java

Comment on lines +57 to +144
@Test
@DisplayName("Given a player UUID, GUI, and key, when creating event, then getPlayerUUID returns the UUID")
void getPlayerUUID_returnsCorrectUUID_whenEventIsCreated() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, GUI_KEY);
assertEquals(PLAYER_UUID, event.getPlayerUUID());
}

@Test
@DisplayName("Given a GUI, when creating event, then getGui returns the same GUI instance")
void getGui_returnsSameInstance_whenEventIsCreated() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, GUI_KEY);
assertEquals(gui, event.getGui());
}

@Test
@DisplayName("Given a non-null GUI key, when calling getGuiKey, then returns present Optional with correct key")
void getGuiKey_returnsPresentOptional_whenKeyIsProvided() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, GUI_KEY);
assertTrue(event.getGuiKey().isPresent());
assertEquals(GUI_KEY, event.getGuiKey().get());
}

@Test
@DisplayName("Given a null GUI key, when calling getGuiKey, then returns empty Optional")
void getGuiKey_returnsEmptyOptional_whenKeyIsNull() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, null);
assertTrue(event.getGuiKey().isEmpty());
}

@Test
@DisplayName("Given a CoreGuiOpenEvent, when calling getHandlers, then returns non-null HandlerList")
void getHandlers_returnsNonNullHandlerList_whenCalled() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, null);
assertNotNull(event.getHandlers());
}

@Test
@DisplayName("Given CoreGuiOpenEvent class, when calling getHandlerList, then returns non-null static HandlerList")
void getHandlerList_returnsNonNullStaticHandlerList_whenCalled() {
HandlerList handlerList = CoreGuiOpenEvent.getHandlerList();
assertNotNull(handlerList);
}

@Test
@DisplayName("Given a CoreGuiOpenEvent, when calling getHandlers and getHandlerList, then they return the same instance")
void getHandlers_returnsSameInstanceAsStaticGetHandlerList() {
StubGui gui = new StubGui();
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, gui, null);
assertEquals(CoreGuiOpenEvent.getHandlerList(), event.getHandlers());
}

@Test
@DisplayName("Given a keyed GUI with a key, when creating event with that key, then event reflects the key")
void event_reflectsGuiKey_whenKeyedGuiIsUsed() {
StubKeyedGui keyedGui = new StubKeyedGui(GUI_KEY);
NamespacedKey extractedKey = keyedGui.getGuiKey().orElse(null);
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, keyedGui, extractedKey);
assertTrue(event.getGuiKey().isPresent());
assertEquals(GUI_KEY, event.getGuiKey().get());
}

@Test
@DisplayName("Given a keyed GUI without a key, when creating event, then event has empty guiKey")
void event_hasEmptyGuiKey_whenKeyedGuiHasNoKey() {
StubKeyedGui keyedGui = new StubKeyedGui(null);
NamespacedKey extractedKey = keyedGui.getGuiKey().orElse(null);
CoreGuiOpenEvent event = new CoreGuiOpenEvent(PLAYER_UUID, keyedGui, extractedKey);
assertTrue(event.getGuiKey().isEmpty());
}

@Test
@DisplayName("Given different player UUIDs, when creating events, then each event has its own UUID")
void events_haveDifferentUUIDs_whenCreatedWithDifferentPlayers() {
UUID uuid1 = UUID.fromString("00000000-0000-0000-0000-000000000001");
UUID uuid2 = UUID.fromString("00000000-0000-0000-0000-000000000002");
StubGui gui = new StubGui();

CoreGuiOpenEvent event1 = new CoreGuiOpenEvent(uuid1, gui, null);
CoreGuiOpenEvent event2 = new CoreGuiOpenEvent(uuid2, gui, null);

assertEquals(uuid1, event1.getPlayerUUID());
assertEquals(uuid2, event2.getPlayerUUID());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add constructor null-input edge-case tests for playerUUID and gui.

Current coverage only exercises nullability for guiKey. Please add tests for new CoreGuiOpenEvent(null, gui, ...) and new CoreGuiOpenEvent(uuid, null, ...) to lock expected behavior for required inputs (throwing or explicit handling).

As per coding guidelines, “Cover edge cases in tests: null inputs, empty collections, zero/negative numeric inputs, and max/limit values.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/diamonddagger590/mccore/event/gui/CoreGuiOpenEventTest.java`
around lines 57 - 144, Add two new test methods to the CoreGuiOpenEventTest
class to cover null-input edge cases for the CoreGuiOpenEvent constructor.
Create one test that passes a null playerUUID parameter to the CoreGuiOpenEvent
constructor to verify it throws an exception or handles null appropriately, and
create another test that passes a null gui parameter to verify the expected
behavior for that required input. These tests should follow the same naming and
assertion patterns as the existing test methods in the class.

Source: Coding guidelines

Comment on lines +25 to +30
@Test
@DisplayName("Given a manager class, when creating a key, then key is not null")
void create_returnsNonNullKey_whenGivenManagerClass() {
ManagerKey<TestManagerA> key = ManagerKeyImpl.create(TestManagerA.class);
assertNotNull(key);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null-input coverage for the factory contract.

ManagerKeyImpl.create(...) is annotated @NotNull on its parameter, but there is no test asserting behavior when null is passed. Add a dedicated test to lock this edge-case contract.

As per coding guidelines, "Cover edge cases in tests: null inputs, empty collections, zero/negative numeric inputs, and max/limit values."

Proposed test addition
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
@@
     `@Test`
     `@DisplayName`("Given a key compared to null, when checking equality, then returns false")
     void equals_returnsFalse_whenComparedToNull() {
         ManagerKey<TestManagerA> key = ManagerKeyImpl.create(TestManagerA.class);
         assertNotEquals(null, key);
     }
+
+    `@Test`
+    `@DisplayName`("Given a null manager class, when creating a key, then throws NullPointerException")
+    void create_throwsNullPointerException_whenGivenNullManagerClass() {
+        assertThrows(NullPointerException.class, () -> ManagerKeyImpl.create(null));
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerKeyImplTest.java`
around lines 25 - 30, The ManagerKeyImpl.create() method is annotated with
`@NotNull` on its parameter, but there is no test verifying the behavior when null
is passed. Add a new test method that passes null as the argument to
ManagerKeyImpl.create() and assert that it throws the expected exception (such
as NullPointerException or IllegalArgumentException). This test should follow
the same naming convention and DisplayName pattern as the existing
create_returnsNonNullKey_whenGivenManagerClass test to ensure null-input edge
cases are properly covered per coding guidelines.

Source: Coding guidelines

Comment on lines +25 to +30
@Test
@DisplayName("Given a hook class, when creating a key, then key is not null")
void create_returnsNonNullKey_whenGivenHookClass() {
PluginHookKey<TestHookA> key = PluginHookKeyImpl.create(TestHookA.class);
assertNotNull(key);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null-input test for PluginHookKeyImpl.create(...).

The suite does not cover null input for the factory method despite the @NotNull contract on the class parameter. Add one edge-case test to pin expected behavior.

As per coding guidelines, "Cover edge cases in tests: null inputs, empty collections, zero/negative numeric inputs, and max/limit values."

Proposed test addition
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
@@
     `@Test`
     `@DisplayName`("Given a key compared to null, when checking equality, then returns false")
     void equals_returnsFalse_whenComparedToNull() {
         PluginHookKey<TestHookA> key = PluginHookKeyImpl.create(TestHookA.class);
         assertNotEquals(null, key);
     }
+
+    `@Test`
+    `@DisplayName`("Given a null hook class, when creating a key, then throws NullPointerException")
+    void create_throwsNullPointerException_whenGivenNullHookClass() {
+        assertThrows(NullPointerException.class, () -> PluginHookKeyImpl.create(null));
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
@DisplayName("Given a hook class, when creating a key, then key is not null")
void create_returnsNonNullKey_whenGivenHookClass() {
PluginHookKey<TestHookA> key = PluginHookKeyImpl.create(TestHookA.class);
assertNotNull(key);
}
package com.diamonddagger590.mccore.registry.plugin;
import com.diamonddagger590.mccore.registry.PluginHookKey;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class PluginHookKeyImplTest {
`@Test`
`@DisplayName`("Given a hook class, when creating a key, then key is not null")
void create_returnsNonNullKey_whenGivenHookClass() {
PluginHookKey<TestHookA> key = PluginHookKeyImpl.create(TestHookA.class);
assertNotNull(key);
}
`@Test`
`@DisplayName`("Given a key compared to null, when checking equality, then returns false")
void equals_returnsFalse_whenComparedToNull() {
PluginHookKey<TestHookA> key = PluginHookKeyImpl.create(TestHookA.class);
assertNotEquals(null, key);
}
`@Test`
`@DisplayName`("Given a null hook class, when creating a key, then throws NullPointerException")
void create_throwsNullPointerException_whenGivenNullHookClass() {
assertThrows(NullPointerException.class, () -> PluginHookKeyImpl.create(null));
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/diamonddagger590/mccore/registry/plugin/PluginHookKeyImplTest.java`
around lines 25 - 30, Add a new test method to the PluginHookKeyImplTest class
that tests the null-input edge case for the PluginHookKeyImpl.create() factory
method. The test should pass null as the hook class parameter and verify that
the method throws the expected exception (such as NullPointerException or
IllegalArgumentException) that aligns with the `@NotNull` contract on the class
parameter. Use an appropriate assertion like assertThrows to capture and
validate the exception behavior.

Source: Coding guidelines

Comment on lines +27 to +127
@Test
@DisplayName("Given a UUID and key, when creating a cache key, then accessors return the same values")
void accessors_returnCorrectValues_whenKeyIsCreated() {
StatisticCacheKey cacheKey = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertEquals(UUID_A, cacheKey.uuid());
assertEquals(KEY_KILLS, cacheKey.key());
}

@Test
@DisplayName("Given two cache keys with the same UUID and key, when comparing, then they are equal")
void equals_returnsTrue_whenBothFieldsMatch() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertEquals(key1, key2);
}

@Test
@DisplayName("Given two cache keys with different UUIDs, when comparing, then they are not equal")
void equals_returnsFalse_whenUuidsDiffer() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_KILLS);
assertNotEquals(key1, key2);
}

@Test
@DisplayName("Given two cache keys with different NamespacedKeys, when comparing, then they are not equal")
void equals_returnsFalse_whenNamespacedKeysDiffer() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_A, KEY_DEATHS);
assertNotEquals(key1, key2);
}

@Test
@DisplayName("Given two cache keys with completely different fields, when comparing, then they are not equal")
void equals_returnsFalse_whenBothFieldsDiffer() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_DEATHS);
assertNotEquals(key1, key2);
}

@Test
@DisplayName("Given two equal cache keys, when comparing hash codes, then they are equal")
void hashCode_isSame_whenKeysAreEqual() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertEquals(key1.hashCode(), key2.hashCode());
}

@Test
@DisplayName("Given two different cache keys, when comparing hash codes, then they differ")
void hashCode_differs_whenKeysAreNotEqual() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_DEATHS);
assertNotEquals(key1.hashCode(), key2.hashCode());
}

@Test
@DisplayName("Given a cache key, when calling toString, then it contains uuid and key info")
void toString_containsFieldInfo_whenCalled() {
StatisticCacheKey cacheKey = new StatisticCacheKey(UUID_A, KEY_KILLS);
String str = cacheKey.toString();
assertNotNull(str);
assertEquals(true, str.contains(UUID_A.toString()));
}

@Test
@DisplayName("Given a cache key compared to itself, when checking equality, then returns true")
void equals_returnsTrue_whenComparedToSelf() {
StatisticCacheKey cacheKey = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertEquals(cacheKey, cacheKey);
}

@Test
@DisplayName("Given a cache key compared to null, when checking equality, then returns false")
void equals_returnsFalse_whenComparedToNull() {
StatisticCacheKey cacheKey = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertNotEquals(null, cacheKey);
}

@Test
@DisplayName("Given a cache key used as a map key, when retrieving by equal key, then value is found")
void cacheKey_worksAsMapKey_whenUsedInHashMap() {
Map<StatisticCacheKey, String> map = new HashMap<>();
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
map.put(key1, "value");

StatisticCacheKey key2 = new StatisticCacheKey(UUID_A, KEY_KILLS);
assertEquals("value", map.get(key2));
}

@Test
@DisplayName("Given a cache key used as a map key, when retrieving by different key, then value is not found")
void cacheKey_returnsNull_whenMapKeyDoesNotMatch() {
Map<StatisticCacheKey, String> map = new HashMap<>();
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
map.put(key1, "value");

StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_KILLS);
assertEquals(null, map.get(key2));
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null-input edge-case tests for StatisticCacheKey construction.

The suite misses required null-input edge cases for uuid and key, which leaves the non-null contract unverified.

Suggested tests
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
@@
     `@Test`
     `@DisplayName`("Given a cache key used as a map key, when retrieving by different key, then value is not found")
     void cacheKey_returnsNull_whenMapKeyDoesNotMatch() {
         Map<StatisticCacheKey, String> map = new HashMap<>();
         StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
         map.put(key1, "value");

         StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_KILLS);
         assertEquals(null, map.get(key2));
     }
+
+    `@Test`
+    `@DisplayName`("Given a null UUID, when creating a cache key, then throws NullPointerException")
+    void constructor_throwsNullPointerException_whenUuidIsNull() {
+        assertThrows(NullPointerException.class, () -> new StatisticCacheKey(null, KEY_KILLS));
+    }
+
+    `@Test`
+    `@DisplayName`("Given a null NamespacedKey, when creating a cache key, then throws NullPointerException")
+    void constructor_throwsNullPointerException_whenNamespacedKeyIsNull() {
+        assertThrows(NullPointerException.class, () -> new StatisticCacheKey(UUID_A, null));
+    }
 }

As per coding guidelines, "Cover edge cases in tests: null inputs, empty collections, zero/negative numeric inputs, and max/limit values".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/diamonddagger590/mccore/statistic/cache/StatisticCacheKeyTest.java`
around lines 27 - 127, Add two test methods to verify null-input edge cases for
the StatisticCacheKey constructor. Create a test that attempts to instantiate
StatisticCacheKey with a null uuid parameter while providing a valid key, and
another test that attempts to instantiate it with a valid uuid but a null key
parameter. These tests should verify that the constructor properly rejects null
inputs and throws an appropriate exception (such as NullPointerException or
IllegalArgumentException) to enforce the non-null contract for both the uuid and
key fields.

Source: Coding guidelines

Comment on lines +76 to +80
@DisplayName("Given two different cache keys, when comparing hash codes, then they differ")
void hashCode_differs_whenKeysAreNotEqual() {
StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_DEATHS);
assertNotEquals(key1.hashCode(), key2.hashCode());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In Java, does the hashCode contract require unequal objects to have different hash codes?

💡 Result:

No, the Java hashCode contract does not require unequal objects to have different hash codes [1][2]. According to the official Java documentation for the Object class, the general contract of the hashCode method specifies three main rules [1][2]: 1. Consistency: The method must consistently return the same integer during an execution of an application, provided no information used in equals comparisons is modified [1][2]. 2. Equality: If two objects are equal according to the equals method, they must produce the same hash code [1][2]. 3. Inequality (The "No Requirement" Rule): It is not required that unequal objects produce distinct hash codes [1][2]. While it is not a requirement to have distinct hash codes for unequal objects, the documentation notes that producing distinct results (a uniform distribution) is beneficial because it can improve the performance of hash tables [1][2]. Instances where unequal objects share the same hash code are commonly referred to as "hash collisions" and are a standard consideration in the design of hashing algorithms [3].

Citations:


Fix test assertion to match Java hashCode contract.

The assertion assertNotEquals(key1.hashCode(), key2.hashCode()) tests an invalid contract. Java's hashCode specification only requires consistency and compatibility with equals(); unequal objects are explicitly allowed to share hash codes. Hash collisions are normal and expected.

Suggested fix
-    `@DisplayName`("Given two different cache keys, when comparing hash codes, then they differ")
-    void hashCode_differs_whenKeysAreNotEqual() {
-        StatisticCacheKey key1 = new StatisticCacheKey(UUID_A, KEY_KILLS);
-        StatisticCacheKey key2 = new StatisticCacheKey(UUID_B, KEY_DEATHS);
-        assertNotEquals(key1.hashCode(), key2.hashCode());
+    `@DisplayName`("Given a cache key, when calling hashCode repeatedly, then it is consistent")
+    void hashCode_isConsistent_whenCalledRepeatedly() {
+        StatisticCacheKey key = new StatisticCacheKey(UUID_A, KEY_KILLS);
+        assertEquals(key.hashCode(), key.hashCode());
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/diamonddagger590/mccore/statistic/cache/StatisticCacheKeyTest.java`
around lines 76 - 80, The test method hashCode_differs_whenKeysAreNotEqual()
incorrectly asserts that unequal StatisticCacheKey objects must have different
hash codes, which violates Java's hashCode contract. Java only requires that
equal objects have equal hash codes, not the reverse. Replace this test with one
that validates the actual contract: create two StatisticCacheKey objects with
identical parameters (same UUID and KEY values), then use assertEquals() to
assert that their hash codes are equal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants