diff --git a/test-parity/node-suite/dgram/STATUS.md b/test-parity/node-suite/dgram/STATUS.md new file mode 100644 index 0000000000..345543126d --- /dev/null +++ b/test-parity/node-suite/dgram/STATUS.md @@ -0,0 +1,81 @@ +# `node:dgram` granular parity status + +## Upstream evidence + +This expansion was reassessed against primary repositories on 2026-07-16: + +- Node.js [`608112af`](https://github.com/nodejs/node/tree/608112affae2cf44d2f8a0a6bfe7967193b459c8/test): 82 `test-dgram*` files in `test/parallel` and `test/sequential`. +- Deno [`f8a17c81`](https://github.com/denoland/deno/blob/f8a17c8171569fa2870d740030aaa59c91fdf9ee/tests/node_compat/config.jsonc): 69 selected `parallel/test-dgram*` or `sequential/test-dgram*` entries. +- Bun [`c4fad462`](https://github.com/oven-sh/bun/tree/c4fad462e7dc20e5e9780f848db42e1e2f52186d/test/js/node): 68 copied upstream `test-dgram*` files plus Bun's focused `node-dgram.test.js`. + +The Perry fixtures are diagnostic adaptations, not verbatim copies. They use ephemeral ports, loopback addresses, sequential round trips, and deterministic summaries rather than upstream harness helpers or fixed ports. + +| Perry area | Representative Node files | Deno selection | Bun copy | +| --- | --- | --- | --- | +| socket and send validation | `test-dgram-createSocket-type.js`, `test-dgram-send-bad-arguments.js`, `test-dgram-send-invalid-msg-type.js` | all 3 | send cases (2/3) | +| bind and close lifecycle | `test-dgram-bind.js`, `test-dgram-bind-error-repeat.js`, `test-dgram-close.js` | all 3 | all 3 | +| AbortSignal | `test-dgram-close-signal.js`, `test-dgram-abort-closed.js` | both | both | +| connection state | `test-dgram-connect.js` | selected | copied | +| send overloads and byte counts | `test-dgram-send-callback-buffer.js`, `test-dgram-bytes-length.js`, `test-dgram-connect-send-callback-buffer.js` | all 3 | all 3 | +| omitted host | `test-dgram-send-default-host.js`, `test-dgram-connect-send-default-host.js` | both | both | +| empty and multiple sends | `test-dgram-send-empty-buffer.js`, `test-dgram-send-empty-array.js`, `test-dgram-implicit-bind.js` | all 3 (empty buffer is Darwin-disabled/flaky) | all 3 | +| callback timing | `test-dgram-send-callback-recursive.js` | selected | copied | +| queue and reference state | `test-dgram-send-queue-info.js`, `test-dgram-ref.js`, `test-dgram-unref.js` | ref/unref (2/3) | ref/unref (2/3) | +| address and connected-send validation | `test-dgram-send-address-types.js`, `test-dgram-send-bad-arguments.js` | both | both | +| legacy and advanced send forms | `test-dgram-sendto.js`, `test-dgram-send-callback-buffer-length.js`, `test-dgram-send-callback-multi-buffer.js` | all 3 | all 3 | +| lookup, errors, and blocking | `test-dgram-custom-lookup.js`, `test-dgram-send-error.js`, `test-dgram-send-cb-quelches-error.js`, `test-dgram-blocklist.js` | lookup/error cases (3/4) | lookup/error cases (3/4) | +| close, bind conflict, and disposal | `test-dgram-close-is-not-callback.js`, `test-dgram-bind-error-repeat.js`, `test-dgram-async-dispose.mjs` | all 3 | all 3 | +| controls and buffer metrics | `test-dgram-setTTL.js`, `test-dgram-multicast-setTTL.js`, `test-dgram-socket-buffer-size.js`, `test-dgram-createSocket-type.js` | TTL/create options (3/4) | TTL cases (2/4) | +| constructor message listener | `test-dgram-udp4.js` | selected | copied | + +## Current coverage + +The directory contains 38 fixtures: the original 4 broad cases and 34 granular cases added in this expansion. + +- `validation/`: socket-type/options matrices, message/list/address validation, connected-send guards, DataView/scatter-gather delivery, legacy `sendto()`, port errors, and offset/length bounds. +- `lifecycle/`: default/port/options bind overloads, bind conflicts and retry state, custom lookup, close arguments and ordering, async-dispose shape, AbortSignal validation, and post-close abort behavior. +- `connection/`: invalid ports, pending/connected state guards, disconnect errors, reconnect state, and connect event/callback ordering. +- `send/`: unconnected and connected overloads, implicit binding, constructor listeners, default address/host behavior, empty buffers/arrays and multiple sends, buffer ranges, block lists, DNS error routing, callback byte counts, and callback asynchrony. +- `metrics/`: constructor and setter buffer sizes, queue metrics, and `ref()`/`unref()` identity before bind, after bind, and after close. +- `control/`: deterministic TTL boundary validation. +- Existing broad cases retain unicast loopback, import/API shape, socket controls, and multicast membership coverage. + +The clean Node 26.5.0 focused baseline run is 18/38 parity passes with 20 stable output differences and no compile failures, runtime errors, or skipped fixtures. The differences diagnose: + +1. Repeated `connect()` calls and connected range/destination overloads miss `ERR_SOCKET_DGRAM_IS_CONNECTED` guards. +2. Invalid `signal`, `lookup`, buffer-size, and address options/arguments are accepted. +3. Successful `send()` callbacks run synchronously. +4. `sendto()` lacks Node-coded argument errors, scatter-gather and empty arrays are rejected, and buffer offset/length bounds are ignored. +5. `Symbol.asyncDispose`, custom lookup dispatch, and send block lists are not implemented. +6. Constructor buffer sizes are ignored, failed binds leave `address()` reporting `EBADF`, and `close` callback/event ordering differs. +7. `close()` does not return the socket, an empty address is treated as a DNS name, and multicast TTL handles `Infinity` differently. + +The work was exercised in coherent batches. The first pass covered core validation, bind/connect lifecycle, basic overloads, callbacks, queue/ref metrics, and AbortSignal. Later batches added 23 cases selected from the larger Node/Deno/Bun corpora: address and advanced-option validation, legacy and connected send forms, custom lookup, close/dispose behavior, bind conflicts and retries, block lists, DNS error routing, buffer metrics/options, TTL boundaries, constructor listeners, message-view/scatter-gather behavior, bounds validation, empty arrays, and implicit-bind state. + +## Stopping judgment and exclusions + +Further upstream ports were stopped where they would duplicate the cases above or cross into a separate runtime/platform feature: + +- **Separate runtime work:** Node's new `bindSync()`/`connectSync()`, descriptor/handle binding, active/pre-aborted signal closure, and repeated bind/close states beyond the deterministic conflict/retry and ordering cases. Surface diagnostics for async disposal, block lists, custom lookup, ranges, and scatter-gather now remain in the suite. +- **Platform/slow assessment:** `reusePort`, shared ports, cluster/child-process handle transfer, IPv6 loopback/link-local/interface-specific cases without a capability guard, multicast interface/loopback variants, and source-specific multicast beyond the existing smoke fixture. +- **Kernel-sensitive errors:** message-size, out-of-band buffer, receive errors, implicit-bind failure, and address-specific OS error text. +- **Scheduler-sensitive races:** close during bind/lookup/listening, recursive send callbacks, error-quelching races, burst close behavior, ping-pong stress, and unref/cluster process-liveness tests. +- **Redundant upstream variants:** the many connected/unconnected callback, empty-packet, default-host, buffer, typed-array, and multiple-send files are represented by smaller grouped fixtures here. + +These exclusions keep the default granular lane deterministic on loopback while preserving the 20 actionable Perry differences as focused regression targets. + +## Verification + +```text +NODE_BIN=/tmp/node-v26.5.0/bin/node \ +python3 scripts/node_suite_run.py "$PWD/target/release/perry" "$PWD" dgram + +dgram 18 38 47.4 diff=20 + +cargo fmt --all -- --check +./scripts/check_file_size.sh +python3 -m json.tool test-parity/node_suite_baseline.json +git diff --check +``` + +No wider module run is required for this module floor: the executable changes are confined to `node-suite/dgram`, and the baseline runner was measured directly against all 38 dgram fixtures. Aggregate metadata remains the last clean full-suite snapshot. diff --git a/test-parity/node-suite/dgram/connection/connect-ordering.ts b/test-parity/node-suite/dgram/connection/connect-ordering.ts new file mode 100644 index 0000000000..42073b265a --- /dev/null +++ b/test-parity/node-suite/dgram/connection/connect-ordering.ts @@ -0,0 +1,24 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", resolve)); + +const sender = dgram.createSocket("udp4"); +const order: string[] = []; +sender.once("connect", () => order.push("event")); + +await new Promise((resolve) => { + sender.connect(receiver.address().port, "127.0.0.1", () => { + order.push("callback"); + resolve(); + }); +}); + +const remote = sender.remoteAddress(); +console.log("connect order:", order.join(",")); +console.log("remote:", remote.address, remote.family, remote.port === receiver.address().port); + +await Promise.all([ + new Promise((resolve) => sender.close(resolve)), + new Promise((resolve) => receiver.close(resolve)), +]); diff --git a/test-parity/node-suite/dgram/connection/state-transitions.ts b/test-parity/node-suite/dgram/connection/state-transitions.ts new file mode 100644 index 0000000000..54d22ec2e2 --- /dev/null +++ b/test-parity/node-suite/dgram/connection/state-transitions.ts @@ -0,0 +1,47 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string }).code ?? "Error"; + } +} + +const socket = dgram.createSocket("udp4"); +const firstPeer = dgram.createSocket("udp4"); +const secondPeer = dgram.createSocket("udp4"); +await Promise.all([ + new Promise((resolve) => firstPeer.bind(0, "127.0.0.1", () => resolve())), + new Promise((resolve) => secondPeer.bind(0, "127.0.0.1", () => resolve())), +]); +const firstPort = firstPeer.address().port; +const secondPort = secondPeer.address().port; + +console.log("disconnect before connect:", codeOf(() => socket.disconnect())); +console.log("bad ports:", [0, -1, 65536].map((port) => codeOf(() => socket.connect(port))).join(",")); + +const firstConnect = new Promise((resolve) => { + socket.connect(firstPort, "127.0.0.1", () => resolve()); +}); +console.log("connect while pending:", codeOf(() => socket.connect(firstPort))); +await firstConnect; + +let remote = socket.remoteAddress(); +console.log("first remote:", remote.address, remote.family, remote.port === firstPort); +console.log("connect while connected:", codeOf(() => socket.connect(secondPort))); +console.log("disconnect result:", socket.disconnect()); +console.log("remote after disconnect:", codeOf(() => socket.remoteAddress())); +console.log("repeat disconnect:", codeOf(() => socket.disconnect())); + +await new Promise((resolve) => { + socket.connect(secondPort, "127.0.0.1", () => resolve()); +}); +remote = socket.remoteAddress(); +console.log("reconnected remote:", remote.address, remote.family, remote.port === secondPort); +await Promise.all([ + new Promise((resolve) => socket.close(() => resolve())), + new Promise((resolve) => firstPeer.close(() => resolve())), + new Promise((resolve) => secondPeer.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/control/ttl-validation.ts b/test-parity/node-suite/dgram/control/ttl-validation.ts new file mode 100644 index 0000000000..532d6137fd --- /dev/null +++ b/test-parity/node-suite/dgram/control/ttl-validation.ts @@ -0,0 +1,26 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string }).code ?? "Error"; + } +} + +const socket = dgram.createSocket("udp4"); +await new Promise((resolve) => socket.bind(0, "127.0.0.1", () => resolve())); + +console.log( + "ttl invalid:", + [0, 256, Infinity, "64"].map((value) => codeOf(() => socket.setTTL(value as never))).join(","), +); +console.log( + "multicast ttl invalid:", + [-1, 256, Infinity, "64"] + .map((value) => codeOf(() => socket.setMulticastTTL(value as never))) + .join(","), +); +console.log("ttl valid:", socket.setTTL(64), socket.setMulticastTTL(0)); +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/lifecycle/abort-signal.ts b/test-parity/node-suite/dgram/lifecycle/abort-signal.ts new file mode 100644 index 0000000000..71e0b5ab25 --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/abort-signal.ts @@ -0,0 +1,25 @@ +import * as dgram from "node:dgram"; + +let invalidSignalSocket: dgram.Socket | undefined; +function invalidSignalResult(): string { + try { + invalidSignalSocket = dgram.createSocket({ type: "udp4", signal: {} as AbortSignal }); + return "accepted"; + } catch (error: unknown) { + return (error as { code?: string }).code ?? "Error"; + } +} + +console.log("invalid signal:", invalidSignalResult()); +if (invalidSignalSocket) { + await new Promise((resolve) => invalidSignalSocket!.close(() => resolve())); +} + +const controller = new AbortController(); +const socket = dgram.createSocket({ type: "udp4", signal: controller.signal }); +let closes = 0; +socket.on("close", () => closes++); +await new Promise((resolve) => socket.close(() => resolve())); +controller.abort(); +await new Promise((resolve) => queueMicrotask(resolve)); +console.log("abort after close:", closes); diff --git a/test-parity/node-suite/dgram/lifecycle/async-dispose-shape.ts b/test-parity/node-suite/dgram/lifecycle/async-dispose-shape.ts new file mode 100644 index 0000000000..27e59ff130 --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/async-dispose-shape.ts @@ -0,0 +1,8 @@ +import * as dgram from "node:dgram"; + +const socket = dgram.createSocket("udp4"); +console.log( + "async dispose:", + typeof (socket as unknown as Record)[Symbol.asyncDispose], +); +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/lifecycle/bind-conflict.ts b/test-parity/node-suite/dgram/lifecycle/bind-conflict.ts new file mode 100644 index 0000000000..92e6eed24b --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/bind-conflict.ts @@ -0,0 +1,16 @@ +import * as dgram from "node:dgram"; + +const first = dgram.createSocket("udp4"); +await new Promise((resolve) => first.bind(0, "127.0.0.1", () => resolve())); + +const second = dgram.createSocket("udp4"); +const error = new Promise((resolve) => { + second.once("error", (value) => resolve(`${value.code}:${value.syscall}`)); +}); +second.bind(first.address().port, "127.0.0.1"); +console.log("bind conflict:", await error); + +await Promise.all([ + new Promise((resolve) => first.close(() => resolve())), + new Promise((resolve) => second.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/lifecycle/bind-error-retry.ts b/test-parity/node-suite/dgram/lifecycle/bind-error-retry.ts new file mode 100644 index 0000000000..06977b3d0a --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/bind-error-retry.ts @@ -0,0 +1,33 @@ +import * as dgram from "node:dgram"; + +const owner = dgram.createSocket("udp4"); +await new Promise((resolve) => owner.bind(0, "127.0.0.1", resolve)); + +const candidate = dgram.createSocket("udp4"); +const results: string[] = []; + +for (let attempt = 0; attempt < 2; attempt++) { + const result = await new Promise((resolve) => { + candidate.once("error", (value) => { + const error = value as Error & { code?: string; syscall?: string }; + resolve(`${error.code}:${error.syscall}`); + }); + candidate.bind(owner.address().port, "127.0.0.1", () => resolve("listening")); + }); + results.push(result); + if (result === "listening") break; +} + +console.log("retry results:", results.join(",")); +let addressState: string; +try { + addressState = `port-${candidate.address().port}`; +} catch (error: unknown) { + addressState = (error as { code?: string }).code ?? "Error"; +} +console.log("address after errors:", addressState); + +await Promise.all([ + new Promise((resolve) => owner.close(resolve)), + new Promise((resolve) => candidate.close(resolve)), +]); diff --git a/test-parity/node-suite/dgram/lifecycle/bind-overloads.ts b/test-parity/node-suite/dgram/lifecycle/bind-overloads.ts new file mode 100644 index 0000000000..cc70e9891d --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/bind-overloads.ts @@ -0,0 +1,28 @@ +import * as dgram from "node:dgram"; + +for (const mode of ["default", "port", "options"] as const) { + const socket = dgram.createSocket("udp4"); + + await new Promise((resolve) => { + function onListening(this: dgram.Socket) { + const address = socket.address(); + console.log( + mode, + address.address, + address.family, + typeof address.port, + address.port > 0, + this === socket, + ); + socket.close(() => resolve()); + } + + if (mode === "default") { + socket.bind(onListening); + } else if (mode === "port") { + socket.bind(0, onListening); + } else { + socket.bind({ port: 0, address: "127.0.0.1" }, onListening); + } + }); +} diff --git a/test-parity/node-suite/dgram/lifecycle/close-arguments.ts b/test-parity/node-suite/dgram/lifecycle/close-arguments.ts new file mode 100644 index 0000000000..9f0212fc64 --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/close-arguments.ts @@ -0,0 +1,14 @@ +import * as dgram from "node:dgram"; + +const socket = dgram.createSocket("udp4"); +let closeEvents = 0; +socket.on("close", () => closeEvents++); +const closed = new Promise((resolve) => { + socket.once("close", () => queueMicrotask(resolve)); +}); + +const result = (socket.close as (callback?: unknown) => dgram.Socket)("not a callback"); +await closed; + +console.log("close result self:", result === socket); +console.log("close events:", closeEvents); diff --git a/test-parity/node-suite/dgram/lifecycle/close-ordering.ts b/test-parity/node-suite/dgram/lifecycle/close-ordering.ts new file mode 100644 index 0000000000..0c1ebb3080 --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/close-ordering.ts @@ -0,0 +1,17 @@ +import * as dgram from "node:dgram"; + +const socket = dgram.createSocket("udp4"); +await new Promise((resolve) => socket.bind(0, "127.0.0.1", resolve)); + +const order: string[] = []; +const closed = new Promise((resolve) => { + socket.once("close", () => { + order.push("event"); + resolve(); + }); +}); + +socket.close(() => order.push("callback")); +await closed; +await Promise.resolve(); +console.log("close order:", order.join(",")); diff --git a/test-parity/node-suite/dgram/lifecycle/custom-lookup.ts b/test-parity/node-suite/dgram/lifecycle/custom-lookup.ts new file mode 100644 index 0000000000..0a21d5ed69 --- /dev/null +++ b/test-parity/node-suite/dgram/lifecycle/custom-lookup.ts @@ -0,0 +1,14 @@ +import * as dgram from "node:dgram"; + +const calls: string[] = []; +const socket = dgram.createSocket({ + type: "udp4", + lookup(hostname, family, callback) { + calls.push(`${hostname}:${family}`); + callback(null, "0.0.0.0", 4); + }, +}); + +await new Promise((resolve) => socket.bind(0, () => resolve())); +console.log("lookup calls:", calls.length, calls[0]?.startsWith("0.0.0.0:") ?? false); +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/metrics/buffer-sizes.ts b/test-parity/node-suite/dgram/metrics/buffer-sizes.ts new file mode 100644 index 0000000000..4c71217433 --- /dev/null +++ b/test-parity/node-suite/dgram/metrics/buffer-sizes.ts @@ -0,0 +1,30 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string }).code ?? "Error"; + } +} + +const unbound = dgram.createSocket("udp4"); +console.log("unbound send get:", codeOf(() => unbound.getSendBufferSize())); +console.log("unbound recv set:", codeOf(() => unbound.setRecvBufferSize(8192))); +await new Promise((resolve) => unbound.close(() => resolve())); + +const socket = dgram.createSocket("udp4"); +await new Promise((resolve) => socket.bind(0, "127.0.0.1", () => resolve())); +console.log( + "invalid recv:", + [-1, Infinity, "bad"].map((value) => codeOf(() => socket.setRecvBufferSize(value as never))).join(","), +); +console.log( + "invalid send:", + [-1, Infinity, "bad"].map((value) => codeOf(() => socket.setSendBufferSize(value as never))).join(","), +); +socket.setRecvBufferSize(10000); +socket.setSendBufferSize(10000); +console.log("positive sizes:", socket.getRecvBufferSize() > 0, socket.getSendBufferSize() > 0); +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/metrics/create-socket-buffer-options.ts b/test-parity/node-suite/dgram/metrics/create-socket-buffer-options.ts new file mode 100644 index 0000000000..dbffa0ed0c --- /dev/null +++ b/test-parity/node-suite/dgram/metrics/create-socket-buffer-options.ts @@ -0,0 +1,15 @@ +import * as dgram from "node:dgram"; + +const socket = dgram.createSocket({ + type: "udp4", + recvBufferSize: 10_000, + sendBufferSize: 15_000, +}); +await new Promise((resolve) => socket.bind(0, "127.0.0.1", resolve)); + +const recvSize = socket.getRecvBufferSize(); +const sendSize = socket.getSendBufferSize(); +console.log("receive option applied:", recvSize === 10_000 || recvSize === 20_000); +console.log("send option applied:", sendSize === 15_000 || sendSize === 30_000); + +await new Promise((resolve) => socket.close(resolve)); diff --git a/test-parity/node-suite/dgram/metrics/queue-and-ref.ts b/test-parity/node-suite/dgram/metrics/queue-and-ref.ts new file mode 100644 index 0000000000..cf9dd79a65 --- /dev/null +++ b/test-parity/node-suite/dgram/metrics/queue-and-ref.ts @@ -0,0 +1,12 @@ +import * as dgram from "node:dgram"; + +const socket = dgram.createSocket("udp4"); +console.log("initial queue:", socket.getSendQueueSize(), socket.getSendQueueCount()); +console.log("unbound identities:", socket.unref() === socket, socket.ref() === socket); + +await new Promise((resolve) => socket.bind(0, "127.0.0.1", () => resolve())); +console.log("bound identities:", socket.unref() === socket, socket.ref() === socket); +console.log("bound queue:", socket.getSendQueueSize(), socket.getSendQueueCount()); + +await new Promise((resolve) => socket.close(() => resolve())); +console.log("closed identities:", socket.unref() === socket, socket.ref() === socket); diff --git a/test-parity/node-suite/dgram/send/blocklist.ts b/test-parity/node-suite/dgram/send/blocklist.ts new file mode 100644 index 0000000000..12cbda98bd --- /dev/null +++ b/test-parity/node-suite/dgram/send/blocklist.ts @@ -0,0 +1,23 @@ +import * as dgram from "node:dgram"; +import { BlockList } from "node:net"; + +const blockList = new BlockList(); +blockList.addAddress("127.0.0.1"); + +const sendSocket = dgram.createSocket({ type: "udp4", sendBlockList: blockList }); +const sendCode = await new Promise((resolve) => { + sendSocket.send("blocked", 12345, "127.0.0.1", (error) => { + resolve(error?.code ?? "none"); + }); +}); +console.log("blocked send:", sendCode); +await new Promise((resolve) => sendSocket.close(() => resolve())); + +const connectSocket = dgram.createSocket({ type: "udp4", sendBlockList: blockList }); +const connectCode = await new Promise((resolve) => { + connectSocket.connect(12345, "127.0.0.1", (error) => { + resolve(error?.code ?? "none"); + }); +}); +console.log("blocked connect:", connectCode); +await new Promise((resolve) => connectSocket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/send/buffer-range.ts b/test-parity/node-suite/dgram/send/buffer-range.ts new file mode 100644 index 0000000000..e4d722367b --- /dev/null +++ b/test-parity/node-suite/dgram/send/buffer-range.ts @@ -0,0 +1,22 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); +const message = Buffer.from("--slice--"); + +const received = new Promise((resolve) => { + receiver.once("message", (value) => resolve(value.toString())); +}); +const callback = new Promise((resolve) => { + sender.send(message, 2, 5, receiver.address().port, "127.0.0.1", (error, bytes) => { + resolve(`${error === null}:${bytes}`); + }); +}); + +console.log("range message:", await received); +console.log("range callback:", await callback); +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/callback-ordering.ts b/test-parity/node-suite/dgram/send/callback-ordering.ts new file mode 100644 index 0000000000..b32b2c2b3b --- /dev/null +++ b/test-parity/node-suite/dgram/send/callback-ordering.ts @@ -0,0 +1,33 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); + +let synchronous = true; +let callbackWasAsync = false; +let callbackBytes = -1; +let callbackError = "unset"; + +const received = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); +}); +const sent = new Promise((resolve) => { + sender.send("callback", receiver.address().port, "127.0.0.1", (error, bytes) => { + callbackWasAsync = !synchronous; + callbackBytes = bytes; + callbackError = error === null ? "null" : error.code; + resolve(); + }); +}); +synchronous = false; + +await Promise.all([received, sent]); +console.log("callback async:", callbackWasAsync); +console.log("callback result:", callbackError, callbackBytes); +console.log("message:", await received); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/connected-overloads.ts b/test-parity/node-suite/dgram/send/connected-overloads.ts new file mode 100644 index 0000000000..4063aae071 --- /dev/null +++ b/test-parity/node-suite/dgram/send/connected-overloads.ts @@ -0,0 +1,26 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); +await new Promise((resolve) => { + sender.connect(receiver.address().port, "127.0.0.1", () => resolve()); +}); + +async function roundTrip(label: string, message: string | Uint8Array) { + const received = new Promise((resolve) => { + receiver.once("message", (value) => resolve(value.toString())); + }); + const callback = new Promise((resolve) => { + sender.send(message, (error, bytes) => resolve(`${error === null}:${bytes}`)); + }); + console.log(label, await received, await callback); +} + +await roundTrip("string", "connected string"); +await roundTrip("typed", new Uint8Array([116, 121, 112, 101, 100])); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/create-socket-listener.ts b/test-parity/node-suite/dgram/send/create-socket-listener.ts new file mode 100644 index 0000000000..e4c5d07f25 --- /dev/null +++ b/test-parity/node-suite/dgram/send/create-socket-listener.ts @@ -0,0 +1,18 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4", (message, rinfo) => { + console.log("constructor listener:", message.toString(), rinfo.family, rinfo.size); +}); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); + +const received = new Promise((resolve) => receiver.once("message", () => resolve())); +await new Promise((resolve) => { + sender.send("listener", receiver.address().port, "127.0.0.1", () => resolve()); +}); +await received; + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/default-address-values.ts b/test-parity/node-suite/dgram/send/default-address-values.ts new file mode 100644 index 0000000000..1ef20c6a3c --- /dev/null +++ b/test-parity/node-suite/dgram/send/default-address-values.ts @@ -0,0 +1,22 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); + +async function send(address: string | null | undefined) { + return await new Promise((resolve) => { + sender.send("default", receiver.address().port, address as never, (error, bytes) => { + resolve(`${error?.code ?? "null"}:${bytes ?? "none"}`); + }); + }); +} + +console.log("empty:", await send("")); +console.log("null:", await send(null)); +console.log("undefined:", await send(undefined)); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/default-host.ts b/test-parity/node-suite/dgram/send/default-host.ts new file mode 100644 index 0000000000..725ebceee8 --- /dev/null +++ b/test-parity/node-suite/dgram/send/default-host.ts @@ -0,0 +1,23 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); + +const first = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); +}); +await new Promise((resolve) => sender.send("implicit host", receiver.address().port, () => resolve())); +console.log("unconnected default host:", await first); + +await new Promise((resolve) => sender.connect(receiver.address().port, () => resolve())); +const second = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); +}); +await new Promise((resolve) => sender.send("connected host", () => resolve())); +console.log("connected default host:", await second); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/empty-and-multiple.ts b/test-parity/node-suite/dgram/send/empty-and-multiple.ts new file mode 100644 index 0000000000..3717453c1d --- /dev/null +++ b/test-parity/node-suite/dgram/send/empty-and-multiple.ts @@ -0,0 +1,39 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const port = receiver.address().port; +const sender = dgram.createSocket("udp4"); + +async function roundTrip( + label: string, + send: (callback: (error: Error | null, bytes: number) => void) => void, +) { + const message = new Promise((resolve) => receiver.once("message", resolve)); + const sent = new Promise((resolve) => { + send((error, bytes) => resolve(`${error === null}:${bytes}`)); + }); + const received = await message; + console.log(label, received.length, await sent); +} + +await roundTrip("empty buffer", (callback) => { + sender.send(Buffer.alloc(0), port, "127.0.0.1", callback); +}); +const messages: string[] = []; +const multipleReceived = new Promise((resolve) => { + receiver.on("message", (message) => { + messages.push(message.toString()); + if (messages.length === 2) resolve(messages.sort().join(",")); + }); +}); +await Promise.all([ + new Promise((resolve) => sender.send("first", port, "127.0.0.1", () => resolve())), + new Promise((resolve) => sender.send("second", port, "127.0.0.1", () => resolve())), +]); +console.log("multiple sends:", await multipleReceived); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/send/empty-array.ts b/test-parity/node-suite/dgram/send/empty-array.ts new file mode 100644 index 0000000000..92d547af0e --- /dev/null +++ b/test-parity/node-suite/dgram/send/empty-array.ts @@ -0,0 +1,35 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", resolve)); +const sender = dgram.createSocket("udp4"); + +let finishCallback: (value: string) => void = () => {}; +const callback = new Promise((resolve) => { + finishCallback = resolve; +}); +const result = codeOf(() => { + sender.send([], receiver.address().port, "127.0.0.1", (error, bytes) => { + finishCallback(`${error === null}:${bytes}`); + }); +}); + +console.log("empty array accepted:", result); +if (result === "none") { + console.log("empty array callback:", await callback); +} + +await Promise.all([ + new Promise((resolve) => sender.close(resolve)), + new Promise((resolve) => receiver.close(resolve)), +]); diff --git a/test-parity/node-suite/dgram/send/error-routing.ts b/test-parity/node-suite/dgram/send/error-routing.ts new file mode 100644 index 0000000000..fa1a863fd6 --- /dev/null +++ b/test-parity/node-suite/dgram/send/error-routing.ts @@ -0,0 +1,19 @@ +import * as dgram from "node:dgram"; + +const withCallback = dgram.createSocket("udp4"); +let callbackErrorEvents = 0; +withCallback.on("error", () => callbackErrorEvents++); +const callbackCode = await new Promise((resolve) => { + withCallback.send("x", 12345, "missing.invalid", (error) => resolve(error?.code ?? "none")); +}); +await new Promise((resolve) => queueMicrotask(resolve)); +console.log("callback route:", callbackCode, callbackErrorEvents); +await new Promise((resolve) => withCallback.close(() => resolve())); + +const withEvent = dgram.createSocket("udp4"); +const eventCode = new Promise((resolve) => { + withEvent.once("error", (error) => resolve(error.code)); +}); +withEvent.send("x", 12345, "missing.invalid"); +console.log("event route:", await eventCode); +await new Promise((resolve) => withEvent.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/send/implicit-bind-state.ts b/test-parity/node-suite/dgram/send/implicit-bind-state.ts new file mode 100644 index 0000000000..71468fcd94 --- /dev/null +++ b/test-parity/node-suite/dgram/send/implicit-bind-state.ts @@ -0,0 +1,39 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", resolve)); + +const first = dgram.createSocket("udp4"); +const second = dgram.createSocket("udp4"); +const received: string[] = []; +const messages = new Promise((resolve) => { + receiver.on("message", (message) => { + received.push(message.toString()); + if (received.length === 2) resolve(); + }); +}); + +await Promise.all([ + new Promise((resolve, reject) => { + first.send("first", receiver.address().port, "127.0.0.1", (error) => + error ? reject(error) : resolve() + ); + }), + new Promise((resolve, reject) => { + second.send("second", receiver.address().port, "127.0.0.1", (error) => + error ? reject(error) : resolve() + ); + }), + messages, +]); + +const firstAddress = first.address(); +const secondAddress = second.address(); +console.log("messages:", received.sort().join(",")); +console.log("families:", firstAddress.family, secondAddress.family); +console.log("ports assigned:", firstAddress.port > 0, secondAddress.port > 0); +console.log("ports distinct:", firstAddress.port !== secondAddress.port); + +await new Promise((resolve) => first.close(resolve)); +await new Promise((resolve) => second.close(resolve)); +await new Promise((resolve) => receiver.close(resolve)); diff --git a/test-parity/node-suite/dgram/send/overloads.ts b/test-parity/node-suite/dgram/send/overloads.ts new file mode 100644 index 0000000000..5b3376b748 --- /dev/null +++ b/test-parity/node-suite/dgram/send/overloads.ts @@ -0,0 +1,32 @@ +import * as dgram from "node:dgram"; + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const port = receiver.address().port; +const sender = dgram.createSocket("udp4"); + +async function sendAndReceive( + expected: string, + send: (callback: (error: Error | null, bytes: number) => void) => void, +) { + const received = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); + }); + const callback = new Promise((resolve) => { + send((error, bytes) => resolve(`${error === null}:${bytes}`)); + }); + console.log(expected, await received, await callback); +} + +await sendAndReceive("string", (callback) => { + sender.send("string", port, "127.0.0.1", callback); +}); + +await sendAndReceive("typed", (callback) => { + sender.send(new Uint8Array([116, 121, 112, 101, 100]), port, "127.0.0.1", callback); +}); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/validation/address-arguments.ts b/test-parity/node-suite/dgram/validation/address-arguments.ts new file mode 100644 index 0000000000..d33474ddf2 --- /dev/null +++ b/test-parity/node-suite/dgram/validation/address-arguments.ts @@ -0,0 +1,30 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); +const invalidAddresses: unknown[] = [[], 0, 1, true, false, 0n, 1n, {}, Symbol("address")]; + +console.log( + "invalid addresses:", + invalidAddresses + .map((address) => + codeOf(() => sender.send("x", receiver.address().port, address as never)) + ) + .join(","), +); + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/validation/buffer-bounds.ts b/test-parity/node-suite/dgram/validation/buffer-bounds.ts new file mode 100644 index 0000000000..6ec67a8ba2 --- /dev/null +++ b/test-parity/node-suite/dgram/validation/buffer-bounds.ts @@ -0,0 +1,37 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const socket = dgram.createSocket("udp4"); +await new Promise((resolve) => socket.bind(0, "127.0.0.1", resolve)); +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", resolve)); +const message = Buffer.from("hello"); +const callback = () => {}; +const port = receiver.address().port; + +console.log( + "offset:", + codeOf(() => socket.send(message, 6, 0, port, "127.0.0.1", callback)), +); +console.log( + "length:", + codeOf(() => socket.send(message, 0, 6, port, "127.0.0.1", callback)), +); +console.log( + "combined:", + codeOf(() => socket.send(message, 3, 4, port, "127.0.0.1", callback)), +); + +await Promise.all([ + new Promise((resolve) => socket.close(resolve)), + new Promise((resolve) => receiver.close(resolve)), +]); diff --git a/test-parity/node-suite/dgram/validation/connected-send-arguments.ts b/test-parity/node-suite/dgram/validation/connected-send-arguments.ts new file mode 100644 index 0000000000..a91e2bd9ec --- /dev/null +++ b/test-parity/node-suite/dgram/validation/connected-send-arguments.ts @@ -0,0 +1,32 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const peer = dgram.createSocket("udp4"); +await new Promise((resolve) => peer.bind(0, "127.0.0.1", () => resolve())); +const socket = dgram.createSocket("udp4"); +await new Promise((resolve) => { + socket.connect(peer.address().port, "127.0.0.1", () => resolve()); +}); + +console.log( + "destination while connected:", + codeOf(() => socket.send("x", peer.address().port, "127.0.0.1")), +); +console.log( + "range destination while connected:", + codeOf(() => socket.send(Buffer.from("x"), 0, 1, peer.address().port, "127.0.0.1")), +); + +await Promise.all([ + new Promise((resolve) => socket.close(() => resolve())), + new Promise((resolve) => peer.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/validation/create-socket-advanced-options.ts b/test-parity/node-suite/dgram/validation/create-socket-advanced-options.ts new file mode 100644 index 0000000000..3a2505f165 --- /dev/null +++ b/test-parity/node-suite/dgram/validation/create-socket-advanced-options.ts @@ -0,0 +1,32 @@ +import * as dgram from "node:dgram"; + +const acceptedSockets: dgram.Socket[] = []; +function codeOf(fn: () => unknown): string { + try { + const socket = fn() as dgram.Socket | undefined; + if (socket) acceptedSockets.push(socket); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +console.log( + "invalid lookup:", + [null, true, 0, "lookup", {}] + .map((lookup) => codeOf(() => dgram.createSocket({ type: "udp4", lookup: lookup as never }))) + .join(","), +); +console.log( + "invalid recv size:", + codeOf(() => dgram.createSocket({ type: "udp4", recvBufferSize: "bad" as never })), +); +console.log( + "invalid send size:", + codeOf(() => dgram.createSocket({ type: "udp4", sendBufferSize: "bad" as never })), +); + +await Promise.all( + acceptedSockets.map((socket) => new Promise((resolve) => socket.close(() => resolve()))), +); diff --git a/test-parity/node-suite/dgram/validation/create-socket-options.ts b/test-parity/node-suite/dgram/validation/create-socket-options.ts new file mode 100644 index 0000000000..5be4976491 --- /dev/null +++ b/test-parity/node-suite/dgram/validation/create-socket-options.ts @@ -0,0 +1,36 @@ +import * as dgram from "node:dgram"; + +function errorOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + const value = error as { code?: string; name?: string }; + return `${value.name}:${value.code}`; + } +} + +const invalidTypes: unknown[] = [ + "udp5", + ["udp4"], + new String("udp4"), + 1, + {}, + true, + false, + null, + undefined, +]; + +console.log( + "invalid types:", + invalidTypes.map((type) => errorOf(() => dgram.createSocket(type as never))).join(","), +); + +const udp4 = dgram.createSocket({ type: "udp4" }); +const udp6 = dgram.createSocket({ type: "udp6" }); +console.log("valid option types:", typeof udp4.send, typeof udp6.send); +await Promise.all([ + new Promise((resolve) => udp4.close(() => resolve())), + new Promise((resolve) => udp6.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/validation/message-views.ts b/test-parity/node-suite/dgram/validation/message-views.ts new file mode 100644 index 0000000000..75aa061f47 --- /dev/null +++ b/test-parity/node-suite/dgram/validation/message-views.ts @@ -0,0 +1,49 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const receiver = dgram.createSocket("udp4"); +await new Promise((resolve) => receiver.bind(0, "127.0.0.1", () => resolve())); +const sender = dgram.createSocket("udp4"); +const data = new ArrayBuffer(4); +new Uint8Array(data).set([118, 105, 101, 119]); + +const viewReceived = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); +}); +const viewCallback = new Promise((resolve) => { + sender.send(new DataView(data), receiver.address().port, "127.0.0.1", (error, bytes) => { + resolve(`${error === null}:${bytes}`); + }); +}); +console.log("data view:", await viewReceived, await viewCallback); + +let finishScatter: (value: string) => void = () => {}; +const scatterCallback = new Promise((resolve) => { + finishScatter = resolve; +}); +const scatterReceived = new Promise((resolve) => { + receiver.once("message", (message) => resolve(message.toString())); +}); +const scatterResult = codeOf(() => { + sender.send([Buffer.from("a"), "b"], receiver.address().port, "127.0.0.1", (error, bytes) => { + finishScatter(`${error === null}:${bytes}`); + }); +}); +console.log("scatter accepted:", scatterResult); +if (scatterResult === "none") { + console.log("scatter delivery:", await scatterReceived, await scatterCallback); +} + +await Promise.all([ + new Promise((resolve) => sender.close(() => resolve())), + new Promise((resolve) => receiver.close(() => resolve())), +]); diff --git a/test-parity/node-suite/dgram/validation/send-arguments.ts b/test-parity/node-suite/dgram/validation/send-arguments.ts new file mode 100644 index 0000000000..d360a1a2ee --- /dev/null +++ b/test-parity/node-suite/dgram/validation/send-arguments.ts @@ -0,0 +1,25 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const socket = dgram.createSocket("udp4"); +const buffer = Buffer.from("hello"); + +console.log("missing message:", codeOf(() => socket.send())); +console.log("number message:", codeOf(() => socket.send(23, 12345, "127.0.0.1"))); +console.log( + "bad list member:", + codeOf(() => socket.send([buffer, 23] as never, 12345, "127.0.0.1")), +); +console.log("port zero:", codeOf(() => socket.send(buffer, 0, "127.0.0.1"))); +console.log("port high:", codeOf(() => socket.send(buffer, 65536, "127.0.0.1"))); + +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node-suite/dgram/validation/sendto.ts b/test-parity/node-suite/dgram/validation/sendto.ts new file mode 100644 index 0000000000..2fdb437f1f --- /dev/null +++ b/test-parity/node-suite/dgram/validation/sendto.ts @@ -0,0 +1,31 @@ +import * as dgram from "node:dgram"; + +function codeOf(fn: () => unknown): string { + try { + fn(); + return "none"; + } catch (error: unknown) { + return (error as { code?: string; name?: string }).code ?? + (error as { name?: string }).name ?? "Error"; + } +} + +const socket = dgram.createSocket("udp4"); +console.log("missing args:", codeOf(() => socket.sendto())); +console.log( + "bad offset:", + codeOf(() => socket.sendto("buffer", "offset" as never, 1, 12345, "127.0.0.1")), +); +console.log( + "bad length:", + codeOf(() => socket.sendto("buffer", 1, "length" as never, 12345, "127.0.0.1")), +); +console.log( + "bad port:", + codeOf(() => socket.sendto("buffer", 1, 1, false as never, "127.0.0.1")), +); +console.log( + "bad address:", + codeOf(() => socket.sendto("buffer", 1, 1, 12345, false as never)), +); +await new Promise((resolve) => socket.close(() => resolve())); diff --git a/test-parity/node_suite_baseline.json b/test-parity/node_suite_baseline.json index 922c40b052..eb8ea749d2 100644 --- a/test-parity/node_suite_baseline.json +++ b/test-parity/node_suite_baseline.json @@ -2,7 +2,7 @@ "_schema": { "description": "Floor baseline for scripts/node_suite_regression_check.py. Each module's run must produce pass >= floor.pass; dropping below is a regression (exit 1). Improvements are always accepted and reported as ratchet candidates. Captured in the node-26 environment with scripts/node_suite_run.py (pre-warm + fast/slow lanes).", "oracle": "node v26.5.0 (pinned by .node-version)", - "note": "Deterministic modules are floored at full pass. Timing/racy modules (http2, net, stream, diagnostics_channel, fs-promises) carry a small margin below observed pass so ordinary flake does not false-alarm; the guard still catches real regressions, which are large (e.g. dns 6->0, http 19->9). node_suite_run.normalize() scrubs environment-variant tokens (console.time hrtime durations, stack-trace frame lines) symmetrically before the stdout compare, so console is floored at full pass (119) on its deterministic content. http is verified 19/19 in isolation but the full-suite harness flakes to 17 under port contention, so it is floored at 17 (flake margin, not a regression); a real http break is a much larger drop. Floors were refreshed from a clean node-26 run at 2810/2863 (98.1%), then the deterministic child_process floor was measured independently at 43/53 on Node 26.5.0. The overall summary is the sum of the committed per-module floors." + "note": "Deterministic modules are floored at full pass. Timing/racy modules (http2, net, stream, diagnostics_channel, fs-promises) carry a small margin below observed pass so ordinary flake does not false-alarm; the guard still catches real regressions, which are large (e.g. dns 6->0, http 19->9). node_suite_run.normalize() scrubs environment-variant tokens (console.time hrtime durations, stack-trace frame lines) symmetrically before the stdout compare, so console is floored at full pass (119) on its deterministic content. http is verified 19/19 in isolation but the full-suite harness flakes to 17 under port contention, so it is floored at 17 (flake margin, not a regression); a real http break is a much larger drop. Floors were refreshed from a clean node-26 run at 2810/2863 (98.1%), then the deterministic child_process floor was measured independently at 43/53 on Node 26.5.0, and dgram is ratcheted independently from a clean focused baseline run. The overall summary is the sum of the committed per-module floors." }, "overall": { "pass": 2818, @@ -47,8 +47,8 @@ "total": 242 }, "dgram": { - "pass": 4, - "total": 4 + "pass": 18, + "total": 38 }, "diagnostics_channel": { "pass": 66,