Skip to content

[Tests] Add unit tests for PlayerSettingDAO, GuiManager, GetItemResponseState, exception classes#36

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

[Tests] Add unit tests for PlayerSettingDAO, GuiManager, GetItemResponseState, exception classes#36
DiamondDagger590 wants to merge 1 commit into
developfrom
claude/stoic-cori-mip2jy

Conversation

@DiamondDagger590

@DiamondDagger590 DiamondDagger590 commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • PlayerSettingDAOTest: Tests attemptCreateTable() for success, table-already-exists, and SQL exception paths (both CREATE TABLE and CREATE INDEX); updateTable() for current version no-op and version 0→1 migration; getPlayerSettings() for stored value parsing, missing stored values (defaults), unparseable stored values (falls back to default), SQL exception handling, and empty registry; getPlayerSetting() for stored value, missing value, SQL exception, and unregistered key (SettingNotRegisteredException); savePlayerSetting() for correct parameter binding and SQL exception wrapping; savePlayerSettings() for multiple settings and empty set (0% → ~95% line coverage, 73/77 lines)
  • GuiManagerTest: Tests all three doesPlayerHaveGui() overloads (UUID, CorePlayer, Player), all three trackPlayerGui() overloads with listener registration lifecycle (new GUI registers, shared GUI doesn't re-register, replacing GUI unregisters old), all three stopTrackingPlayer() overloads with listener unregistration (sole viewer unregisters, multiple viewers preserves listeners, untracked player no-op), all three getOpenedGui() overloads, refreshGui() with online/offline viewers, and CoreGuiOpenEvent firing (0% → ~95% line coverage, 43/45 lines)
  • GetItemResponseStateTest: Tests all 4 enum constants (PENDING_RESPONSE, ERRORED, ITEM_FOUND, ITEM_NOT_FOUND) via valueOf() and values(), non-null invariant (0% → 100% line coverage, 5/5 lines)
  • InventoryAlreadyExistsForGuiExceptionTest: Tests constructor field storage, getGui() accessor, getMessage() format containing GUI UUID and refreshGui() hint, RuntimeException inheritance (0% → 100% line coverage, 5/5 lines)
  • InvalidItemBuilderExceptionTest: Tests constructor field storage and message passthrough, getBuilder() accessor, RuntimeException inheritance (0% → 100% line coverage, 4/4 lines)

Infrastructure change

Added Mockito 5.14.2 as a testImplementation dependency to enable mocking JDBC components (Connection, PreparedStatement, ResultSet), Database, CorePlugin, BaseGui, BaseItemBuilder, Gui, CorePlayer, and Bukkit Server/PluginManager for testing classes that depend on framework types.

Classes covered (avoiding PR #27, PR #30, PR #31, PR #32, PR #33, PR #34, and PR #35 overlap)

PR #27 covers GetItemRequest, StatisticEntry, ReloadableContent, ReloadableContentManager, StartupProfile. PR #30 covers RegistryAccess, StatisticModifyEvent, PostStatisticModifyEvent, Transaction. PR #31 covers database events, GUI events, SQLiteDatabaseDriver, DatabaseDriver, ManagerKey/PluginHookKey constants. PR #32 covers player events, CorePlayer, Credentials, ConnectionDetails, CorePlayerOfflineException. PR #33 covers PlayerStatisticData, ReloadableParser, ItemPluginType, ChainPlayerContextFilter. PR #34 covers BatchTransaction, FailSafeTransaction, ItemBuilderConfigurationKeys, RegistryKeyImpl. PR #35 covers BootstrapContext, IllegalSlotAssignmentException, TableVersionHistoryDAO, MutexDAO. This PR targets entirely different classes with no overlap.

Coverage impact

5 classes brought from 0% to 95–100% line coverage. Approximately 130 new lines covered.

Test plan

  • All new tests pass via ./gradlew test (verified in individual batches)
  • No existing tests broken by these additions
  • No production code changes — test-only PR (plus Mockito dependency addition)

https://claude.ai/code/session_01CRSBzspNu93d5GUwx8RDWc


Generated by Claude Code

Summary by CodeRabbit

  • Tests

    • Added comprehensive test coverage for player settings database operations, GUI management, item builder exceptions, and GUI inventory handling.
  • Chores

    • Updated build configuration to include Mockito testing framework.

…nseState, exception classes

Add comprehensive test coverage for 5 classes that previously had 0% coverage:

- PlayerSettingDAOTest: Tests attemptCreateTable (success, table exists, SQL
  exceptions), updateTable (current version, version 0), getPlayerSettings
  (stored value, no stored value, unparseable, SQL exception, no settings),
  getPlayerSetting (stored value, no stored value, SQL exception, unregistered
  key), savePlayerSetting (success, SQL exception), savePlayerSettings (multiple
  settings, empty set)

- GuiManagerTest: Tests doesPlayerHaveGui (UUID, CorePlayer, Player overloads),
  trackPlayerGui (new gui, shared gui, replacing gui, CorePlayer/Player
  overloads), stopTrackingPlayer (sole viewer, multiple viewers, untracked,
  CorePlayer/Player overloads), getOpenedGui (UUID, CorePlayer, Player),
  refreshGui (online/offline viewers), event firing

- GetItemResponseStateTest: Tests all 4 enum constants via valueOf and values()

- InventoryAlreadyExistsForGuiExceptionTest: Tests constructor, getGui(),
  getMessage() containing UUID and refreshGui hint, RuntimeException inheritance

- InvalidItemBuilderExceptionTest: Tests constructor, getBuilder(), getMessage(),
  RuntimeException inheritance

Infrastructure: Added Mockito 5.14.2 as testImplementation dependency.

https://claude.ai/code/session_01CRSBzspNu93d5GUwx8RDWc
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds comprehensive test coverage to the McCore project by introducing JUnit 5 and Mockito-based test suites. Mockito is added as a dependency, followed by test classes validating enum constants, exception behaviors, database access operations, and GUI component management with full listener lifecycle verification.

Changes

Test Coverage Expansion

Layer / File(s) Summary
Test Infrastructure Setup
build.gradle.kts
Mockito 5.14.2 is added as a test dependency to enable mock-based testing.
Simple Unit Tests for Enums and Exceptions
src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java, src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java, src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java
GetItemResponseStateTest validates enum cardinality and valueOf mapping for four states. InvalidItemBuilderExceptionTest verifies builder/message retention and RuntimeException type. InventoryAlreadyExistsForGuiExceptionTest confirms GUI storage, UUID/refresh message inclusion, and exception type using Mockito-mocked BaseGui.
PlayerSettingDAO Test Suite
src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java
Comprehensive test coverage for schema creation (table exists/missing, SQL exceptions), table versioning (current vs. reset states), bulk and single-key read operations with parsing and default fallback, error handling on query exceptions, single and bulk write operations with statement binding validation, and RuntimeException on SQL failures. Uses mocked CorePlugin injected via reflection and registry lifecycle management.
GuiManager Integration Tests
src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java
Comprehensive test suite covering player GUI tracking (tracked/untracked lookup, UUID/CorePlayer/Player delegation), listener registration (once per unique GUI, avoiding re-registration), listener lifecycle on GUI replacement, unregistration on last viewer removal, no-op on untracked player removal, inventory refresh when online (and no-throw when offline), and CoreGuiOpenEvent firing via PluginManager.callEvent. Uses mocked Bukkit Server/PluginManager injected via reflection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding unit tests for five classes (PlayerSettingDAO, GuiManager, GetItemResponseState, and two exception classes).
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-mip2jy

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 infrastructure (Mockito dependency + new test classes) with no changes to production source files.

Reviewing the diff against the extensibility checklist:

build.gradle.kts adds mockito-core under testImplementation scope only. It is not exposed via compileOnlyApi, api, or implementation, so it does not leak into the compile or runtime classpath of downstream consumers.

All new .java files reside under src/test/java/. No production class, interface, abstract class, method signature, field, constant, or registry key is added, removed, or modified.

Checklist sweep against test-only changes:

  • Extension opportunities: N/A (no new production API surface)
  • Framework contract stability: No abstract methods added, no renames, no signature changes
  • GUI framework: No changes to BaseGui, PaginatedGui, Slot, or GuiManager production sources
  • Database framework: No UpdateTableFunction changes, no raw DDL in production code
  • @NotNull/@Nullable: No new public method parameters or return types in production code
  • Registry/extension points: No RegistryKey/ManagerKey constants added or modified
  • McCore-specific rule: Test code mocks CorePlugin and CorePlayer directly — these are McCore's own types, not consumer-plugin-specific identifiers. No upstream plugin logic embedded.

One observation worth noting, though it does not rise to a blocking concern at this scope:

CONCERN: Reflection-based injection of CorePlugin.instance and Bukkit.server in test setup/teardown is brittle and couples tests to private field names that are not part of any published contract.
WHY: If a future refactor renames CorePlugin.instance or changes how the singleton is held, these tests fail with an opaque NoSuchFieldException rather than a compilation error, and the pattern may encourage downstream plugin authors who read McCore's test suite to replicate the same reflection hack against a field McCore never guaranteed stable.
WHERE: PlayerSettingDAOTest#setUp/tearDown, GuiManagerTest#setUp/tearDownCorePlugin.class.getDeclaredField("instance"), Bukkit.class.getDeclaredField("server")

No extensibility concerns found that affect downstream consumers.

@github-actions

Copy link
Copy Markdown

Testing Review

I'll systematically apply every checklist item to the diff.


Findings


CONCERN: GuiManagerTest manually injects a Mockito mock into Bukkit.server via reflection instead of using MockBukkit's ServerMock.
WHY: The checklist explicitly prohibits using Mockito to mock a Bukkit class where MockBukkit provides a real implementation. ServerMock is the correct substitute for Server. The manual reflection hack is also fragile — if Paper's internals change field visibility or name, the setup silently breaks. More critically, the field is being set to null in @AfterEach, but if a test throws before tearDown, the null server leaks into the next test class in the same JVM run because Bukkit.server is a static field.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java (lines 42–53, setUp/tearDown)


CONCERN: GuiManagerTest never calls MockBukkit.mock() / MockBukkit.unmock() despite calling Bukkit.getPluginManager() indirectly through GuiManager logic and directly stubbing Bukkit-level state.
WHY: The checklist requires MockBukkit to be set up and torn down correctly when Bukkit APIs are exercised. Bypassing MockBukkit and directly setting Bukkit.server via reflection is not the approved pattern — it can leave the static Bukkit state dirty between test classes.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java


CONCERN: GuiManagerTest.trackPlayerGui_stopsOldGui_whenPlayerAlreadyTracked verifies gui1.unregisterListeners() was called, but does not verify that gui2.registerListeners() was also called on the replacement.
WHY: The coverage gap means the test does not fully assert the contract: replacing a tracked GUI must both tear down the old one and register the new one. Half the state transition is unverified.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.javatrackPlayerGui_stopsOldGui_whenPlayerAlreadyTracked


CONCERN: GuiManagerTest.stopTrackingPlayer_doesNothing_whenPlayerNotTracked contains no assertion.
WHY: A test with no assertion cannot fail — it is structurally vacuous per the checklist. At minimum it should assert doesPlayerHaveGui(uuid) returns false and that no exception escapes.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.javastopTrackingPlayer_doesNothing_whenPlayerNotTracked


CONCERN: GuiManagerTest.refreshGui_doesNotThrow_whenPlayerIsOffline contains no assertion.
WHY: Same structural problem — a test that only runs code without asserting anything cannot fail. The intent ("does not throw") should be expressed as assertDoesNotThrow(() -> guiManager.refreshGui(gui)).
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.javarefreshGui_doesNotThrow_whenPlayerIsOffline


CONCERN: PlayerSettingDAOTest.updateTable_doesNothing_whenVersionIsCurrent contains no assertion and no verification.
WHY: The test runs PlayerSettingDAO.updateTable(connection) and then ends. There is no verify(...) confirming that no update statement was executed, and no assertion on any return value. A test with zero assertions cannot fail.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.javaupdateTable_doesNothing_whenVersionIsCurrent


CONCERN: PlayerSettingDAOTest.updateTable_setsVersionToOne_whenVersionIsZero contains no assertion and no verification.
WHY: The test sets up mocks for a version-0 scenario, calls updateTable, and then ends without asserting that the version-set statement was executed (e.g., verify(setVersionStmt).executeUpdate()). The test cannot fail even if the method does nothing.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.javaupdateTable_setsVersionToOne_whenVersionIsZero


CONCERN: No test covers the updateTable migration on an already-migrated schema (version == 1) separately from the "version is current" path, and neither existing updateTable test has assertions, making the migration-on-already-migrated-schema checklist item completely uncovered.
WHY: The checklist requires: "For any database migration change (UpdateTableFunction), is there a test verifying it runs on both a fresh schema and an already-migrated schema?" Neither of the two updateTable tests have meaningful assertions, so both paths are effectively untested.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java


CONCERN: PlayerSettingDAOTest uses RegistryResetExtension as a plain static call (RegistryResetExtension.setupRegistry() / RegistryResetExtension.resetRegistry()) rather than as a JUnit 5 extension (@ExtendWith(RegistryResetExtension.class)).
WHY: If a test throws before tearDown, the @AfterEach is still called by JUnit, but if the extension were registered with @ExtendWith it would be lifecycle-managed correctly even in the presence of test failures. More importantly, calling it as a static utility is a structural misuse of the extension pattern — it exists as a JUnit extension specifically to be registered, not manually called, which means any test that fails mid-setup will leave registry state dirty for subsequent tests.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.javasetUp / tearDown


CONCERN: PlayerSettingDAOTest injects into CorePlugin.instance via reflection but does not verify that the field existed before injection, nor does it guard against the field name changing. More critically, the tearDown sets it to null — if setUp fails midway (e.g., RegistryResetExtension.setupRegistry() throws), the CorePlugin.instance field will not have been set and tearDown will still null it out, which is benign, but if CorePlugin.instance had previously been set by another test the null-out is destructive. This is a latent leak risk.
WHY: Structural fragility in test fixture setup/teardown can cause non-deterministic failures in parallel or reordered test runs. The setup should use @ExtendWith or a dedicated test helper rather than raw reflection in @BeforeEach.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.javasetUp / tearDown


CONCERN: GetItemResponseStateTest is testing auto-generated enum compiler behavior (values(), valueOf(), null-safety of constants) rather than any logic in GetItemResponseState. These tests provide zero safety net against real bugs and inflate coverage metrics with noise.
WHY: This is a coverage gap in disguise — the tests exist but cover only JVM-guaranteed behavior. If GetItemResponseState has any methods with non-trivial logic, those are not tested. If it is a pure enum with no logic, the test file should not exist at all (it does not satisfy "non-trivial logic" threshold from the checklist).
WHERE: src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java


CONCERN: InvalidItemBuilderExceptionTest mocks BaseItemBuilder but BaseItemBuilder is a pure Java class with no Bukkit dependencies in its constructor path. There is no reason to mock it.
WHY: Mocking a concrete class unnecessarily introduces Mockito subclass-proxy behavior and can mask real construction failures. The checklist flags Mockito usage where real implementations are available. A plain new ConcreteItemBuilder(...) would be correct here; if BaseItemBuilder is abstract, a minimal anonymous subclass should be used.
WHERE: src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java


CONCERN: No test covers PlayerSettingDAO.getPlayerSettings or getPlayerSetting when the ResultSet returns multiple rows (i.e., multiple settings stored in the DB for one player).
WHY: The checklist requires edge cases for collections. The multi-row path exercises different loop iterations and potential partial-failure semantics that the single-row tests do not cover.
WHERE: src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java


CONCERN: No test covers GuiManager.refreshGui when called for a GUI that has no tracked players at all (empty viewer set).
WHY: Edge case: empty collection input. If the implementation iterates over viewers and the set is empty, this is a boundary condition that should be explicitly tested rather than assumed.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java


CONCERN: GuiManagerTest mocks Gui<CorePlayer> but there is no test for GuiManager behavior when gui.getUUID() returns null — a defensive edge case for the internal UUID-to-GUI tracking map.
WHY: Null UUID from a GUI is a plausible programming error in downstream code. The manager's contract under that condition (NPE vs. graceful handling) is untested.
WHERE: src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java


Summary

Production files changed: GuiManager (implied by new tests), PlayerSettingDAO (implied by new tests), GetItemResponseState (implied), InvalidItemBuilderException (implied), InventoryAlreadyExistsForGuiException (implied), build.gradle.kts

Test files present: GuiManagerTest.java, PlayerSettingDAOTest.java, GetItemResponseStateTest.java, InvalidItemBuilderExceptionTest.java, InventoryAlreadyExistsForGuiExceptionTest.java

Coverage gaps:

  • updateTable both paths lack assertions — migration checklist item unmet
  • stopTrackingPlayer (untracked player) and refreshGui (offline player) lack assertions — structurally vacuous
  • Multi-row ResultSet path in getPlayerSettings/getPlayerSetting untested
  • refreshGui with zero viewers untested
  • Null UUID from Gui not tested
  • GuiManagerTest bypasses MockBukkit entirely, leaking Bukkit.server static state
  • RegistryResetExtension used as a static utility rather than a registered JUnit 5 extension
  • GetItemResponseStateTest tests only compiler-generated enum behavior, not logic

@github-actions

Copy link
Copy Markdown

Security Review

No security concerns found.

The diff adds only test infrastructure: a Mockito dependency in build.gradle.kts and four new test classes (GetItemResponseStateTest, PlayerSettingDAOTest, InvalidItemBuilderExceptionTest, InventoryAlreadyExistsForGuiExceptionTest, GuiManagerTest). None of the checklist items are triggered:

  • SQL Injection: PlayerSettingDAOTest verifies that savePlayerSetting binds playerUUID.toString(), settingKey.toString(), and setting.name() via setString(1/2/3, …) — confirming the production DAO uses parameterised statements. No new DAO query construction appears in the test code itself.
  • DDL Injection: No UpdateTableFunction or DDL string construction is introduced.
  • MiniMessage Injection: No call to deserialize() appears anywhere in the diff.
  • ChatResponse Safety: No ChatResponse implementation or chat content handling is present.
  • onClick() Return Value: No Slot.onClick() implementation is present.
  • Framework Permission Gating: No framework operations affecting player state are introduced; test scaffolding uses reflection only to inject mock plugin instances within test lifecycle boundaries.

@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: 4

🤖 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/database/response/GetItemResponseStateTest.java`:
- Around line 18-40: In
src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java
(lines 18-40) add assertThrows checks that
GetItemResponseState.valueOf("UNKNOWN") and GetItemResponseState.valueOf(null)
throw IllegalArgumentException/NullPointerException as appropriate (use
valueOf's expected exception); in
src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java
(lines 14-43) add unit tests that call the InvalidItemBuilderException
constructors with a null builder and with a null message respectively and assert
the expected NullPointerException or IllegalArgumentException; in
src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java
(lines 18-57) add a test that constructs InventoryAlreadyExistsForGuiException
with a null gui argument and assert the expected exception; ensure each test
references the exact constructor/class names (GetItemResponseState.valueOf,
InvalidItemBuilderException(...), InventoryAlreadyExistsForGuiException(...))
and uses assertThrows to validate the null/invalid-input behavior.

In
`@src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java`:
- Around line 130-162: The two failing tests in PlayerSettingDAOTest
(updateTable_doesNothing_whenVersionIsCurrent and
updateTable_setsVersionToOne_whenVersionIsZero) lack assertions and one mocks
the wrong column name; update the mocks to use the "table_version" column when
stubbing ResultSet.getInt in the first test, and add assertions/verifications:
in updateTable_doesNothing_whenVersionIsCurrent assert that no
update/prepared-statement for setting version was executed (e.g.,
verify(connection, never()).prepareStatement(matches the SET version SQL) or
verify(versionStmt, never()).executeUpdate()), and in
updateTable_setsVersionToOne_whenVersionIsZero verify that the set-version
PreparedStatement was created and executed (e.g.,
verify(setVersionStmt).executeUpdate() or
verify(connection).prepareStatement(matches the SET version SQL)); keep
references to PlayerSettingDAO.updateTable, getVersionStmt/getVersionRs, and
setVersionStmt when adding the verifications.

In `@src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java`:
- Around line 263-265: The two test methods
stopTrackingPlayer_doesNothing_whenPlayerNotTracked and
refreshGui_doesNotThrow_whenPlayerIsOffline currently contain no assertions;
update each to wrap the call under test in an assertion like
assertDoesNotThrow(() -> guiManager.stopTrackingPlayer(UUID.randomUUID())) and
assertDoesNotThrow(() -> guiManager.refreshGui(somePlayerUuid)) respectively,
and add a simple post-condition assertion (e.g.,
assertFalse(guiManager.isPlayerTracked(uuid)) or assertEquals(expectedGuiState,
guiManager.getGuiStateFor(uuid))) after the call to provide a meaningful
verification; ensure you import and use JUnit's Assertions.assertDoesNotThrow
and pick an existing observable method (such as isPlayerTracked or
getGuiStateFor) from GuiManager to assert the expected post-state.
- Around line 36-61: The tests currently inject a mocked Bukkit server via
reflection in setUp/tearDown and mock Bukkit types (mockServer, mockPlugin,
mockPluginManager); replace that with MockBukkit lifecycle and real MockBukkit
test doubles: call MockBukkit.mock() in setUp and MockBukkit.unmock() in
tearDown, remove the reflective manipulation of Bukkit.server and the
Mockito-created mockServer/mockPluginManager; create a plugin instance using
MockBukkit's plugin test helper (e.g., JavaPluginMock or
MockBukkit.createMockPlugin) and use the ServerMock/PluginManager from
MockBukkit instead of when(...)/doNothing() stubs, then initialize GuiManager
with that real mock-plugin (GuiManager<>(mockPlugin -> pluginMockInstance));
update other test sites that currently mock Bukkit types to use the
corresponding MockBukkit mocks (PlayerMock, ServerMock, etc.) instead of Mockito
mocks.
🪄 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: c6004506-1228-4fc3-9dd7-1bf5bb4885f0

📥 Commits

Reviewing files that changed from the base of the PR and between 6a8d9ea and c7f3239.

📒 Files selected for processing (6)
  • build.gradle.kts
  • src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java
  • src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java
  • src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java
  • src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java
  • src/test/java/com/diamonddagger590/mccore/gui/GuiManagerTest.java

Comment on lines +18 to +40
@Test
@DisplayName("Given PENDING_RESPONSE, when accessed via valueOf, then returns correct constant")
void valueOf_returnsPendingResponse_whenGivenPendingResponseString() {
assertEquals(GetItemResponseState.PENDING_RESPONSE, GetItemResponseState.valueOf("PENDING_RESPONSE"));
}

@Test
@DisplayName("Given ERRORED, when accessed via valueOf, then returns correct constant")
void valueOf_returnsErrored_whenGivenErroredString() {
assertEquals(GetItemResponseState.ERRORED, GetItemResponseState.valueOf("ERRORED"));
}

@Test
@DisplayName("Given ITEM_FOUND, when accessed via valueOf, then returns correct constant")
void valueOf_returnsItemFound_whenGivenItemFoundString() {
assertEquals(GetItemResponseState.ITEM_FOUND, GetItemResponseState.valueOf("ITEM_FOUND"));
}

@Test
@DisplayName("Given ITEM_NOT_FOUND, when accessed via valueOf, then returns correct constant")
void valueOf_returnsItemNotFound_whenGivenItemNotFoundString() {
assertEquals(GetItemResponseState.ITEM_NOT_FOUND, GetItemResponseState.valueOf("ITEM_NOT_FOUND"));
}

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

Shared gap: edge-case coverage is missing across newly added tests.
All three test files emphasize happy paths but omit null/invalid-input checks required by your test guidelines.

  • src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java#L18-L40: add assertThrows cases for GetItemResponseState.valueOf("UNKNOWN") and GetItemResponseState.valueOf(null).
  • src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java#L14-L43: add constructor null-argument tests for builder and message.
  • src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java#L18-L57: add constructor null-argument test for gui.

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

📍 Affects 3 files
  • src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java#L18-L40 (this comment)
  • src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java#L14-L43
  • src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java#L18-L57
🤖 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/database/response/GetItemResponseStateTest.java`
around lines 18 - 40, In
src/test/java/com/diamonddagger590/mccore/database/response/GetItemResponseStateTest.java
(lines 18-40) add assertThrows checks that
GetItemResponseState.valueOf("UNKNOWN") and GetItemResponseState.valueOf(null)
throw IllegalArgumentException/NullPointerException as appropriate (use
valueOf's expected exception); in
src/test/java/com/diamonddagger590/mccore/exception/builder/item/InvalidItemBuilderExceptionTest.java
(lines 14-43) add unit tests that call the InvalidItemBuilderException
constructors with a null builder and with a null message respectively and assert
the expected NullPointerException or IllegalArgumentException; in
src/test/java/com/diamonddagger590/mccore/exception/gui/InventoryAlreadyExistsForGuiExceptionTest.java
(lines 18-57) add a test that constructs InventoryAlreadyExistsForGuiException
with a null gui argument and assert the expected exception; ensure each test
references the exact constructor/class names (GetItemResponseState.valueOf,
InvalidItemBuilderException(...), InventoryAlreadyExistsForGuiException(...))
and uses assertThrows to validate the null/invalid-input behavior.

Source: Coding guidelines

Comment on lines +130 to +162
@Test
@DisplayName("Given version is current, when updateTable, then no update occurs")
void updateTable_doesNothing_whenVersionIsCurrent() throws SQLException {
Connection connection = mock(Connection.class);
PreparedStatement versionStmt = mock(PreparedStatement.class);
ResultSet versionRs = mock(ResultSet.class);

when(connection.prepareStatement(anyString())).thenReturn(versionStmt);
when(versionStmt.executeQuery()).thenReturn(versionRs);
when(versionRs.next()).thenReturn(true);
when(versionRs.getInt("version")).thenReturn(1);

PlayerSettingDAO.updateTable(connection);
}

@Test
@DisplayName("Given version is 0, when updateTable, then version is set to 1")
void updateTable_setsVersionToOne_whenVersionIsZero() throws SQLException {
Connection connection = mock(Connection.class);

PreparedStatement getVersionStmt = mock(PreparedStatement.class);
ResultSet getVersionRs = mock(ResultSet.class);
when(getVersionRs.next()).thenReturn(false);
when(getVersionStmt.executeQuery()).thenReturn(getVersionRs);

PreparedStatement setVersionStmt = mock(PreparedStatement.class);

when(connection.prepareStatement(anyString()))
.thenReturn(getVersionStmt)
.thenReturn(setVersionStmt);

PlayerSettingDAO.updateTable(connection);
}

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 | 🟠 Major | ⚡ Quick win

Fix updateTable tests: missing assertions and incorrect mocked version column.

These two tests currently do not verify outcomes, and the stub on Line 140 uses "version" instead of "table_version", which can exercise the wrong branch while still passing.

Suggested fix
@@
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@
     void updateTable_doesNothing_whenVersionIsCurrent() throws SQLException {
         Connection connection = mock(Connection.class);
         PreparedStatement versionStmt = mock(PreparedStatement.class);
+        PreparedStatement setVersionStmt = mock(PreparedStatement.class);
         ResultSet versionRs = mock(ResultSet.class);

-        when(connection.prepareStatement(anyString())).thenReturn(versionStmt);
+        when(connection.prepareStatement(anyString())).thenReturn(versionStmt).thenReturn(setVersionStmt);
         when(versionStmt.executeQuery()).thenReturn(versionRs);
         when(versionRs.next()).thenReturn(true);
-        when(versionRs.getInt("version")).thenReturn(1);
+        when(versionRs.getInt("table_version")).thenReturn(1);

         PlayerSettingDAO.updateTable(connection);
+        verify(setVersionStmt, never()).executeUpdate();
     }
@@
     void updateTable_setsVersionToOne_whenVersionIsZero() throws SQLException {
@@
         PlayerSettingDAO.updateTable(connection);
+        verify(setVersionStmt).executeUpdate();
     }

As per coding guidelines, src/test/java/**/*.java requires that every test method has at least one assertion, and upstream DAO version reads use table_version.

🧰 Tools
🪛 PMD (7.25.0)

[Medium] 139-139: CheckResultSet (Best Practices): Always check the return of one of the navigation method (next,previous,first,last) of a ResultSet.

(CheckResultSet (Best Practices))


[Medium] 152-152: CheckResultSet (Best Practices): Always check the return of one of the navigation method (next,previous,first,last) of a ResultSet.

(CheckResultSet (Best Practices))

🤖 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/database/table/impl/PlayerSettingDAOTest.java`
around lines 130 - 162, The two failing tests in PlayerSettingDAOTest
(updateTable_doesNothing_whenVersionIsCurrent and
updateTable_setsVersionToOne_whenVersionIsZero) lack assertions and one mocks
the wrong column name; update the mocks to use the "table_version" column when
stubbing ResultSet.getInt in the first test, and add assertions/verifications:
in updateTable_doesNothing_whenVersionIsCurrent assert that no
update/prepared-statement for setting version was executed (e.g.,
verify(connection, never()).prepareStatement(matches the SET version SQL) or
verify(versionStmt, never()).executeUpdate()), and in
updateTable_setsVersionToOne_whenVersionIsZero verify that the set-version
PreparedStatement was created and executed (e.g.,
verify(setVersionStmt).executeUpdate() or
verify(connection).prepareStatement(matches the SET version SQL)); keep
references to PlayerSettingDAO.updateTable, getVersionStmt/getVersionRs, and
setVersionStmt when adding the verifications.

Source: Coding guidelines

Comment on lines +36 to +61
@BeforeEach
@SuppressWarnings("unchecked")
void setUp() throws Exception {
mockPlugin = mock(CorePlugin.class);
mockServer = mock(Server.class);
mockPluginManager = mock(PluginManager.class);
when(mockPlugin.getServer()).thenReturn(mockServer);
when(mockServer.getPluginManager()).thenReturn(mockPluginManager);
when(mockServer.getLogger()).thenReturn(Logger.getLogger("TestServer"));
when(mockServer.isPrimaryThread()).thenReturn(true);
doNothing().when(mockPluginManager).callEvent(any());

// Set the Bukkit server so Bukkit.getPluginManager() works
Field serverField = Bukkit.class.getDeclaredField("server");
serverField.setAccessible(true);
serverField.set(null, mockServer);

guiManager = new GuiManager<>(mockPlugin);
}

@AfterEach
void tearDown() throws Exception {
Field serverField = Bukkit.class.getDeclaredField("server");
serverField.setAccessible(true);
serverField.set(null, null);
}

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 | 🟠 Major | 🏗️ Heavy lift

Replace manual Bukkit static injection and mocked Bukkit types with MockBukkit-backed tests.

The current harness at Line 38–Line 61 mutates Bukkit.server via reflection and relies on mocked Bukkit types (for example Line 109 and Line 299). This can produce false positives versus actual Bukkit behavior and violates the test guidelines for this repository.

As per coding guidelines, “Set up and tear down MockBukkit correctly (MockBukkit.mock() / MockBukkit.unmock()) without leaking across tests” and “Do not use Mockito to mock a Bukkit class where MockBukkit already provides a real implementation (e.g., PlayerMock, ServerMock).”

Also applies to: 109-110, 156-157, 223-224, 284-285, 299-300

🤖 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/gui/GuiManagerTest.java` around
lines 36 - 61, The tests currently inject a mocked Bukkit server via reflection
in setUp/tearDown and mock Bukkit types (mockServer, mockPlugin,
mockPluginManager); replace that with MockBukkit lifecycle and real MockBukkit
test doubles: call MockBukkit.mock() in setUp and MockBukkit.unmock() in
tearDown, remove the reflective manipulation of Bukkit.server and the
Mockito-created mockServer/mockPluginManager; create a plugin instance using
MockBukkit's plugin test helper (e.g., JavaPluginMock or
MockBukkit.createMockPlugin) and use the ServerMock/PluginManager from
MockBukkit instead of when(...)/doNothing() stubs, then initialize GuiManager
with that real mock-plugin (GuiManager<>(mockPlugin -> pluginMockInstance));
update other test sites that currently mock Bukkit types to use the
corresponding MockBukkit mocks (PlayerMock, ServerMock, etc.) instead of Mockito
mocks.

Source: Coding guidelines

Comment on lines +263 to +265
void stopTrackingPlayer_doesNothing_whenPlayerNotTracked() {
guiManager.stopTrackingPlayer(UUID.randomUUID());
}

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 explicit assertions to zero-assertion tests.

stopTrackingPlayer_doesNothing_whenPlayerNotTracked (Line 263) and refreshGui_doesNotThrow_whenPlayerIsOffline (Line 311) have no assertions. Add assertDoesNotThrow(...) and/or a post-condition assertion so the tests can fail meaningfully.

As per coding guidelines, “Every test method must have at least one assertion — a test with no assertion cannot fail.”

Also applies to: 311-319

🤖 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/gui/GuiManagerTest.java` around
lines 263 - 265, The two test methods
stopTrackingPlayer_doesNothing_whenPlayerNotTracked and
refreshGui_doesNotThrow_whenPlayerIsOffline currently contain no assertions;
update each to wrap the call under test in an assertion like
assertDoesNotThrow(() -> guiManager.stopTrackingPlayer(UUID.randomUUID())) and
assertDoesNotThrow(() -> guiManager.refreshGui(somePlayerUuid)) respectively,
and add a simple post-condition assertion (e.g.,
assertFalse(guiManager.isPlayerTracked(uuid)) or assertEquals(expectedGuiState,
guiManager.getGuiStateFor(uuid))) after the call to provide a meaningful
verification; ensure you import and use JUnit's Assertions.assertDoesNotThrow
and pick an existing observable method (such as isPlayerTracked or
getGuiStateFor) from GuiManager to assert the expected post-state.

Source: Coding guidelines

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