fix(node): deserialize void response now ignores payload length for all void commands#3182
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3182 +/- ##
============================================
- Coverage 74.45% 73.75% -0.70%
Complexity 943 943
============================================
Files 1186 1145 -41
Lines 106343 101476 -4867
Branches 83377 78654 -4723
============================================
- Hits 79176 74843 -4333
+ Misses 24426 23979 -447
+ Partials 2741 2654 -87
🚀 New features to boost your workflow:
|
|
@T1B0 mind taking a look? |
T1B0
left a comment
There was a problem hiding this comment.
i get the limiting buffer size part it's probably safer this way, and the client test is definitly a nice addition ! i don't understand why this MR touch the bdd test, it seems unrelated to the fix part or i'm missing something ?
…sponse` utils Closes apache#3128
…ESSAGES.deserialize` functions Closes apache#3128
…GES.deserialize` to use it Closes apache#3128
…eam and topic IDs - Updated message and topic tests to reference `this.stream.id` and `this.topic.id` instead of passing IDs directly. - Enhanced stream cleanup in tests by ensuring existing streams are deleted. - Added an `After` hook to clean up created streams after tests.
…en` step for assertions - Introduced an `After` hook to ensure streams are deleted after tests. - Simplified `Given` by going back to it original implementation. - Removed unused import of `deserializeVoidResponse`. Closes apache#3128
6b3587e to
11990e3
Compare
@T1B0 I modified the BDD scenarios since |
T1B0
left a comment
There was a problem hiding this comment.
please keep this MR minimalist toward the issue goal (no bdd cleanup / no argument prefixing).
|
re-reading this, i think this part "[BDD test] initially failed because stream IDs were hardcoded as 0 in the step definitions instead of using the dynamically assigned IDs from the server." is the base of the misunderstanding: stream/topics ID are not hardcoded, they are defined by bdd test scenario and passed as those arguments you prefixed. at the moment BDD test just expect a fresh server (you can restart your iggy server with --fresh opt to do so) so they'll fail if you run them twice without a fresh restart between each runs. Can we just revert the bdd/ part and LGTM ? |
- Changes were out of scope and will be discussed in a separate issue Close apache#3128
641e26c to
416235d
Compare
|
@T1B0 reverted BDD changes |
T1B0
left a comment
There was a problem hiding this comment.
Nice catch, ty for your contribution! LGTM 🚀
…yload-length-for-all-void-commands
…ll void commands (apache#3182)
…ll void commands (apache#3182)
Which issue does this PR close?
Closes #3128
Rationale
The issue reported that
deserializeVoidResponsehad been changed fromstatus === 0 && data.length === 0to juststatus === 0,removing the protocol safety check for void commands. However, when I inspected the current code, thedata.length === 0check was already present. Tracing further up the call chain, I found the real root cause:handleResponsewas usingr.subarray(8)to slice the payload, ignoring the length field the server sends. This meant data could include trailing bytes from the next response in the buffer, making thedata.length === 0check unreliable regardless of whether it was there or not.SendMessageswas a separate problem, it useddeserializeVoidResponseeven though the server returns a non-empty payload for that command. Instead of weakening the global void check to accommodate it, it now has its owndeserializeStatusResponsethat only checksstatus === 0What changed?
handleResponseinclient.utils.tswas usingr.subarray(8)to slice the payload, ignoring the length field the server sends. This meant data could include trailing bytes from the next response in the buffer.SendMessageswas usingdeserializeVoidResponsewhich checksdata.length === 0even though the server returns a non-empty payload for that command. Instead of weakening the global void check, it now has its owndeserializeStatusResponsethat only checksstatus === 0.Local Execution
Unit (npm run test:unit) — all pass. Two new test suites were added to client.utils.test.ts:
handleResponse: proves that withlength=0and trailing bytes, data is now emptydeserializeStatusResponse: proves it returns true with non-empty data (key difference fromdeserializeVoidResponse)A deserialize block was also added to send-messages.command.test.ts proving SendMessages accepts non-empty server payloads.
Unit test screenshot
E2E (npm run test:e2e) — 57/58 pass. The one failure (
getClusterMetadata) is pre-existing and unrelated, it expects a 2-node cluster which requiresIGGY_CLUSTER_ENABLED=trueand two running server instances, matching how CI runs it. The TLS test is skipped locally for the same infrastructure reason.E2E test screenshot
BDD (
npm run test:bdd) the main scenario (Create stream and send messages) initially failed because stream IDs were hardcoded as0in the step definitions instead of using the dynamically assigned IDs from the server. This was fixed by usingthis.stream.idandthis.topic.idin the step implementations, and making the Given I have no streams in the system step delete any existing streams before asserting a clean state. The scenario now passes end-to-end, includingSendMessagesagainst a real server. Four cluster/leader redirection scenarios remain undefined, their step definitions don't exist yet in any SDK.BDD test screenshot
AI Usage
If AI tools were used, please answer:
Notes
During investigation I initially assumed SendMessages required a dedicated deserializer because the server might return a non-empty response payload, the CreateMessage type has a payload field, which led to some confusion and the issue description appear to be pointing in that direction as well. However, that payload is the outgoing message content sent in the request, not the server response.
Both the Rust server source (send_empty_ok_response() in send_messages_handler.rs) and live debug logs (
ONDATA object true 8 falseexactly 8 bytes = 4 status + 4 length, zero payload) confirm the response is always empty. With handleResponse correctly bounding data by length,SendMessageswould work fine withdeserializeVoidResponse.deserializeStatusResponsewas kept as a defensive measure in case server behaviour changes in the future, but it is not strictly necessary given the current implementation, just let me know if I should removed it.