[Tests] Add unit tests for GetItemRequest, StatisticEntry, ReloadableContent, StartupProfile + fix thread-safety bug#27
Conversation
…Content, StartupProfile Add tests for five classes that previously had 0% (or 28.6%) line coverage: - GetItemRequestTest: CompletableFuture state transitions (PENDING, FOUND, NOT_FOUND, ERRORED) - StatisticEntryTest: typed accessor methods and ClassCastException edge cases - ReloadableContentTest: constructor loading, reload lifecycle, yaml document updates - ReloadableContentManagerTest: tracking, bulk reload, deduplication - StartupProfileTest: system property setting, enum values All targeted classes now at 100% line coverage. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
|
Warning Review limit reached
More reviews will be available in 56 minutes and 58 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughThis PR adds JUnit 5 tests for five domain classes and supporting infrastructure. StartupProfile enum tests verify system property setting and standard enum methods. ReloadableContent tests cover YAML-backed single-item loading, mutation, and typed extraction. ReloadableContentManager tests verify bulk reload orchestration and deduplication. GetItemRequest tests validate ChangesTest Coverage for Domain Classes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Extensibility ReviewBreaking change risk: NONE — this diff adds only test classes with no changes to production source code. The entire diff consists of new files added under No extensibility concerns found. |
Testing ReviewI'll systematically apply every checklist item to the diff. AnalysisProduction files changedThe diff contains only test file additions — no production source files are modified. This means I must evaluate whether the tests are structurally correct and whether any implied production code has coverage gaps. Production files changed: None (test-only diff)
FindingsCONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: No test file is placed in SummaryProduction files changed: None (test-only additions) Test files present:
Coverage gaps:
|
Security ReviewNo security concerns found. The diff contains exclusively test code ( |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 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/bootstrap/StartupProfileTest.java`:
- Line 21: In StartupProfileTest, rename test method identifiers to the
repository convention givenContext_whenAction_thenOutcome—for example rename
setSystemProperty_setsTrue_whenProd to
givenProdProfile_whenSetSystemProperty_thenReturnsTrue; apply the same pattern
to the other test methods noted in the comment (the methods at lines mentioned)
so their names start with given..., contain _when..._, and end with _then..._;
update any references/usages to those method names (tests and any IDE run
configurations) but leave annotations like `@Test` intact.
In
`@src/test/java/com/diamonddagger590/mccore/configuration/ReloadableContentManagerTest.java`:
- Line 33: Rename the test methods in ReloadableContentManagerTest to follow the
givenContext_whenAction_thenOutcome convention; specifically change
reloadAllContent_reloadsAll_whenContentTracked to a name like
givenTrackedContent_whenReloadAllContent_thenReloadsAll and similarly rename the
other test methods referenced at lines 54, 70, 76, and 96 to follow the
given..._when..._then... pattern so each method name describes the precondition,
the action, and the expected outcome.
- Around line 70-72: The test method
reloadAllContent_completesSuccessfully_whenNoContentTracked currently contains
no assertions; wrap the manager.reloadAllContent() invocation in an assertion
such as assertDoesNotThrow (or add an observable postcondition assertion) so the
test can fail on regressions — update
ReloadableContentManagerTest.reloadAllContent_completesSuccessfully_whenNoContentTracked
to call assertDoesNotThrow(() -> manager.reloadAllContent()) (or equivalent)
referencing the manager.reloadAllContent() call.
In
`@src/test/java/com/diamonddagger590/mccore/configuration/ReloadableContentTest.java`:
- Line 37: Rename the test methods in ReloadableContentTest to follow the
givenContext_whenAction_thenOutcome convention; for example, change
constructor_loadsContentImmediately_whenCallbackProvided() to
givenConstructor_whenCallbackProvided_thenLoadsContentImmediately and similarly
rename the other tests in the class (those currently named with the pattern like
constructor_* or other descriptive names) to start with "given..." followed by
"_when..." and end with "_then..." so they all conform to the repository naming
guideline; update only the method names (and any references inside annotations
like `@Test`) to preserve behavior.
In
`@src/test/java/com/diamonddagger590/mccore/database/response/GetItemRequestTest.java`:
- Around line 13-125: Add a unit test that constructs a GetItemRequest with a
null CompletableFuture (new GetItemRequest<>(null)) and asserts the class
handles this edge case according to project expectations: verify
request.getItemResponseState() is set to ERRORED (or PENDING_RESPONSE if policy
dictates) and request.getItem() is empty, and that getItemCompletableFuture()
returns null (or throws a documented NPE if that is the defined behavior). Place
the test alongside other constructor tests and reference the GetItemRequest
constructor, getItemResponseState(), getItem(), and getItemCompletableFuture()
methods so reviewers can locate the behavior under test.
- Around line 17-124: Rename the test methods in GetItemRequestTest to follow
the givenContext_whenAction_thenOutcome convention: change
constructor_setsPendingState_whenFutureNotComplete ->
givenFutureNotCompleted_whenConstructingRequest_thenPendingResponse;
state_becomesItemFound_whenFutureCompletesWithValue ->
givenFutureCompletesWithValue_whenCompleted_thenItemFound;
state_becomesItemNotFound_whenFutureCompletesWithNull ->
givenFutureCompletesWithNull_whenCompleted_thenItemNotFound;
state_becomesErrored_whenFutureCompletesExceptionally ->
givenFutureCompletesExceptionally_whenCompleted_thenErrored;
getItemCompletableFuture_returnsOriginalFuture ->
givenRequest_whenGetItemCompletableFuture_thenReturnsOriginalFuture;
constructor_setsItemFound_whenFutureAlreadyCompletedWithValue ->
givenFutureAlreadyCompletedWithValue_whenConstructingRequest_thenItemFound;
constructor_setsItemNotFound_whenFutureAlreadyCompletedWithNull ->
givenFutureAlreadyCompletedWithNull_whenConstructingRequest_thenItemNotFound;
constructor_setsErrored_whenFutureAlreadyFailed ->
givenFutureAlreadyFailed_whenConstructingRequest_thenErrored;
state_becomesItemFound_whenFutureCompletesWithComplexObject ->
givenFutureCompletesWithComplexObject_whenCompleted_thenItemFound;
getItem_returnsEmpty_beforeCompletion ->
givenFutureNotCompleted_whenGetItem_thenReturnsEmpty; update each `@DisplayName`
if present to match the new method name semantics and ensure any references to
these method names in test suites are updated.
In `@src/test/java/com/diamonddagger590/mccore/statistic/StatisticEntryTest.java`:
- Around line 23-126: Tests use names like getAsInt_returnsValue_whenTypeIsInt
that don't follow the repository's givenContext_whenAction_thenOutcome
convention; rename each test method to that pattern (e.g.,
getAsInt_returnsValue_whenTypeIsInt ->
givenTypeInt_whenGetAsInt_thenReturnsValue) for all affected methods such as
getAsLong_returnsValue_whenTypeIsLong,
getAsDouble_returnsValue_whenTypeIsDouble,
getAsString_returnsValue_whenTypeIsString,
getAsTimestamp_returnsValue_whenTypeIsTimestamp,
getAsSetString_returnsUnmodifiableCopy_whenTypeIsSetString,
getAsSetString_isDefensiveCopy_whenOriginalModified,
getAsInt_throwsClassCast_whenValueIsNotInteger,
getAsString_throwsClassCast_whenValueIsNotString,
getAsLong_throwsClassCast_whenValueIsNotLong,
getAsTimestamp_throwsClassCast_whenValueIsNotInstant,
recordAccessors_returnConstructorValues, equals_returnsTrue_whenSameValues;
follow the exact pattern given{Context}_when{Action}_then{Outcome} and update
any references/imports if the test names are used elsewhere.
- Around line 74-84: The test
getAsSetString_isDefensiveCopy_whenOriginalModified in StatisticEntryTest
currently mutates original before reading entry.getAsSetString(), which doesn't
prove defensiveness; change the sequence so you create original with "x",
construct StatisticEntry(TEST_KEY, StatisticType.SET_STRING, original), then
call entry.getAsSetString() and store it to result, then mutate original
(original.add("y")) and finally assert result remains unchanged (e.g.,
assertEquals(1, result.size()) or assertTrue(result.contains("x"))). This uses
the variables and methods StatisticEntry, entry.getAsSetString(), and original
to verify the defensive copy behavior.
🪄 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: 1e750c0a-e381-4ca6-800e-efec9f2d0847
📒 Files selected for processing (5)
src/test/java/com/diamonddagger590/mccore/bootstrap/StartupProfileTest.javasrc/test/java/com/diamonddagger590/mccore/configuration/ReloadableContentManagerTest.javasrc/test/java/com/diamonddagger590/mccore/configuration/ReloadableContentTest.javasrc/test/java/com/diamonddagger590/mccore/database/response/GetItemRequestTest.javasrc/test/java/com/diamonddagger590/mccore/statistic/StatisticEntryTest.java
|
|
||
| @Test | ||
| @DisplayName("Given PROD profile, when setSystemProperty is called, then system property is set to true") | ||
| void setSystemProperty_setsTrue_whenProd() { |
There was a problem hiding this comment.
Rename test methods to givenContext_whenAction_thenOutcome format.
Current test names don’t follow the repository’s required test naming pattern. Rename them to the expected given..._when..._then... style for consistency.
Suggested rename diff
- void setSystemProperty_setsTrue_whenProd() {
+ void givenProdProfile_whenSetSystemProperty_thenSetsTrue() {
@@
- void setSystemProperty_setsFalse_whenTest() {
+ void givenTestProfile_whenSetSystemProperty_thenSetsFalse() {
@@
- void setSystemProperty_overridesPrevious_whenCalledAgain() {
+ void givenExistingTestProperty_whenProdSetsSystemProperty_thenOverridesToTrue() {
@@
- void values_containsBothProfiles() {
+ void givenStartupProfileEnum_whenValuesCalled_thenContainsProdAndTest() {
@@
- void valueOf_returnsCorrectProfile() {
+ void givenValidProfileNames_whenValueOfCalled_thenReturnsMatchingProfile() {
@@
- void name_returnsPROD_forProdProfile() {
+ void givenProdProfile_whenNameCalled_thenReturnsProd() {
@@
- void name_returnsTEST_forTestProfile() {
+ void givenTestProfile_whenNameCalled_thenReturnsTest() {As per coding guidelines, "Follow the givenContext_whenAction_thenOutcome naming convention for test methods (e.g., givenEmptyPage_whenGetSlots_thenReturnsEmpty)."
Also applies to: 28-28, 35-35, 45-45, 54-54, 61-61, 67-67
🤖 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/bootstrap/StartupProfileTest.java`
at line 21, In StartupProfileTest, rename test method identifiers to the
repository convention givenContext_whenAction_thenOutcome—for example rename
setSystemProperty_setsTrue_whenProd to
givenProdProfile_whenSetSystemProperty_thenReturnsTrue; apply the same pattern
to the other test methods noted in the comment (the methods at lines mentioned)
so their names start with given..., contain _when..._, and end with _then..._;
update any references/usages to those method names (tests and any IDE run
configurations) but leave annotations like `@Test` intact.
|
|
||
| @Test | ||
| @DisplayName("Given tracked content, when reloadAllContent is called, then all content is reloaded") | ||
| void reloadAllContent_reloadsAll_whenContentTracked() { |
There was a problem hiding this comment.
Rename test methods to the required given..._when..._then... convention.
These names are understandable, but they do not match the mandated repository test naming pattern.
As per coding guidelines: "Follow the givenContext_whenAction_thenOutcome naming convention for test methods (e.g., givenEmptyPage_whenGetSlots_thenReturnsEmpty)."
Also applies to: 54-54, 70-70, 76-76, 96-96
🤖 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/configuration/ReloadableContentManagerTest.java`
at line 33, Rename the test methods in ReloadableContentManagerTest to follow
the givenContext_whenAction_thenOutcome convention; specifically change
reloadAllContent_reloadsAll_whenContentTracked to a name like
givenTrackedContent_whenReloadAllContent_thenReloadsAll and similarly rename the
other test methods referenced at lines 54, 70, 76, and 96 to follow the
given..._when..._then... pattern so each method name describes the precondition,
the action, and the expected outcome.
|
|
||
| @Test | ||
| @DisplayName("Given a yaml document, when ReloadableContent is created with callback, then content is loaded immediately") | ||
| void constructor_loadsContentImmediately_whenCallbackProvided() { |
There was a problem hiding this comment.
Align test method names with given..._when..._then... format.
These method names are readable, but they do not follow the repository’s required test naming convention. Please rename them to givenContext_whenAction_thenOutcome for consistency.
As per coding guidelines: "Follow the givenContext_whenAction_thenOutcome naming convention for test methods (e.g., givenEmptyPage_whenGetSlots_thenReturnsEmpty)."
Also applies to: 46-46, 55-55, 66-66, 75-75, 84-84, 93-93, 102-102, 111-111, 125-125
🤖 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/configuration/ReloadableContentTest.java`
at line 37, Rename the test methods in ReloadableContentTest to follow the
givenContext_whenAction_thenOutcome convention; for example, change
constructor_loadsContentImmediately_whenCallbackProvided() to
givenConstructor_whenCallbackProvided_thenLoadsContentImmediately and similarly
rename the other tests in the class (those currently named with the pattern like
constructor_* or other descriptive names) to start with "given..." followed by
"_when..." and end with "_then..." so they all conform to the repository naming
guideline; update only the method names (and any references inside annotations
like `@Test`) to preserve behavior.
| void constructor_setsPendingState_whenFutureNotComplete() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| assertEquals(GetItemResponseState.PENDING_RESPONSE, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a future that completes with a value, when completed, then state is ITEM_FOUND and item is present") | ||
| void state_becomesItemFound_whenFutureCompletesWithValue() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| future.complete("test-value"); | ||
|
|
||
| assertEquals(GetItemResponseState.ITEM_FOUND, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isPresent()); | ||
| assertEquals("test-value", request.getItem().get()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a future that completes with null, when completed, then state is ITEM_NOT_FOUND") | ||
| void state_becomesItemNotFound_whenFutureCompletesWithNull() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| future.complete(null); | ||
|
|
||
| assertEquals(GetItemResponseState.ITEM_NOT_FOUND, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a future that completes exceptionally, when completed, then state is ERRORED") | ||
| void state_becomesErrored_whenFutureCompletesExceptionally() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| future.completeExceptionally(new RuntimeException("db error")); | ||
|
|
||
| assertEquals(GetItemResponseState.ERRORED, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a request, when getItemCompletableFuture is called, then the original future is returned") | ||
| void getItemCompletableFuture_returnsOriginalFuture() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| assertSame(future, request.getItemCompletableFuture()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a pre-completed future with a value, when request is created, then state is immediately ITEM_FOUND") | ||
| void constructor_setsItemFound_whenFutureAlreadyCompletedWithValue() { | ||
| CompletableFuture<Integer> future = CompletableFuture.completedFuture(42); | ||
| GetItemRequest<Integer> request = new GetItemRequest<>(future); | ||
|
|
||
| assertEquals(GetItemResponseState.ITEM_FOUND, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isPresent()); | ||
| assertEquals(42, request.getItem().get()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a pre-completed future with null, when request is created, then state is immediately ITEM_NOT_FOUND") | ||
| void constructor_setsItemNotFound_whenFutureAlreadyCompletedWithNull() { | ||
| CompletableFuture<String> future = CompletableFuture.completedFuture(null); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| assertEquals(GetItemResponseState.ITEM_NOT_FOUND, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a pre-failed future, when request is created, then state is immediately ERRORED") | ||
| void constructor_setsErrored_whenFutureAlreadyFailed() { | ||
| CompletableFuture<String> future = CompletableFuture.failedFuture(new RuntimeException("fail")); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| assertEquals(GetItemResponseState.ERRORED, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a future completing with a complex object, when completed, then item holds the object") | ||
| void state_becomesItemFound_whenFutureCompletesWithComplexObject() { | ||
| record TestItem(int id, String name) {} | ||
| CompletableFuture<TestItem> future = new CompletableFuture<>(); | ||
| GetItemRequest<TestItem> request = new GetItemRequest<>(future); | ||
|
|
||
| TestItem item = new TestItem(1, "sword"); | ||
| future.complete(item); | ||
|
|
||
| assertEquals(GetItemResponseState.ITEM_FOUND, request.getItemResponseState()); | ||
| assertTrue(request.getItem().isPresent()); | ||
| assertSame(item, request.getItem().get()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given item is empty before completion, when getItem is called, then returns empty Optional") | ||
| void getItem_returnsEmpty_beforeCompletion() { | ||
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| GetItemRequest<String> request = new GetItemRequest<>(future); | ||
|
|
||
| assertFalse(request.getItem().isPresent()); | ||
| } |
There was a problem hiding this comment.
Rename test methods to givenContext_whenAction_thenOutcome for guideline compliance.
The current method names are descriptive but do not follow the required naming convention for tests in this repository.
💡 Example rename pattern
- void constructor_setsPendingState_whenFutureNotComplete() {
+ void givenPendingFuture_whenConstructingRequest_thenStateIsPendingResponse() {As per coding guidelines, "Follow the givenContext_whenAction_thenOutcome naming convention for test methods."
🤖 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/GetItemRequestTest.java`
around lines 17 - 124, Rename the test methods in GetItemRequestTest to follow
the givenContext_whenAction_thenOutcome convention: change
constructor_setsPendingState_whenFutureNotComplete ->
givenFutureNotCompleted_whenConstructingRequest_thenPendingResponse;
state_becomesItemFound_whenFutureCompletesWithValue ->
givenFutureCompletesWithValue_whenCompleted_thenItemFound;
state_becomesItemNotFound_whenFutureCompletesWithNull ->
givenFutureCompletesWithNull_whenCompleted_thenItemNotFound;
state_becomesErrored_whenFutureCompletesExceptionally ->
givenFutureCompletesExceptionally_whenCompleted_thenErrored;
getItemCompletableFuture_returnsOriginalFuture ->
givenRequest_whenGetItemCompletableFuture_thenReturnsOriginalFuture;
constructor_setsItemFound_whenFutureAlreadyCompletedWithValue ->
givenFutureAlreadyCompletedWithValue_whenConstructingRequest_thenItemFound;
constructor_setsItemNotFound_whenFutureAlreadyCompletedWithNull ->
givenFutureAlreadyCompletedWithNull_whenConstructingRequest_thenItemNotFound;
constructor_setsErrored_whenFutureAlreadyFailed ->
givenFutureAlreadyFailed_whenConstructingRequest_thenErrored;
state_becomesItemFound_whenFutureCompletesWithComplexObject ->
givenFutureCompletesWithComplexObject_whenCompleted_thenItemFound;
getItem_returnsEmpty_beforeCompletion ->
givenFutureNotCompleted_whenGetItem_thenReturnsEmpty; update each `@DisplayName`
if present to match the new method name semantics and ensure any references to
these method names in test suites are updated.
| void getAsInt_returnsValue_whenTypeIsInt() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.INT, 42); | ||
| assertEquals(42, entry.getAsInt()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a LONG entry, when getAsLong is called, then returns the long value") | ||
| void getAsLong_returnsValue_whenTypeIsLong() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.LONG, 100_000L); | ||
| assertEquals(100_000L, entry.getAsLong()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a DOUBLE entry, when getAsDouble is called, then returns the double value") | ||
| void getAsDouble_returnsValue_whenTypeIsDouble() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.DOUBLE, 3.14); | ||
| assertEquals(3.14, entry.getAsDouble(), 0.001); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a STRING entry, when getAsString is called, then returns the string value") | ||
| void getAsString_returnsValue_whenTypeIsString() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.STRING, "hello"); | ||
| assertEquals("hello", entry.getAsString()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a TIMESTAMP entry, when getAsTimestamp is called, then returns the instant value") | ||
| void getAsTimestamp_returnsValue_whenTypeIsTimestamp() { | ||
| Instant now = Instant.now(); | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.TIMESTAMP, now); | ||
| assertEquals(now, entry.getAsTimestamp()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a SET_STRING entry, when getAsSetString is called, then returns an unmodifiable copy") | ||
| void getAsSetString_returnsUnmodifiableCopy_whenTypeIsSetString() { | ||
| Set<String> original = new LinkedHashSet<>(); | ||
| original.add("a"); | ||
| original.add("b"); | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.SET_STRING, original); | ||
|
|
||
| Set<String> result = entry.getAsSetString(); | ||
| assertEquals(2, result.size()); | ||
| assertTrue(result.contains("a")); | ||
| assertTrue(result.contains("b")); | ||
| assertNotSame(original, result); | ||
| assertThrows(UnsupportedOperationException.class, () -> result.add("c")); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a SET_STRING entry, when original set is modified after creation, then entry copy is unaffected") | ||
| void getAsSetString_isDefensiveCopy_whenOriginalModified() { | ||
| Set<String> original = new LinkedHashSet<>(); | ||
| original.add("x"); | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.SET_STRING, original); | ||
|
|
||
| original.add("y"); | ||
|
|
||
| Set<String> result = entry.getAsSetString(); | ||
| assertEquals(2, result.size()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a STRING entry, when getAsInt is called, then throws ClassCastException") | ||
| void getAsInt_throwsClassCast_whenValueIsNotInteger() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.STRING, "not-a-number"); | ||
| assertThrows(ClassCastException.class, entry::getAsInt); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given an INT entry, when getAsString is called, then throws ClassCastException") | ||
| void getAsString_throwsClassCast_whenValueIsNotString() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.INT, 42); | ||
| assertThrows(ClassCastException.class, entry::getAsString); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given an INT entry, when getAsLong is called, then throws ClassCastException") | ||
| void getAsLong_throwsClassCast_whenValueIsNotLong() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.INT, 42); | ||
| assertThrows(ClassCastException.class, entry::getAsLong); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given a STRING entry, when getAsTimestamp is called, then throws ClassCastException") | ||
| void getAsTimestamp_throwsClassCast_whenValueIsNotInstant() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.STRING, "not-an-instant"); | ||
| assertThrows(ClassCastException.class, entry::getAsTimestamp); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given an entry, when record accessors are called, then returns constructor values") | ||
| void recordAccessors_returnConstructorValues() { | ||
| StatisticEntry entry = new StatisticEntry(TEST_KEY, StatisticType.INT, 99); | ||
| assertEquals(TEST_KEY, entry.key()); | ||
| assertEquals(StatisticType.INT, entry.type()); | ||
| assertEquals(99, entry.value()); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Given two entries with same values, when equals is called, then returns true") | ||
| void equals_returnsTrue_whenSameValues() { | ||
| StatisticEntry a = new StatisticEntry(TEST_KEY, StatisticType.INT, 1); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Rename test methods to the required givenContext_whenAction_thenOutcome format.
Current method names (for example, getAsInt_returnsValue_whenTypeIsInt) do not follow the enforced test naming convention for this repository.
As per coding guidelines: "Follow the givenContext_whenAction_thenOutcome naming convention for test methods (e.g., givenEmptyPage_whenGetSlots_thenReturnsEmpty)."
🤖 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/StatisticEntryTest.java`
around lines 23 - 126, Tests use names like getAsInt_returnsValue_whenTypeIsInt
that don't follow the repository's givenContext_whenAction_thenOutcome
convention; rename each test method to that pattern (e.g.,
getAsInt_returnsValue_whenTypeIsInt ->
givenTypeInt_whenGetAsInt_thenReturnsValue) for all affected methods such as
getAsLong_returnsValue_whenTypeIsLong,
getAsDouble_returnsValue_whenTypeIsDouble,
getAsString_returnsValue_whenTypeIsString,
getAsTimestamp_returnsValue_whenTypeIsTimestamp,
getAsSetString_returnsUnmodifiableCopy_whenTypeIsSetString,
getAsSetString_isDefensiveCopy_whenOriginalModified,
getAsInt_throwsClassCast_whenValueIsNotInteger,
getAsString_throwsClassCast_whenValueIsNotString,
getAsLong_throwsClassCast_whenValueIsNotLong,
getAsTimestamp_throwsClassCast_whenValueIsNotInstant,
recordAccessors_returnConstructorValues, equals_returnsTrue_whenSameValues;
follow the exact pattern given{Context}_when{Action}_then{Outcome} and update
any references/imports if the test names are used elsewhere.
…ead imports - Add assertDoesNotThrow to empty-manager reloadAllContent test - Fix StatisticEntry defensive-copy test: capture result before mutating the original set so the test actually proves isolation - Replace Instant.now() with fixed Instant.ofEpochSecond(1_000_000) - Remove unused imports from ReloadableContentTest, ReloadableContentManagerTest, and StartupProfileTest https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Extensibility ReviewBreaking change risk: NONE — this diff adds only test classes with no modifications to any production source files. No extensibility concerns found. All changes are confined to |
Testing ReviewI'll systematically apply every checklist item to the diff, examining each new test file and inferring what production classes are being exercised. AnalysisProduction files changed (inferred from test targets):
Test files present:
FindingsCONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: No test file is placed in CONCERN: SummaryProduction files changed: Test files present: Coverage gaps:
|
Security ReviewNo security concerns found. The diff contains only test classes ( |
…e and item GetItemRequest is designed for cross-thread polling — the main Minecraft thread reads responseState/item while a database executor thread writes them via CompletableFuture callbacks. Without volatile, the Java Memory Model does not guarantee visibility of these writes to the polling thread. Two bugs fixed: - Visibility: the polling thread could cache a stale PENDING_RESPONSE indefinitely since the JIT is free to hoist the field read - Ordering: without volatile, the writes to item and responseState could be reordered, letting a reader observe ITEM_FOUND with item still null Add 3 cross-thread tests (100 iterations each) covering the ITEM_FOUND, ITEM_NOT_FOUND, and ERRORED paths with futures completed from a separate executor thread. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Extensibility ReviewBreaking change risk: NONE — all production source changes are internal field visibility modifiers; all other additions are test-only. Reviewing each change against the checklist: Production source changes ( Test source additions: Scanning specifically for checklist items across all changed files:
No extensibility concerns found. |
Testing ReviewI'll systematically apply every checklist item to each production file changed and each test file added. Production Files Analysis
|
Security ReviewNo security concerns found. The diff contains two categories of changes, neither of which touches an attack surface identified in the checklist:
No checklist item applies to any changed line. |
…ests Introduce a reusable JUnit 5 extension that manages ExecutorService lifecycle (create before each test, shutdown + awaitTermination after). Refactor GetItemRequestTest cross-thread tests to use it via @RegisterExtension instead of per-test try/finally blocks. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Prevent false-positive review comments that suggest renaming test methods to givenContext_whenAction_thenOutcome — the repo convention is methodUnderTest_expectedOutcome_whenCondition. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Extensibility ReviewBreaking change risk: NONE — all changes are confined to test sources, test fixtures, and an internal Reviewing each file against the checklist:
No extensibility concerns found. |
Testing ReviewI'll systematically apply every checklist item to the diff. FindingsCONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: There is no test for CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: The CONCERN: SummaryProduction files changed: Test files present: Coverage gaps:
|
Security ReviewNo security concerns found. The diff contains only:
None of the changed files touch SQL query construction, DDL generation, MiniMessage deserialization paths, ChatResponse lifecycle, inventory slot callbacks, or framework permission gates. All checklist items pass with nothing to flag. |
…sona Add checklist item to testing persona and review command requiring cross-thread tests to use ManagedExecutorExtension instead of manually managing ExecutorService lifecycle. Update CLAUDE.md testing section with the same guidance. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Document ManagedExecutorExtension and RegistryResetExtension usage in the always-applied core rules file. https://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Extensibility ReviewBreaking change risk: NONE — all changes are test infrastructure, documentation, and an internal Reviewing each modified surface area against the checklist: Production code: Only Test fixtures ( Documentation files ( New tests: All test classes are package-private, use One observation worth noting, though it does not rise to a breaking concern: CONCERN: Recommend adding a Javadoc sentence to No other extensibility concerns found. |
Extensibility ReviewBreaking change risk: NONE — all changes are test infrastructure, documentation, and a non-breaking Reviewing each touched surface against the checklist: Production code changes ( Test fixture ( Test files ( Documentation files ( One observation worth noting, though it falls below a formal concern threshold:
No extensibility concerns found. |
Testing ReviewI'll systematically apply every checklist item to the diff. AnalysisProduction files changed:
Documentation/config files changed (no production logic):
Test files present:
Test fixture files present:
Checklist FindingsCoverage CompletenessCONCERN: CONCERN: CONCERN: CONCERN: There is no test covering CONCERN: TimeProvider UsageNo new production code calls MockBukkit UsageCONCERN: Bukkit-Dependent vs. Pure-Java SeparationCONCERN: Framework Test QualityCONCERN: CONCERN: SummaryProduction files changed: Test files present: Test fixture files present: Coverage gaps:
|
Security ReviewNo security concerns found. The diff contains exclusively: documentation updates ( |
Testing ReviewTesting Audit ReportCONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: The CONCERN: CONCERN: CONCERN: CONCERN: CONCERN: Production files changed: Test files present: Coverage gaps:
|
Security ReviewNo security concerns found. The diff contains only documentation updates ( |
Summary
CompletableFuturestate machine transitions —PENDING_RESPONSE,ITEM_FOUND,ITEM_NOT_FOUND, andERRORED— including pre-completed futures, pre-failed futures, and cross-thread completion scenarios (0% → 100% line coverage)getAsInt,getAsLong,getAsDouble,getAsString,getAsTimestamp,getAsSetString), defensive copy behavior for set accessor,ClassCastExceptionedge cases, and record equality (28.6% → 100% line coverage)reloadContent()lifecycle, yaml document mutation detection, and accessor methods (0% → 100% line coverage)reloadAllContent()invocation, empty manager safety, callback invocation counting, andSet-based deduplication (0% → 100% line coverage)PROD("true") andTEST("false"), override behavior, and enum completeness (0% → 100% line coverage)Bug fix: Thread-safety in
GetItemRequestGetItemRequestis designed for cross-thread polling — the main Minecraft thread readsresponseState/itemwhile a database executor thread writes them viaCompletableFuturecallbacks. Neither field wasvolatile, which means:PENDING_RESPONSEindefinitely since the JIT is free to hoist the field read into a registervolatile, writes toitemandresponseStatecould be reordered by the CPU/compiler, letting a reader observeITEM_FOUNDwhileitemis stillnullFix: Added
volatileto bothresponseStateanditemfields. Three cross-thread tests (100 iterations each) verify visibility for theITEM_FOUND,ITEM_NOT_FOUND, andERROREDpaths.Test plan
./gradlew testhttps://claude.ai/code/session_011rsAGFzo1R3u5H6uSzPNLA
Summary by CodeRabbit
Tests
Chores