Skip to content

[Tests] Improve item builder test coverage (BaseItemBuilder, SkullBuilder, SpawnerBuilder)#67

Open
DiamondDagger590 wants to merge 1 commit into
developfrom
claude/eager-thompson-uesj4h
Open

[Tests] Improve item builder test coverage (BaseItemBuilder, SkullBuilder, SpawnerBuilder)#67
DiamondDagger590 wants to merge 1 commit into
developfrom
claude/eager-thompson-uesj4h

Conversation

@DiamondDagger590

@DiamondDagger590 DiamondDagger590 commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • New BaseItemBuilderMockBukkitTest with 34 tests covering enchantment addition/removal, getPlainName, getPlainLore, setColor, setItemDamage, isEdible, builder conversions (success paths), withType, setTrim, copy constructor, and fluent API chaining
  • Enhanced SkullBuilderTest with 5 new tests covering withAudience(Audience) with/without UUID, hideSkullDynamicToolTip merge branch (existing tooltip data), and build verification with UUID and name+url textures
  • Enhanced SpawnerBuilderTest with 5 new tests exercising positive count/delay/range code paths and zero/negative count guard branches

Coverage Impact

Class Before After
BaseItemBuilder 65.0% 85.8%
SkullBuilder 67.0% 95.3%
SpawnerBuilder 72.0% 72.0%
Overall 81.0% 82.9%

SpawnerBuilder coverage is unchanged because MockBukkit does not fully implement BlockStateMeta/CreatureSpawner, so property-value assertions are not possible — tests use assertDoesNotThrow to exercise the code paths.

Notes

  • Uses assertDoesNotThrow patterns where MockBukkit's DataComponentTypes support is incomplete (enchantments, color, damage, trim)
  • Uses real DataComponentTypes assertions where MockBukkit supports them (PROFILE, TOOLTIP_DISPLAY, ITEM_NAME, CUSTOM_NAME, LORE)
  • Ran testing audit persona and addressed all valid findings (unused imports, misleading method names, vacuous assertions)

Test plan

  • All 2166+ existing tests pass
  • All new tests pass
  • Testing audit persona reviewed and findings addressed
  • ./gradlew test green

🤖 Generated with Claude Code

https://claude.ai/code/session_013TmAF3ehyVWsALSN2Ua7yY


Generated by Claude Code

Summary by CodeRabbit

  • Tests
    • Added comprehensive coverage for item builder behavior, including metadata, enchantments, colors, damage, lore, cloning, conversions, and fluent chaining.
    • Added tests for skull tooltip preservation and audience-based profile data.
    • Added validation that spawner configurations build successfully across varied numeric values.

…wnerBuilder

Improve test coverage for the item builder subsystem:
- BaseItemBuilder: new BaseItemBuilderMockBukkitTest covering enchantment
  addition/removal, getPlainName, getPlainLore, setColor, setItemDamage,
  isEdible, builder conversions, withType, setTrim, copy constructor, and
  fluent API chaining (coverage 65% -> 85.8%)
- SkullBuilder: add tests for withAudience(Audience) with/without UUID,
  hideSkullDynamicToolTip merge branch, build with UUID and name+url
  (coverage 67% -> 95.3%)
- SpawnerBuilder: add tests exercising positive count/delay/range and
  zero/negative count guard paths (coverage unchanged at 72% due to
  MockBukkit BlockStateMeta limitations)

Overall line coverage: 81.0% -> 82.9%

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

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds MockBukkit coverage for BaseItemBuilder, including fluent operations, item conversion, metadata, copying, and output behavior. Extends SkullBuilderTest and SpawnerBuilderTest with profile, tooltip, audience, and spawn-value cases.

Changes

Builder Test Coverage

Layer / File(s) Summary
Base builder behavior
src/test/java/com/diamonddagger590/mccore/builder/item/BaseItemBuilderMockBukkitTest.java
Adds lifecycle setup and tests for enchantments, names, lore, colors, damage, and edible materials.
Builder conversions and item output
src/test/java/com/diamonddagger590/mccore/builder/item/BaseItemBuilderMockBukkitTest.java
Tests specialized builder conversions, type handling, item stack output, trimming, copying, and fluent chaining.
Specialized builder edge cases
src/test/java/com/diamonddagger590/mccore/builder/item/impl/SkullBuilderTest.java, src/test/java/com/diamonddagger590/mccore/builder/item/impl/SpawnerBuilderTest.java
Covers SkullBuilder tooltip/profile behavior and SpawnerBuilder builds across positive, zero, and negative values.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 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 title clearly summarizes the main change: expanded item builder test coverage for BaseItemBuilder, SkullBuilder, and SpawnerBuilder.
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.
✨ 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/eager-thompson-uesj4h

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.

@github-actions

Copy link
Copy Markdown

Testing Review

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


Analysis

Production files changed (inferred from test targets)

  • BaseItemBuilder (enchantment, color, damage, lore, name, builder conversion methods)
  • SkullBuilder (hideSkullDynamicToolTip merge path, withAudience(Audience), withAudience(UUID), build)
  • SpawnerBuilder (build with various numeric inputs)

Test files present

  • BaseItemBuilderMockBukkitTest.java (new)
  • SkullBuilderTest.java (modified — additions only)
  • SpawnerBuilderTest.java (modified — additions only)

Findings


CONCERN: withAudience_setsUuid_whenAudienceHasUuid has no assertion that the UUID was actually applied to the builder's internal state or that the resulting ItemStack reflects it.
WHY: assertSame(builder, result) only verifies the fluent return value, not the behavior. The test cannot detect a regression where the UUID is silently dropped after being passed to withAudience(Audience). The parallel test build_setsProfile_whenAudienceUuidProvided uses withAudience(UUID) directly, bypassing the Audience code path entirely. There is no test that calls withAudience(Audience) and then build() and asserts that DataComponentTypes.PROFILE is set.
WHERE: SkullBuilderTest.java / SkullBuilder (withAudience(Audience)build() path)


CONCERN: withAudience_returnsSelf_whenAudienceHasNoUuid has no assertion about side effects — only that the builder is returned.
WHY: The test cannot catch a regression where the null-UUID path corrupts internal state (e.g., clears a previously-set UUID). It should assert that a subsequent build() either does not set PROFILE, or that pre-existing profile data is not disturbed.
WHERE: SkullBuilderTest.java / SkullBuilder (withAudience(Audience) null path)


CONCERN: Mockito is used to mock Audience even though PlayerMock (MockBukkit) implements Audience and has a real UUID.
WHY: The checklist explicitly prohibits mocking a Bukkit/Adventure class where a real implementation is available via MockBukkit. PlayerMock is an Audience with a stable UUID, making the Mockito mock redundant and fragile (it stubs internal Adventure API calls via getOrDefault).
WHERE: SkullBuilderTest.javawithAudience_setsUuid_whenAudienceHasUuid and withAudience_returnsSelf_whenAudienceHasNoUuid


CONCERN: All five SpawnerBuilder build_completesWithoutError_* tests use assertDoesNotThrow as their sole assertion.
WHY: assertDoesNotThrow cannot distinguish between a correct build and one that silently no-ops. None of the tests assert that the ItemStack actually has the expected BlockStateMeta state (spawn count, delay, range, entity type) after build() is called. Zero/negative count tests are described as "count is skipped" but never verify that the skip actually occurred (i.e., that the spawner's count field was not mutated).
WHERE: SpawnerBuilderTest.java — all five new test methods


CONCERN: asItemStack_appliesLore_whenStringLoreSet and asItemStack_appliesComponentLore_whenComponentLoreSet assert only assertNotNull(result).
WHY: A non-null ItemStack is trivially guaranteed for any non-AIR material. These tests cannot detect a regression where lore is silently dropped. They should assert on the actual lore data component of the result.
WHERE: BaseItemBuilderMockBukkitTest.javaAsItemStackIntegrationTests


CONCERN: No test verifies that setItemDamage with a negative value is handled correctly.
WHY: The checklist requires coverage of negative numeric inputs. setItemDamage(0) is covered, but setItemDamage(-1) is absent. If production code clamps, throws, or silently ignores negatives, there is no regression guard.
WHERE: BaseItemBuilderMockBukkitTest.javaSetItemDamageTests / BaseItemBuilder.setItemDamage


CONCERN: No test verifies that setItemDamage actually persists the damage value on the resulting ItemStack.
WHY: All three setItemDamage tests use only assertDoesNotThrow. They cannot detect a regression where the damage is computed but never written to the item.
WHERE: BaseItemBuilderMockBukkitTest.javaSetItemDamageTests / BaseItemBuilder.setItemDamage


CONCERN: setColor_completesWithoutError_whenNonColorableItem describes behavior ("no change") but does not assert it.
WHY: The display name says "no change" but the test body only calls assertDoesNotThrow. There is no assertion that the item's data is unmodified, so a regression that incorrectly mutates a non-colorable item would pass this test.
WHERE: BaseItemBuilderMockBukkitTest.javaSetColorTests


CONCERN: addEnchantment_appliesEnchantment_whenRegularItem does not assert that the enchantment is actually present on the resulting ItemStack.
WHY: assertDoesNotThrow(() -> builder.asItemStack()) only confirms no exception is thrown. A regression where the enchantment is accepted but silently dropped would pass this test. The test addEnchantment_doesNotApply_whenInvalidString correctly asserts isEmpty(); the success path should symmetrically assert the enchantment is present.
WHERE: BaseItemBuilderMockBukkitTest.javaEnchantmentAdditionTests


CONCERN: addEnchantment_preservesExistingEnchantments_whenAddingNew does not assert that either enchantment survives on the built item.
WHY: Same structural problem as above — only assertDoesNotThrow is used. A regression where the second addEnchantment call overwrites the first would not be caught.
WHERE: BaseItemBuilderMockBukkitTest.javaEnchantmentAdditionTests


CONCERN: withType_doesNotReplace_whenItemExists and withType_doesNotReplace_whenSingleArgAndItemExists assert on builder.getType() rather than on builder.asItemStack().getType().
WHY: If getType() reads from mutable internal state that asItemStack() overrides, these tests would pass while the built item is wrong. The assertion target should be the final produced ItemStack.
WHERE: BaseItemBuilderMockBukkitTest.javaWithTypeTests


CONCERN: RegistryResetExtension.setupRegistry() / resetRegistry() is called manually in @BeforeEach / @AfterEach in BaseItemBuilderMockBukkitTest rather than being declared as a JUnit 5 extension via @ExtendWith.
WHY: If a test throws before tearDown() completes, or if a future author adds a test that returns early, resetRegistry() may still be called — but the sequencing is fragile compared to the declarative @ExtendWith(RegistryResetExtension.class) pattern used elsewhere. More critically, if MockBukkit.unmock() fails (throws), resetRegistry() is skipped because there is no try/finally. This risks registry state leakage across test classes.
WHERE: BaseItemBuilderMockBukkitTest.javasetUp / tearDown


Summary

Production files changed: BaseItemBuilder, SkullBuilder, SpawnerBuilder

Test files present: BaseItemBuilderMockBukkitTest.java (new), SkullBuilderTest.java (modified), SpawnerBuilderTest.java (modified)

Coverage gaps:

  • withAudience(Audience)build()PROFILE present: not tested end-to-end
  • Null-UUID withAudience side-effect on subsequent build(): not tested
  • setItemDamage negative input: not tested
  • setItemDamage actual value persistence: no assertion
  • setColor no-op on non-colorable item: no assertion
  • Enchantment presence on built item (success path): no assertion
  • Multi-enchantment preservation: no assertion
  • Lore content on built item: no assertion
  • SpawnerBuilder state post-build(): no assertions in any of the five new tests

@github-actions

Copy link
Copy Markdown

Security Review

No security concerns found.

The diff is entirely test code (src/test/java/…). No production DAO methods, DDL migration functions, MiniMessage deserialization calls, ChatResponse logic, Slot.onClick() implementations, or permission-gated framework operations were added or modified. None of the checklist items apply to test-only changes.

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

🤖 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/builder/item/BaseItemBuilderMockBukkitTest.java`:
- Around line 301-333: Extend SetItemDamageTests with a test covering a negative
value passed to ItemBuilder.setItemDamage, asserting the documented behavior and
that the call completes without error. Follow the existing zero-damage test
structure and naming conventions, using a damageable DIAMOND_SWORD builder.
- Around line 80-95: Strengthen the affected tests so their assertions verify
the behavior promised by each `@DisplayName`, not merely absence of exceptions or
object identity. In addEnchantment_appliesStoredEnchantment_whenEnchantedBook
and addEnchantment_preservesExistingStoredEnchantments_whenAddingNew, inspect
EnchantmentStorageMeta.hasStoredEnchant for the expected stored enchantments.
Update the tests covering unchanged enchantments, no-change operations, and
damage clamping to assert the resulting state and clamped value. In the
copy-constructor test, verify the copied display name, placeholders, item flags,
and other configured state in addition to its type.
🪄 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: 5823f5b9-dcea-4021-b2f4-7b121a37d2e6

📥 Commits

Reviewing files that changed from the base of the PR and between ca6b94b and 5eafc47.

📒 Files selected for processing (3)
  • src/test/java/com/diamonddagger590/mccore/builder/item/BaseItemBuilderMockBukkitTest.java
  • src/test/java/com/diamonddagger590/mccore/builder/item/impl/SkullBuilderTest.java
  • src/test/java/com/diamonddagger590/mccore/builder/item/impl/SpawnerBuilderTest.java

Comment on lines +80 to +95
@Test
@DisplayName("Given an enchanted book, when addEnchantment is called, then stored enchantment is applied without error")
void addEnchantment_appliesStoredEnchantment_whenEnchantedBook() {
ItemBuilder builder = ItemBuilder.from(ItemType.ENCHANTED_BOOK);

assertDoesNotThrow(() -> builder.addEnchantment(Enchantment.SHARPNESS, 5));
}

@Test
@DisplayName("Given an enchanted book with existing stored enchantment, when addEnchantment is called, then both are applied without error")
void addEnchantment_preservesExistingStoredEnchantments_whenAddingNew() {
ItemBuilder builder = ItemBuilder.from(ItemType.ENCHANTED_BOOK);
builder.addEnchantment(Enchantment.SHARPNESS, 3);

assertDoesNotThrow(() -> builder.addEnchantment(Enchantment.UNBREAKING, 2));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Several @DisplayNames promise verified behavior that the assertions never check.

These tests only assert "no exception thrown" or reference-sameness, but their names/descriptions claim to verify the actual resulting state:

  • Lines 80-86 / 88-95: @DisplayName says the (stored) enchantment "is applied", but only assertDoesNotThrow is checked — a broken addEnchantment on ENCHANTED_BOOK would still pass. Verify via EnchantmentStorageMeta.hasStoredEnchant(...).
  • Lines 127-135: @DisplayName says "returns builder unchanged", but only assertSame(builder, result) (chaining) is checked, not that enchantments are actually unaffected.
  • Lines 285-290: @DisplayName says "completes without error and no change", but no state is compared before/after.
  • Lines 312-317: @DisplayName says "clamps to max without error", but the clamped damage value is never asserted.
  • Lines 585-598: @DisplayName says "original state is preserved on the copy", but only copy.getType() is checked — setDisplayName, addPlaceholder, and addItemFlag effects are set up but never verified on copy.

These gaps mean a real regression in enchantment application, damage clamping, or copy-state preservation would not be caught by these tests.

🧪 Example fix for the copy-constructor test
             ItemStack stack = original.asItemStack();
             ItemBuilder copy = ItemBuilder.from(stack);
 
             assertNotNull(copy);
             assertEquals(Material.DIAMOND, copy.getType());
+            assertEquals("Original Name", copy.getPlainName());
+            assertTrue(copy.asItemStack().getItemMeta().hasItemFlag(org.bukkit.inventory.ItemFlag.HIDE_ENCHANTS));

Also applies to: 127-135, 285-290, 312-317, 585-598

🤖 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/builder/item/BaseItemBuilderMockBukkitTest.java`
around lines 80 - 95, Strengthen the affected tests so their assertions verify
the behavior promised by each `@DisplayName`, not merely absence of exceptions or
object identity. In addEnchantment_appliesStoredEnchantment_whenEnchantedBook
and addEnchantment_preservesExistingStoredEnchantments_whenAddingNew, inspect
EnchantmentStorageMeta.hasStoredEnchant for the expected stored enchantments.
Update the tests covering unchanged enchantments, no-change operations, and
damage clamping to assert the resulting state and clamped value. In the
copy-constructor test, verify the copied display name, placeholders, item flags,
and other configured state in addition to its type.

Comment on lines +301 to +333
@Nested
@DisplayName("setItemDamage")
class SetItemDamageTests {

@Test
@DisplayName("Given a damageable item, when setItemDamage is called, then completes without error")
void setItemDamage_completesWithoutError_whenDamageableItem() {
ItemBuilder builder = ItemBuilder.from(ItemType.DIAMOND_SWORD);
assertDoesNotThrow(() -> builder.setItemDamage(100));
}

@Test
@DisplayName("Given damage exceeding max durability, when setItemDamage is called, then clamps to max without error")
void setItemDamage_clampsToMax_whenExceedingDurability() {
ItemBuilder builder = ItemBuilder.from(ItemType.DIAMOND_SWORD);
assertDoesNotThrow(() -> builder.setItemDamage(Material.DIAMOND_SWORD.getMaxDurability() + 100));
}

@Test
@DisplayName("Given zero damage, when setItemDamage is called, then completes without error")
void setItemDamage_completesWithoutError_whenZeroDamage() {
ItemBuilder builder = ItemBuilder.from(ItemType.DIAMOND_SWORD);
assertDoesNotThrow(() -> builder.setItemDamage(0));
}

@Test
@DisplayName("Given setItemDamage, when called, then returns builder for chaining")
void setItemDamage_returnsSelf_forChaining() {
ItemBuilder builder = ItemBuilder.from(ItemType.DIAMOND_SWORD);
ItemBuilder result = builder.setItemDamage(50);
assertSame(builder, result);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing negative-value edge case for setItemDamage.

This nested class covers positive, max-exceeding, and zero damage, but not a negative value. Coding guidelines require zero/negative numeric-input coverage for new tests.

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/builder/item/BaseItemBuilderMockBukkitTest.java`
around lines 301 - 333, Extend SetItemDamageTests with a test covering a
negative value passed to ItemBuilder.setItemDamage, asserting the documented
behavior and that the call completes without error. Follow the existing
zero-damage test structure and naming conventions, using a damageable
DIAMOND_SWORD builder.

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