Skip to content

Conversation

@sanjomo
Copy link
Member

@sanjomo sanjomo commented Dec 17, 2025

Description

Brief description of the changes in this PR.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Test improvements
  • Build/tooling changes

Related Issue

Closes #(issue number)

Changes Made

Testing

  • All existing tests pass
  • New tests added for new functionality
  • Tests pass locally with mvn test
  • Integration tests pass (if applicable)

Checklist

  • Code follows project coding standards
  • Self-review completed
  • Code is commented where necessary
  • Documentation updated (if needed)
  • Commit messages follow conventional format
  • No merge conflicts
  • All CI checks pass

Additional Notes

Any additional information, screenshots, or context that reviewers should know.

Summary by CodeRabbit

  • Tests
    • Improved distributed test synchronization and per-room message tracking for more deterministic room-state and broadcast assertions.
    • Added test-side behavior to auto-join rooms from handshake parameters and a "get-my-rooms" query to observe client rooms; reduced nondeterministic waits.
  • Chores
    • Non-functional whitespace/formatting adjustment in build config (no behavioral change).

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

Adds deterministic test synchronization and per-room message tracking utilities to distributed Socket.IO tests; introduces automatic room joining from handshake URL parameters and a get-my-rooms ACK-capable event on test nodes; replaces non-thread-safe test storage with thread-safe collections and updates imports/usages across many integration tests. pom.xml only has whitespace edits.

Changes

Cohort / File(s) Summary
Common test sync & tracking
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java
Adds awaitRoomSync(room, expected) helper, per-room message tracking fields (roomMsg, joinMsg), switches per-client arrays to AtomicReferenceArray, uses UUID-named rooms and CompletableFuture coordination, and replaces ad-hoc sleeps with explicit synchronization and ConcurrentHashMap-backed sets.
Hazelcast-based distributed tests
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubMultiChannelUnReliableTest.java, .../DistributedHazelcastPubSubSingleChannelUnreliableTest.java, .../DistributedHazelcastRingBufferMultiChannelTest.java, .../DistributedHazelcastRingBufferSingleChannelTest.java
Add collection imports and symmetric node changes: a get-my-rooms event handler that replies via ACK and a connect listener that parses the join handshake param (comma-split, trim, filter empties, dedupe) and auto-joins the client to each room.
Redisson-based distributed tests
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubMultiChannelUnReliableTest.java, .../DistributedRedissonPubSubSingleChannelUnreliableTest.java, .../DistributedRedissonReliableMultiChannelTest.java, .../DistributedRedissonReliableSingleChannelTest.java, .../DistributedRedissonStreamMultiChannelTest.java, .../DistributedRedissonStreamSingleChannelTest.java
Same pattern as Hazelcast cohort: collection imports, get-my-rooms ACK handler on both nodes, and connect-time join parsing with trimming/filtering/deduplication and auto-join semantics applied symmetrically.
Build config whitespace only
pom.xml
Whitespace/indentation change around the surefire plugin element; no functional change to rerunFailingTestsCount or other settings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect awaitRoomSync for correct timeout/looping and failure reporting.
  • Validate thread-safety and index usage for AtomicReferenceArray and lifecycle of per-room maps/sets in DistributedCommonTest.java.
  • Verify consistent parsing, trimming, empty-filtering, and deduplication of join across all modified test files.
  • Check get-my-rooms ACK handling for correctness and that it doesn't expose internal state incorrectly.
  • Confirm tests still run deterministically with CI settings (pom whitespace change only).

Suggested labels

enhancement

Poem

🐰 I hopped through rooms with tidy names,

UUIDs and ACKs played tidy games,
I counted joins with sync so neat,
Atomic arrays kept every beat,
Now tests will hop in step and meet.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete with mostly empty sections—no actual changes are documented, testing status is unchecked, and the 'Changes Made' and 'Additional Notes' sections are blank. Fill in the 'Changes Made' section with key refactoring details, mark completed testing checkboxes, add notes explaining the refactoring rationale, and optionally remove empty placeholder bullets.
Title check ❓ Inconclusive The title 'Test refactoring' is vague and generic, lacking specificity about what tests were refactored or the main objective of the changes. Provide a more specific title that describes the key refactoring goal, such as 'Improve distributed test determinism with explicit synchronization' or 'Add thread-safe structures to distributed tests'.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test-refactoring

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13f9954 and 3f384b3.

📒 Files selected for processing (1)
  • pom.xml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pom.xml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: qodana
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: build (25) / build
  • GitHub Check: build (21) / build
  • GitHub Check: build (17) / build
  • GitHub Check: Analyze (java-kotlin)

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (1)

880-891: Race condition: connect listeners registered after connect() is called.

The connect() calls on lines 880-881 are made before the EVENT_CONNECT listeners are registered on lines 884-889. This creates a race condition where the connect event may fire before the listener is attached, causing connectLatch to never count down.

Apply this fix to register listeners before connecting:

+        a.on(Socket.EVENT_CONNECT, args -> {
+            connectLatch.countDown();
+        });
+        b.on(Socket.EVENT_CONNECT, args -> {
+            connectLatch.countDown();
+        });
         a.connect();
         b.connect();
         CountDownLatch joinLatch = new CountDownLatch(2);
         CountDownLatch connectLatch = new CountDownLatch(2);
-        a.on(Socket.EVENT_CONNECT, args -> {
-            connectLatch.countDown();
-        });
-        b.on(Socket.EVENT_CONNECT, args -> {
-            connectLatch.countDown();
-        });
♻️ Duplicate comments (5)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubSingleChannelUnreliableTest.java (1)

90-113: Duplicated listener configuration across nodes and test files.

The get-my-rooms handler and connect listener logic is identical to the other Hazelcast test files. Consider the helper method extraction suggested in DistributedHazelcastRingBufferMultiChannelTest.java to reduce this repetition.

Also applies to: 134-157

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubMultiChannelUnReliableTest.java (2)

56-58: Outdated comment references Redis instead of Hazelcast.

     // -------------------------------------------
-    // Redis + Node Setup
+    // Hazelcast + Node Setup
     // -------------------------------------------

90-113: Duplicated listener configuration - same pattern as other test files.

Same duplication pattern already noted. The helper method extraction would benefit all four Hazelcast test files.

Also applies to: 136-159

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastRingBufferSingleChannelTest.java (2)

56-58: Outdated comment references Redis instead of Hazelcast.

     // -------------------------------------------
-    // Redis + Node Setup
+    // Hazelcast + Node Setup
     // -------------------------------------------

90-113: Duplicated listener configuration across nodes.

Same duplication pattern as the other Hazelcast test files. Consider the shared helper method approach.

Also applies to: 135-158

🧹 Nitpick comments (13)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamSingleChannelTest.java (1)

131-154: Consider extracting duplicated listener setup to reduce maintenance burden.

The event listeners here are identical to node1's setup (lines 86-109). While duplication in test setup is common, extracting this logic to a helper method would make future updates easier and reduce the risk of divergence between nodes.

Example refactor:

private void setupTestListeners(SocketIOServer node) {
    node.addEventListener("get-my-rooms", String.class, (client, data, ackSender) -> {
        if (ackSender.isAckRequested()) {
            ackSender.sendAckData(client.getAllRooms());
        }
    });
    
    node.addConnectListener(client -> {
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> joinParams = params.get("join");
        if (joinParams == null || joinParams.isEmpty()) {
            return;
        }
        
        Set<String> rooms = joinParams.stream()
                .flatMap(v -> Arrays.stream(v.split(",")))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());
        
        rooms.forEach(client::joinRoom);
    });
}

Then call setupTestListeners(node1) and setupTestListeners(node2) in the respective setup sections.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubMultiChannelUnReliableTest.java (2)

85-89: Test helper looks good, but consider removing the unused parameter.

The get-my-rooms event handler correctly returns the client's room membership via ACK. However, the data parameter is declared but never used.

If the event doesn't require data, consider using Object.class and naming it more explicitly, or documenting why it's present:

-        node1.addEventListener("get-my-rooms", String.class, (client, data, ackSender) ->{
+        node1.addEventListener("get-my-rooms", String.class, (client, ignored, ackSender) ->{
             if (ackSender.isAckRequested()){
                 ackSender.sendAckData(client.getAllRooms());
             }
         });

130-153: Consider extracting common event listener setup to reduce duplication.

The get-my-rooms event listener and connect listener logic is identical between node1 and node2. While duplication in test setup is sometimes acceptable for clarity, extracting to a helper method could improve maintainability.

Consider extracting the common setup:

private void configureTestEventListeners(SocketIOServer node) {
    node.addEventListener("get-my-rooms", String.class, (client, ignored, ackSender) -> {
        if (ackSender.isAckRequested()) {
            ackSender.sendAckData(client.getAllRooms());
        }
    });
    
    node.addConnectListener(client -> {
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> joinParams = params.get("join");
        if (joinParams == null || joinParams.isEmpty()) {
            return;
        }
        
        Set<String> rooms = joinParams.stream()
                .flatMap(v -> Arrays.stream(v.split(",")))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());
        
        rooms.forEach(client::joinRoom);
    });
}

Then call configureTestEventListeners(node1) and configureTestEventListeners(node2) after creating each node.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubSingleChannelUnreliableTest.java (1)

130-153: LGTM with optional refactor suggestion.

The get-my-rooms handler and connect listener for node2 are identical to node1 and implement the same test infrastructure correctly.

While the duplication is intentional for independent node setup, consider extracting the common logic into helper methods if this pattern continues to expand.

Example refactor:

private void registerGetMyRoomsHandler(SocketIOServer node) {
    node.addEventListener("get-my-rooms", String.class, (client, data, ackSender) -> {
        if (ackSender.isAckRequested()) {
            ackSender.sendAckData(client.getAllRooms());
        }
    });
}

private void registerAutoJoinListener(SocketIOServer node) {
    node.addConnectListener(client -> {
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> joinParams = params.get("join");
        if (joinParams == null || joinParams.isEmpty()) {
            return;
        }
        Set<String> rooms = joinParams.stream()
                .flatMap(v -> Arrays.stream(v.split(",")))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());
        rooms.forEach(client::joinRoom);
    });
}

Then in setup:

registerGetMyRoomsHandler(node1);
registerAutoJoinListener(node1);
// ... later ...
registerGetMyRoomsHandler(node2);
registerAutoJoinListener(node2);
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamMultiChannelTest.java (1)

133-156: Logic is correct; consider extracting shared handlers.

The node2 handlers are identical to node1 and work correctly. However, the ~50 lines of duplicated logic between the two nodes could be extracted into helper methods (e.g., createGetMyRoomsHandler() and createAutoJoinConnectListener()) to improve maintainability and reduce the risk of divergence during future updates.

Example refactor:

+    private void registerCommonHandlers(SocketIOServer node) {
+        node.addEventListener("get-my-rooms", String.class, (client, data, ackSender) -> {
+            if (ackSender.isAckRequested()) {
+                ackSender.sendAckData(client.getAllRooms());
+            }
+        });
+        
+        node.addConnectListener(client -> {
+            Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
+            List<String> joinParams = params.get("join");
+            if (joinParams == null || joinParams.isEmpty()) {
+                return;
+            }
+            
+            Set<String> rooms = joinParams.stream()
+                    .flatMap(v -> Arrays.stream(v.split(",")))
+                    .map(String::trim)
+                    .filter(s -> !s.isEmpty())
+                    .collect(Collectors.toSet());
+            
+            rooms.forEach(client::joinRoom);
+        });
+    }

Then call registerCommonHandlers(node1); and registerCommonHandlers(node2); after setting up the join/leave room event listeners for each node.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableSingleChannelTest.java (2)

85-108: Extract duplicated event and connect listeners into a helper method.

The get-my-rooms event listener and connect listener logic are duplicated identically between node1 and node2. Since this PR focuses on test refactoring, consider extracting this setup into a reusable helper method to improve maintainability and reduce duplication.

Apply this pattern:

+    private void setupNodeEventListeners(SocketIOServer node) {
+        node.addEventListener("get-my-rooms", String.class, (client, data, ackSender) -> {
+            if (ackSender.isAckRequested()) {
+                ackSender.sendAckData(client.getAllRooms());
+            }
+        });
+        
+        node.addConnectListener(client -> {
+            Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
+            List<String> joinParams = params.get("join");
+            if (joinParams == null || joinParams.isEmpty()) {
+                return;
+            }
+            
+            Set<String> rooms = joinParams.stream()
+                    .flatMap(v -> Arrays.stream(v.split(",")))
+                    .map(String::trim)
+                    .filter(s -> !s.isEmpty())
+                    .collect(Collectors.toSet());
+            
+            rooms.forEach(client::joinRoom);
+        });
+    }

Then replace both occurrences with:

-        node1.addEventListener("get-my-rooms", String.class, (client, data, ackSender) ->{
-            if (ackSender.isAckRequested()){
-                ackSender.sendAckData(client.getAllRooms());
-            }
-        });
-        node1.addConnectListener(client -> {
-            ...
-        });
+        setupNodeEventListeners(node1);
-        node2.addEventListener("get-my-rooms", String.class, (client, data, ackSender) ->{
-            if (ackSender.isAckRequested()){
-                ackSender.sendAckData(client.getAllRooms());
-            }
-        });
-        node2.addConnectListener(client -> {
-            ...
-        });
+        setupNodeEventListeners(node2);

Also applies to: 130-153


85-89: Consider using Object.class for unused event data.

The get-my-rooms event listener declares String.class but never uses the data parameter. Since the event acts as a trigger rather than passing data, consider using Object.class for clarity, though this is a minor point.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableMultiChannelTest.java (2)

85-89: Unused parameter in event handler.

The data parameter is declared as String.class but never used in the handler logic. Consider using Object.class or Void.class if no data is expected, or document why a String type is required.

Apply this diff if no data is expected:

-        node1.addEventListener("get-my-rooms", String.class, (client, data, ackSender) ->{
+        node1.addEventListener("get-my-rooms", Object.class, (client, data, ackSender) -> {

131-154: Consider extracting duplicated node setup logic.

The event handler and connect listener for node2 (lines 131-154) are identical to node1 (lines 85-108). While duplication in test setup is common, extracting these to a helper method would improve maintainability.

Example helper method:

private void registerTestEventHandlers(SocketIOServer node) {
    node.addEventListener("get-my-rooms", Object.class, (client, data, ackSender) -> {
        if (ackSender.isAckRequested()) {
            ackSender.sendAckData(client.getAllRooms());
        }
    });
    
    node.addConnectListener(client -> {
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> joinParams = params.get("join");
        if (joinParams == null || joinParams.isEmpty()) {
            return;
        }
        
        Set<String> rooms = joinParams.stream()
                .flatMap(v -> Arrays.stream(v.split(",")))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());
        
        rooms.forEach(client::joinRoom);
    });
}

Then call registerTestEventHandlers(node1) and registerTestEventHandlers(node2) after server creation.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastRingBufferMultiChannelTest.java (2)

90-113: Consider extracting duplicated node configuration logic.

The get-my-rooms event listener and the connect listener for auto-joining rooms are duplicated verbatim for both node1 and node2. This pattern is also repeated across all four Hazelcast test files in this PR.

Consider extracting a helper method to reduce duplication:

private void configureNodeListeners(SocketIOServer node) {
    node.addEventListener("get-my-rooms", String.class, (client, data, ackSender) -> {
        if (ackSender.isAckRequested()) {
            ackSender.sendAckData(client.getAllRooms());
        }
    });
    node.addConnectListener(client -> {
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> joinParams = params.get("join");
        if (joinParams == null || joinParams.isEmpty()) {
            return;
        }
        Set<String> rooms = joinParams.stream()
                .flatMap(v -> Arrays.stream(v.split(",")))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());
        rooms.forEach(client::joinRoom);
    });
}

This could be placed in DistributedCommonTest and called from each subclass's setup() method.

Also applies to: 135-158


56-58: Outdated comment references Redis instead of Hazelcast.

The comment says "Redis + Node Setup" but this test class uses Hazelcast. Consider updating for clarity.

     // -------------------------------------------
-    // Redis + Node Setup
+    // Hazelcast + Node Setup
     // -------------------------------------------
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (1)

479-495: Room sync check may be incorrect for distributed scenarios.

The awaitRoomSync method sums the client counts from both nodes:

int count = node1.getRoomOperations(room).getClients().size() +
            node2.getRoomOperations(room).getClients().size();

In a distributed setup, each client connects to exactly one node. If 2 clients connect (one to each node), the expected count should be 2, and summing gives 2 - this works. However, if 2 clients both connect to node1, you'd get count=2 from node1 alone, which also works.

The issue is if the expectation is that both nodes should see all clients (via distributed sync), this check doesn't validate that. Consider whether you need to verify that each node's view is eventually consistent.

Also, Thread.sleep(1) is very aggressive polling. Consider using a slightly larger interval (e.g., 10-50ms) to reduce CPU churn during tests.

-            Thread.sleep(1);
+            Thread.sleep(50);
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubSingleChannelUnreliableTest.java (1)

56-58: Outdated comment references Redis instead of Hazelcast.

Same issue as in other Hazelcast test files - the comment says "Redis + Node Setup" but this uses Hazelcast.

     // -------------------------------------------
-    // Redis + Node Setup
+    // Hazelcast + Node Setup
     // -------------------------------------------
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6697793 and 8211b91.

📒 Files selected for processing (11)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (12 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubMultiChannelUnReliableTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastPubSubSingleChannelUnreliableTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastRingBufferMultiChannelTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedHazelcastRingBufferSingleChannelTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubMultiChannelUnReliableTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubSingleChannelUnreliableTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableMultiChannelTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableSingleChannelTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamMultiChannelTest.java (3 hunks)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamSingleChannelTest.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (21) / build
  • GitHub Check: build (17) / build
  • GitHub Check: build (25) / build
  • GitHub Check: qodana
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (15)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamSingleChannelTest.java (2)

20-24: LGTM! Necessary imports for the new test infrastructure.

All imports are properly utilized in the new event handlers and connect listeners.


86-109: Well-structured test event handlers with proper defensive coding.

The get-my-rooms handler correctly checks for ACK requests before responding, and the connect listener implements robust URL parameter parsing with deduplication and comma-separated value support. The logic is sound with appropriate null checks and filtering.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubMultiChannelUnReliableTest.java (2)

20-24: LGTM! Necessary imports for the new test infrastructure.

The imports are all used in the URL parameter parsing and room deduplication logic.


90-108: Excellent test infrastructure for auto-joining rooms from URL parameters.

The implementation correctly handles:

  • Comma-separated room names (join=a,b)
  • Multiple join parameters (join=a&join=b)
  • Deduplication via Set
  • Whitespace trimming and empty string filtering
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonPubSubSingleChannelUnreliableTest.java (3)

20-24: LGTM!

The added imports are appropriate for the new test infrastructure functionality (URL parameter parsing, room deduplication, and stream operations).


85-89: LGTM!

The get-my-rooms handler correctly responds with the client's current rooms when an acknowledgment is requested, enabling tests to verify room membership.


90-108: LGTM!

The connect listener correctly implements automatic room joining from handshake URL parameters. The logic handles comma-separated values, deduplicates via Set, and filters empty strings appropriately.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonStreamMultiChannelTest.java (2)

19-24: LGTM!

The new imports support the URL parameter processing and stream-based room joining logic added in the connect listeners.


87-110: LGTM!

The get-my-rooms event handler and connect listener for node1 are well-implemented:

  • The ACK request check prevents unnecessary data transmission.
  • The URL parameter parsing robustly handles null/empty cases, comma-separated values, whitespace trimming, and deduplication via Set.
  • Early return pattern keeps the logic clean.
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableSingleChannelTest.java (2)

20-24: LGTM!

The added imports are all necessary for the new test helper functionality.


90-108: LGTM! The connect listener logic handles edge cases well.

The URL parameter parsing correctly handles:

  • Null/empty checks
  • Comma-separated room names
  • Whitespace trimming
  • Empty string filtering
  • Deduplication via Set

This provides solid test infrastructure for auto-joining rooms via handshake parameters.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedRedissonReliableMultiChannelTest.java (2)

20-24: LGTM!

The import additions are necessary for the new URL parameter parsing and room auto-join functionality.


90-108: LGTM with minor observation.

The URL parameter-based room joining logic is well-structured with proper null checks, deduplication, and filtering. The comma-split support is a nice touch for usability.

Note: This logic is duplicated identically for node2. Consider extracting to a helper method if this pattern appears in additional test classes.

netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (2)

394-477: Well-structured test with proper isolation and synchronization.

The refactoring introduces:

  • UUID-based room naming to prevent inter-test interference
  • Clear early/late message semantics with separate tracking
  • Proper use of awaitRoomSync before assertions

This addresses non-deterministic timing issues in the original test.


811-869: Good test structure with proper listener registration order.

This test correctly registers the EVENT_CONNECT listeners before calling connect(), and uses CompletableFuture for coordinating async assertions on get-my-rooms responses.

@github-actions
Copy link

github-actions bot commented Dec 18, 2025

Qodana for JVM

61 new problems were found

Inspection name Severity Problems
Vulnerable declared dependency 🔶 Warning 8
Pointless arithmetic expression 🔶 Warning 7
Comparison of 'short' and 'char' values 🔶 Warning 2
Result of method call ignored 🔶 Warning 2
Busy wait 🔶 Warning 1
Injection point with ambiguous dependencies 🔶 Warning 1
Constant values 🔶 Warning 1
Number of placeholders does not match number of arguments in logging call 🔶 Warning 1
Unnecessary 'null' check before method call 🔶 Warning 1
Wrapper type may be primitive 🔶 Warning 1
Non-distinguishable logging calls ◽️ Notice 27
Vulnerable declared dependency ◽️ Notice 9

☁️ View the detailed Qodana report

Detected 123 dependencies

Third-party software list

This page lists the third-party software dependencies used in project

Dependency Version Licenses
aesh 2.8.2 Apache-2.0
annotations 26.0.2-1 Apache-2.0
arc-processor 3.30.1 Apache-2.0
arc 3.30.1 Apache-2.0
asm-analysis 9.9 BSD-3-Clause
asm-commons 9.9 BSD-3-Clause
asm-tree 9.9 BSD-3-Clause
asm-util 9.9 BSD-3-Clause
asm 9.9 BSD-3-Clause
byte-buddy 1.17.7 Apache-2.0
cache-api 1.1.1 Apache-2.0
commons-codec 1.20.0 Apache-2.0
commons-compress 1.28.0 Apache-2.0
commons-io 2.21.0 Apache-2.0
commons-logging-jboss-logging 1.0.0.final Apache-2.0
commons-logging 1.3.5 Apache-2.0
crac 1.5.0 BSD-2-Clause
gizmo 1.9.0 Apache-2.0
gizmo2 2.0.0.beta10 Apache-2.0
hazelcast 5.2.5 MIT
jackson-annotations 2.20 Apache-2.0
jackson-core 2.20.1 Apache-2.0
jackson-databind 2.20.1 Apache-2.0
jackson-dataformat-yaml 2.20.0 AML
jackson-datatype-jsr310 2.20.0 Apache-2.0
jakarta.annotation-api 2.1.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.annotation-api 3.0.0 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.el-api 6.0.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.enterprise.cdi-api 4.1.0 Apache-2.0
jakarta.enterprise.lang-model 4.1.0 Apache-2.0
jakarta.inject-api 2.0.1 Apache-2.0
jakarta.interceptor-api 2.2.0 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.json-api 2.1.3 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.transaction-api 2.0.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jandex-gizmo2 3.5.2 Apache-2.0
jandex 3.5.2 Apache-2.0
jansi 2.4.0 Apache-2.0
jboss-logging 3.6.1.final Apache-2.0
jboss-logmanager 3.1.2.final Apache-2.0
jboss-threads 3.9.1 Apache-2.0
jctools-core 4.0.5 Apache-2.0
jdk-classfile-backport 25.1 GPL-2.0-only
jodd-util 6.3.0 BSD-2-Clause
jspecify 1.0.0 Apache-2.0
jul-to-slf4j 2.0.17 MIT
kryo 5.6.2 BSD-3-Clause
log4j-api 2.25.2 Apache-2.0
log4j-to-slf4j 2.25.2 Apache-2.0
micrometer-commons 1.16.0 Apache-2.0
micrometer-observation 1.16.0 Apache-2.0
micronaut-aop 4.10.2 Apache-2.0
micronaut-context-propagation 4.10.2 Apache-2.0
micronaut-context 4.10.2 Apache-2.0
micronaut-core-reactive 4.10.2 Apache-2.0
micronaut-core 4.10.2 Apache-2.0
micronaut-discovery-core 4.10.2 Apache-2.0
micronaut-http-server 4.10.2 Apache-2.0
micronaut-http 4.10.2 Apache-2.0
micronaut-inject 4.10.2 Apache-2.0
micronaut-retry 4.10.2 Apache-2.0
micronaut-router 4.10.2 Apache-2.0
micronaut-runtime 4.10.2 Apache-2.0
microprofile-config-api 3.1 Apache-2.0
microprofile-context-propagation-api 1.3 Apache-2.0
minlog 1.3.1 BSD-3-Clause
mutiny 3.0.1 Apache-2.0
nativeimage 23.1.2 UPL-1.0
netty-common 4.2.9.final Apache-2.0
parsson 1.1.7 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
quarkus-arc-deployment 3.30.1 Apache-2.0
quarkus-arc-dev 3.30.1 Apache-2.0
quarkus-arc 3.30.1 Apache-2.0
quarkus-bootstrap-app-model 3.30.1 Apache-2.0
quarkus-bootstrap-core 3.30.1 Apache-2.0
quarkus-bootstrap-runner 3.30.1 Apache-2.0
quarkus-builder 3.30.1 Apache-2.0
quarkus-class-change-agent 3.30.1 Apache-2.0
quarkus-classloader-commons 3.30.1 Apache-2.0
quarkus-core-deployment 3.30.1 Apache-2.0
quarkus-core 3.30.1 Apache-2.0
quarkus-development-mode-spi 3.30.1 Apache-2.0
quarkus-devui-deployment-spi 3.30.1 Apache-2.0
quarkus-fs-util 1.2.0 Apache-2.0
quarkus-hibernate-validator-spi 3.30.1 Apache-2.0
quarkus-ide-launcher 3.30.1 Apache-2.0
quarkus-smallrye-context-propagation-spi 3.30.1 Apache-2.0
reactive-streams 1.0.4 MIT-0
reactor-core 3.6.2 Apache-2.0
reactor-core 3.7.9 Apache-2.0
readline 2.6 Apache-2.0
redisson 3.52.0 Apache-2.0
reflectasm 1.11.9 BSD-3-Clause
rxjava 3.1.8 Apache-2.0
slf4j-api 2.0.17 MIT
slf4j-jboss-logmanager 2.0.2.final Apache-2.0
smallrye-common-annotation 2.14.0 Apache-2.0
smallrye-common-classloader 2.14.0 Apache-2.0
smallrye-common-constraint 2.14.0 Apache-2.0
smallrye-common-cpu 2.14.0 Apache-2.0
smallrye-common-expression 2.14.0 Apache-2.0
smallrye-common-function 2.14.0 Apache-2.0
smallrye-common-io 2.14.0 Apache-2.0
smallrye-common-net 2.14.0 Apache-2.0
smallrye-common-os 2.14.0 Apache-2.0
smallrye-common-process 2.14.0 Apache-2.0
smallrye-common-ref 2.14.0 Apache-2.0
smallrye-common-resource 2.14.0 Apache-2.0
smallrye-config-common 3.14.1 Apache-2.0
smallrye-config-core 3.14.1 Apache-2.0
smallrye-config 3.14.1 Apache-2.0
snakeyaml 2.4 Apache-2.0
snakeyaml 2.5 Apache-2.0
spring-aop 7.0.1 Apache-2.0
spring-beans 7.0.1 Apache-2.0
spring-boot-autoconfigure 4.0.0 Apache-2.0
spring-boot-starter-logging 4.0.0 Apache-2.0
spring-boot-starter 4.0.0 Apache-2.0
spring-boot 4.0.0 Apache-2.0
spring-context 7.0.1 Apache-2.0
spring-core 7.0.1 Apache-2.0
spring-expression 7.0.1 Apache-2.0
wildfly-common 2.0.1 Apache-2.0
word 23.1.2 UPL-1.0
Contact Qodana team

Contact us at qodana-support@jetbrains.com

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (3)

217-217: Clarify the 256ms timeout choice.

The timeout was changed from 1000ms to 256ms. While this specific value (256 = 2^8) may be intentional, it seems arbitrary for a negative assertion. Consider documenting why 256ms is sufficient or using a rounder value like 200ms or 500ms for clarity.


485-501: Helper method improves test determinism.

The awaitRoomSync method provides a reliable barrier to ensure distributed room membership is synchronized before assertions. The 5-second timeout and polling approach are appropriate for integration tests.

Consider slightly longer sleep intervals

The Thread.sleep(1) creates a very tight polling loop. Consider Thread.sleep(10) or Thread.sleep(50) to reduce CPU usage while still providing fast synchronization:

-            Thread.sleep(1);
+            Thread.sleep(10);

This is a minor optimization and the current implementation is acceptable for tests.


716-718: Reconsider the Thread.sleep after awaitRoomSync.

Line 718 adds a Thread.sleep(200) after awaitRoomSync, which seems contradictory. The comment "allow cross-node adapter propagation" suggests uncertainty about whether awaitRoomSync alone is sufficient.

If awaitRoomSync correctly waits for distributed room state, the additional sleep should be unnecessary. If it's needed, consider:

  1. Increasing the timeout or polling logic in awaitRoomSync itself
  2. Documenting why the additional delay is required despite synchronization

Does the test pass reliably without the 200ms sleep? If so, remove it to avoid unnecessary delays and maintain consistency with other tests.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8211b91 and 13ed79d.

📒 Files selected for processing (2)
  • netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (24 hunks)
  • pom.xml (0 hunks)
💤 Files with no reviewable changes (1)
  • pom.xml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (17) / build
  • GitHub Check: build (21) / build
  • GitHub Check: build (25) / build
  • GitHub Check: qodana
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (13)
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java (13)

24-30: LGTM! Imports support thread-safe refactoring.

The new imports (UUID, CompletableFuture, ConcurrentHashMap, AtomicReferenceArray, fail) align with the test refactoring goals to introduce deterministic synchronization and thread-safe data structures.

Also applies to: 48-48


147-149: LGTM! Thread-safe message tracking with AtomicReferenceArray.

Replacing the plain array with AtomicReferenceArray correctly ensures thread-safe updates from concurrent listener callbacks.

Also applies to: 189-189, 195-195, 201-201, 207-207, 218-221


180-180: Good addition of deterministic room synchronization.

The awaitRoomSync call ensures both clients are registered in the room before broadcasting, eliminating race conditions.


250-253: LGTM! Appropriate use of thread-safe sets.

Using ConcurrentHashMap.newKeySet() is correct for thread-safe set operations from concurrent listeners. The semantic change from List to Set also better represents the test's intent to verify unique messages.


289-289: Good synchronization before broadcast.

Adding awaitRoomSync("room1", 4) ensures all clients are registered before the broadcast, improving test determinism.


318-319: LGTM! Consistent thread-safe refactoring.

The conversion to AtomicReferenceArray maintains consistency with other tests and ensures thread-safe message storage.

Also applies to: 346-346, 350-350, 356-357, 366-367, 373-373, 383-383, 385-385


398-483: Excellent refactoring for test isolation and determinism.

Key improvements:

  • UUID-based room naming (line 398) prevents cross-test interference
  • Separate tracking structures (earlyLatch, lateLatch, joinMsg, roomMsg) provide clear per-phase message tracking
  • Refactored listeners avoid off() calls and explicitly handle "early" vs "late" messages
  • awaitRoomSync (line 472) ensures distributed room state is consistent before the late broadcast

These changes significantly improve test reliability and maintainability.


514-515: LGTM! Consistent thread-safe refactoring.

The AtomicReferenceArray usage maintains consistency across the test suite.

Also applies to: 528-528, 532-532, 550-551


579-582: LGTM! Thread-safety and synchronization for multi-room test.

The refactoring ensures both rooms are properly synchronized before broadcasts and uses thread-safe message storage for concurrent listeners.

Also applies to: 603-604, 613-613, 621-622, 625-625, 629-629, 636-637


650-715: LGTM! Improved thread-safety and code clarity.

The refactoring to ConcurrentHashMap.newKeySet() and awaitRoomSync improves thread-safety and test determinism. The formatting changes enhance readability.

Also applies to: 720-742


774-833: LGTM! Thread-safe two-phase broadcast test.

The AtomicReferenceArray usage ensures thread-safe message tracking across two sequential broadcast phases.


845-901: Excellent refactoring with CompletableFuture-based assertions.

Key improvements:

  • UUID-based room naming (lines 845-846) prevents cross-test contamination
  • awaitRoomSync calls (lines 861-862) ensure deterministic room state
  • CompletableFuture coordination (lines 865-866, 896-898) provides better error propagation than CountDownLatch alone
  • Exceptions in ACK callbacks are now properly captured via completeExceptionally

This pattern significantly improves test reliability and error visibility.


907-920: LGTM! Consistent refactoring with CompletableFuture-based assertions.

The refactoring mirrors the improvements in testConnectAndJoinDifferentRoomTest, providing UUID-based room isolation, deterministic synchronization, and robust error handling through CompletableFuture.

Also applies to: 922-959

b.on(Socket.EVENT_CONNECT, args -> {
connectLatch.countDown();
});
awaitRoomSync(room, 2);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix indentation.

Line 921 is missing one space of indentation compared to surrounding lines.

Apply this diff to fix indentation:
-       awaitRoomSync(room, 2);
+        awaitRoomSync(room, 2);
📝 Committable suggestion

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

Suggested change
awaitRoomSync(room, 2);
awaitRoomSync(room, 2);
🤖 Prompt for AI Agents
In
netty-socketio-core/src/test/java/com/socketio4j/socketio/integration/DistributedCommonTest.java
around line 921, the statement awaitRoomSync(room, 2); is indented one space
less than surrounding lines; fix by adding the missing leading space so the
line's indentation matches the block context (align with adjacent lines), then
run formatter/checkstyle to ensure consistent indentation across the file.

@NeatGuyCoding
Copy link
Collaborator

Seems qodana's condifuration is for single person, if another one open a pr, it would fail

@NeatGuyCoding
Copy link
Collaborator

like me, like denpendabot, our PRs are all failed

@sanjomo
Copy link
Member Author

sanjomo commented Dec 18, 2025

like me, like denpendabot, our PRs are all failed

#90 - your PR works well, only bots PR problem as they cant access secret, we can manually check and approve bot PRs

@sanjomo sanjomo merged commit bde42ca into main Dec 18, 2025
11 of 14 checks passed
@sanjomo sanjomo deleted the test-refactoring branch December 18, 2025 14:12
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.

3 participants