test(server): cover buffered duplex chunk drain after a late listener bind (#11865)
## Thinking Path > - Paperclip uses duplex routes to carry data from plugin workers. > - PR #11860 added the product fix for buffered data after an early route end. > - The fix needs a regression test for a listener that binds after the byte cap ends the route. > - This pull request adds that test and protects the fix from later regressions. > - The benefit is clear test coverage for late-listener delivery. ## Linked Issues or Issue Description This pull request adds regression coverage for the fix in [PR #11860](https://github.com/paperclipai/paperclip/pull/11860). The product fix already exists on `master`. Before that fix, a late listener could receive no data after the byte cap ended the route. The test sends two three-byte `€` chunks to a route with a four-byte cap, waits for route end, then binds the listener. It expects the first valid chunk. ## What Changed - Add one server regression test for late-listener delivery after byte-cap route termination. - Keep the product code unchanged in this pull request. ## Verification - The test passes on the current branch. - PR #11860 merged the product fix into `master` at commit `33eb68b3ae4ce7ee27b31c59bd41db600ad47d19`. - GitHub CI passes on the current head. - Greptile reports 5/5 with no blocking finding. ## Risks Low risk. This pull request changes one test file and no product code, schema, public API, or authentication flow. ## Model Used OpenAI GPT-5. Runtime model ID: GPT-5. Context window: not exposed in this run. Capabilities used: repository review, GitHub operations, and tool use. The implementation came from the engineer's authorized test commit. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used with version and capability details - [x] I have checked ROADMAP.md and confirmed this pull request does not duplicate planned core work - [x] I have searched GitHub for duplicate or related pull requests and linked them above - [x] I have either linked an existing issue or described the issue in this pull request - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket id - [x] I have run the relevant test and GitHub CI passes - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation, or documentation does not apply - [x] I have considered and documented the risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
5bc6031f79
commit
0148c2c6eb
|
|
@ -397,6 +397,34 @@ describe("plugin worker manager duplex channel route", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("drains a buffered valid chunk to a listener that binds after the byte cap ends the route", async () => {
|
||||
const handle = makeDuplexHandle({
|
||||
duplexChannelLimits: { maxTotalDataBytes: 4 },
|
||||
});
|
||||
try {
|
||||
await handle.start();
|
||||
const session = await handle.openDuplexChannel(
|
||||
duplexOpenInput({
|
||||
workerSessionId: "ws-A",
|
||||
// "€" is three bytes in UTF-8. The first chunk is 3 bytes (≤ 4), so the
|
||||
// host counts and buffers it. The second chunk brings the total to 6
|
||||
// bytes (> 4), so the host ends the route.
|
||||
data: [{ chunk: "€" }, { chunk: "€" }],
|
||||
}),
|
||||
);
|
||||
// Wait so both data frames arrive and the route ends on the byte cap before
|
||||
// a listener binds. The first chunk is a valid buffered frame. The host must
|
||||
// keep it, so the late listener drains it. This proves the host does not
|
||||
// drop a buffered valid chunk when the route ends before a listener binds.
|
||||
await expect(session.wait()).resolves.toEqual({ exitCode: null });
|
||||
const chunks: string[] = [];
|
||||
session.onData((chunk) => chunks.push(chunk));
|
||||
expect(chunks).toEqual(["€"]);
|
||||
} finally {
|
||||
await handle.stop().catch(() => undefined);
|
||||
}
|
||||
});
|
||||
|
||||
it("ends the active route when the lifetime timer expires", async () => {
|
||||
const handle = makeDuplexHandle({
|
||||
duplexChannelLimits: { maxDurationMs: 100 },
|
||||
|
|
|
|||
Loading…
Reference in New Issue