socketduct 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/AGENTS.md +2 -2
  2. package/changelog.d/20261010130509-bound-log-frame-compression-memory.md +1 -0
  3. package/docs/runbooks/jsonl-integrity-rollout.md +8 -6
  4. package/package.json +2 -2
  5. package/scripts/test.js +9 -14
  6. package/src/client/multiplexed-transport-set.js +20 -2
  7. package/src/logging/compressed-log-writer.js +1 -1
  8. package/src/logging/zstd-frame-codec.js +15 -1
  9. package/src/reverse/client.js +44 -1
  10. package/src/reverse/hub.js +10 -1
  11. package/src/reverse/runtime.js +11 -2
  12. package/test/e2e/gateway-process.test.js +93 -92
  13. package/test/e2e/logger-shutdown-process.test.js +40 -37
  14. package/test/e2e/reverse-client-recovery-process.test.js +107 -103
  15. package/test/e2e/reverse-gateway-process.test.js +328 -318
  16. package/test/integration/initial-target-recovery.test.js +333 -334
  17. package/test/integration/logging-cli.test.js +121 -119
  18. package/test/integration/logging-compressed-recovery.test.js +96 -95
  19. package/test/integration/multiplexed-compatibility.test.js +178 -177
  20. package/test/integration/multiplexed-corrections.test.js +360 -359
  21. package/test/integration/multiplexed-failures.test.js +253 -251
  22. package/test/integration/multiplexed-transport-set.test.js +334 -322
  23. package/test/integration/multiplexed-transport.test.js +135 -134
  24. package/test/integration/reverse-gateway-stream-id-reset.test.js +90 -89
  25. package/test/integration/reverse-gateway.test.js +2309 -2218
  26. package/test/integration/reverse-resume-continuity.test.js +219 -218
  27. package/test/integration/reverse-route-class.test.js +363 -278
  28. package/test/integration/socketduct-http-agent.test.js +314 -312
  29. package/test/integration/socketduct-https-agent.test.js +516 -485
  30. package/test/integration/stream-compression.test.js +862 -824
  31. package/test/integration/tcp-forwarding.test.js +2550 -2447
  32. package/test/integration/tls-forwarding.test.js +593 -592
  33. package/test/unit/auth/auth.test.js +43 -44
  34. package/test/unit/auth/static-token-store.test.js +9 -8
  35. package/test/unit/client/connect.test.js +41 -40
  36. package/test/unit/client/deferred-virtual-socket.test.js +95 -94
  37. package/test/unit/client/multiplexed-spool-budget.test.js +155 -154
  38. package/test/unit/client/multiplexed-transport-set.test.js +845 -732
  39. package/test/unit/client/socketduct-socket.test.js +768 -771
  40. package/test/unit/client/virtual-socket.test.js +198 -168
  41. package/test/unit/config/docker-gateway-files.test.js +224 -224
  42. package/test/unit/config/environment.test.js +472 -492
  43. package/test/unit/index.test.js +53 -52
  44. package/test/unit/logging/compressed-log-worker.test.js +23 -22
  45. package/test/unit/logging/compressed-log-writer.test.js +155 -154
  46. package/test/unit/logging/log-retention.test.js +52 -47
  47. package/test/unit/logging/log-segment-index.test.js +84 -81
  48. package/test/unit/logging/zstd-frame-codec.test.js +28 -19
  49. package/test/unit/protocol/compression.test.js +254 -298
  50. package/test/unit/protocol/frame.test.js +280 -276
  51. package/test/unit/protocol/v3-compression.test.js +21 -23
  52. package/test/unit/protocol/v3-frame.test.js +42 -41
  53. package/test/unit/release-configuration.test.js +15 -12
  54. package/test/unit/reverse/disconnect-attribution.test.js +65 -64
  55. package/test/unit/reverse-terminal-outcomes.test.js +53 -50
  56. package/test/unit/server/control-handler.test.js +232 -231
  57. package/test/unit/server/primary.test.js +51 -49
  58. package/test/unit/server/relay-runtime.test.js +280 -294
  59. package/test/unit/sessions/session.test.js +48 -62
  60. package/test/unit/spool/spool-file.test.js +401 -385
@@ -1,5 +1,4 @@
1
- import assert from "node:assert/strict"
2
- import test from "node:test"
1
+ import {describe, expect, it} from "@velocious/testing"
3
2
 
4
3
  import {
5
4
  connectSocketduct,
@@ -23,61 +22,63 @@ import {connectSocketduct as directConnectSocketduct, SocketductSocket as Direct
23
22
  import {createJsonlFileLogger as directCreateJsonlFileLogger} from "../../src/logging/structured-logger.js"
24
23
  import {createCompressedJsonlFileLogger as directCreateCompressedJsonlFileLogger} from "../../src/logging/compressed-log-writer.js"
25
24
 
26
- test("getStartupSummary returns the Socketduct service identity and default mode", () => {
27
- assert.deepEqual(getStartupSummary(), {
28
- name: "Socketduct",
29
- runtime: "node",
30
- mode: "single-worker"
25
+ describe("public index exports", () => {
26
+ it("getStartupSummary returns the Socketduct service identity and default mode", () => {
27
+ expect(getStartupSummary()).toEqual({
28
+ name: "Socketduct",
29
+ runtime: "node",
30
+ mode: "single-worker"
31
+ })
31
32
  })
32
- })
33
33
 
34
- test("index exports the public SocketductSocket client API", () => {
35
- assert.equal(SocketductSocket, DirectSocketductSocket)
36
- assert.equal(connectSocketduct, directConnectSocketduct)
37
- assert.equal(createSocketductConnectionFactory, directCreateSocketductConnectionFactory)
38
- assert.equal(createJsonlFileLogger, directCreateJsonlFileLogger)
39
- assert.equal(createCompressedJsonlFileLogger, directCreateCompressedJsonlFileLogger)
40
- assert.equal(SocketductHttpAgent, DirectSocketductHttpAgent)
41
- })
34
+ it("index exports the public SocketductSocket client API", () => {
35
+ expect(SocketductSocket).toBe(DirectSocketductSocket)
36
+ expect(connectSocketduct).toBe(directConnectSocketduct)
37
+ expect(createSocketductConnectionFactory).toBe(directCreateSocketductConnectionFactory)
38
+ expect(createJsonlFileLogger).toBe(directCreateJsonlFileLogger)
39
+ expect(createCompressedJsonlFileLogger).toBe(directCreateCompressedJsonlFileLogger)
40
+ expect(SocketductHttpAgent).toBe(DirectSocketductHttpAgent)
41
+ })
42
42
 
43
- test("package exports expose dedicated HTTP and HTTPS adapter entrypoints without an HTTPS barrel export", async () => {
44
- const socketduct = await import("socketduct")
45
- const socketductHttp = await import("socketduct/http")
46
- const socketductHttps = await import("socketduct/https")
47
- const {SocketductHttpsAgent} = await import("../../src/client/https-agent.js")
43
+ it("package exports expose dedicated HTTP and HTTPS adapter entrypoints without an HTTPS barrel export", async () => {
44
+ const socketduct = await import("socketduct")
45
+ const socketductHttp = await import("socketduct/http")
46
+ const socketductHttps = await import("socketduct/https")
47
+ const {SocketductHttpsAgent} = await import("../../src/client/https-agent.js")
48
48
 
49
- assert.equal(socketduct.SocketductHttpAgent, DirectSocketductHttpAgent)
50
- assert.equal(socketductHttp.SocketductHttpAgent, DirectSocketductHttpAgent)
51
- assert.equal(socketductHttps.SocketductHttpsAgent, SocketductHttpsAgent)
52
- assert.equal("SocketductHttpsAgent" in socketduct, false)
53
- })
49
+ expect(socketduct.SocketductHttpAgent).toBe(DirectSocketductHttpAgent)
50
+ expect(socketductHttp.SocketductHttpAgent).toBe(DirectSocketductHttpAgent)
51
+ expect(socketductHttps.SocketductHttpsAgent).toBe(SocketductHttpsAgent)
52
+ expect("SocketductHttpsAgent" in socketduct).toBe(false)
53
+ })
54
54
 
55
- test("package exports the protocol-v3 pooled connection and relay entrypoints", async () => {
56
- const multiplexed = await import("socketduct/multiplexed")
57
- const relay = await import("socketduct/relay")
55
+ it("package exports the protocol-v3 pooled connection and relay entrypoints", async () => {
56
+ const multiplexed = await import("socketduct/multiplexed")
57
+ const relay = await import("socketduct/relay")
58
58
 
59
- assert.equal(multiplexed.MultiplexedSocketductConnection, MultiplexedSocketductConnection)
60
- assert.equal(multiplexed.MultiplexedSocketductTransportSet, MultiplexedSocketductTransportSet)
61
- assert.equal(multiplexed.SocketductRecoveryLookupError, SocketductRecoveryLookupError)
62
- assert.equal(createMultiplexedSocketductConnection({
63
- relay: {host: "127.0.0.1", port: 3100, token: "test-token"}
64
- }) instanceof MultiplexedSocketductConnection, true)
65
- assert.equal(createMultiplexedSocketductTransportSet({
66
- connectionId: "process_identity_01",
67
- spoolDirectory: "/tmp/socketduct-index-test",
68
- maxTotalBufferBytes: 1024,
69
- maxTotalSpoolBytes: 1024,
70
- maxTotalQueuedBytes: 2048,
71
- gateways: [{gatewayId: "starlink", endpoint: {relay: {host: "127.0.0.1", port: 3100, token: "test-token"}}}]
72
- }) instanceof MultiplexedSocketductTransportSet, true)
73
- assert.equal(typeof relay.startMultiplexedTcpRelay, "function")
74
- })
59
+ expect(multiplexed.MultiplexedSocketductConnection).toBe(MultiplexedSocketductConnection)
60
+ expect(multiplexed.MultiplexedSocketductTransportSet).toBe(MultiplexedSocketductTransportSet)
61
+ expect(multiplexed.SocketductRecoveryLookupError).toBe(SocketductRecoveryLookupError)
62
+ expect(createMultiplexedSocketductConnection({
63
+ relay: {host: "127.0.0.1", port: 3100, token: "test-token"}
64
+ }) instanceof MultiplexedSocketductConnection).toBe(true)
65
+ expect(createMultiplexedSocketductTransportSet({
66
+ connectionId: "process_identity_01",
67
+ spoolDirectory: "/tmp/socketduct-index-test",
68
+ maxTotalBufferBytes: 1024,
69
+ maxTotalSpoolBytes: 1024,
70
+ maxTotalQueuedBytes: 2048,
71
+ gateways: [{gatewayId: "starlink", endpoint: {relay: {host: "127.0.0.1", port: 3100, token: "test-token"}}}]
72
+ }) instanceof MultiplexedSocketductTransportSet).toBe(true)
73
+ expect(typeof relay.startMultiplexedTcpRelay).toBe("function")
74
+ })
75
75
 
76
- test("package exports native reverse hub, gateway, and client entrypoints", async () => {
77
- const reverse = await import("socketduct/reverse")
78
- const relay = await import("socketduct/relay")
79
- assert.equal(reverse.OutboundReverseGateway, OutboundReverseGateway)
80
- assert.equal(reverse.ReverseGatewayConnection, ReverseGatewayConnection)
81
- assert.equal(typeof reverse.startReverseGatewayHub, "function")
82
- assert.equal(relay.startReverseGatewayHub, reverse.startReverseGatewayHub)
76
+ it("package exports native reverse hub, gateway, and client entrypoints", async () => {
77
+ const reverse = await import("socketduct/reverse")
78
+ const relay = await import("socketduct/relay")
79
+ expect(reverse.OutboundReverseGateway).toBe(OutboundReverseGateway)
80
+ expect(reverse.ReverseGatewayConnection).toBe(ReverseGatewayConnection)
81
+ expect(typeof reverse.startReverseGatewayHub).toBe("function")
82
+ expect(relay.startReverseGatewayHub).toBe(reverse.startReverseGatewayHub)
83
+ })
83
84
  })
@@ -1,29 +1,30 @@
1
- import assert from "node:assert/strict"
2
- import test from "node:test"
1
+ import {describe, expect, it} from "@velocious/testing"
3
2
 
4
3
  import {writeAll} from "../../../src/logging/compressed-log-worker.js"
5
4
 
6
- test("worker positional writes preserve every byte across all short-write offsets", async () => {
7
- const source = Buffer.from("short-write-safe-durable-frame")
8
- for (let maximum = 1; maximum <= source.length; maximum += 1) {
9
- const destination = Buffer.alloc(source.length + 7)
10
- /** @type {number[]} */
11
- const positions = []
12
- const handle = {
13
- async write(/** @type {Buffer} */ bytes, /** @type {number} */ offset, /** @type {number} */ length, /** @type {number} */ position) {
14
- const bytesWritten = Math.min(maximum, length)
15
- positions.push(position)
16
- Buffer.from(bytes).copy(destination, position, offset, offset + bytesWritten)
17
- return {bytesWritten, buffer: bytes}
5
+ describe("compressed log worker", () => {
6
+ it("worker positional writes preserve every byte across all short-write offsets", async () => {
7
+ const source = Buffer.from("short-write-safe-durable-frame")
8
+ for (let maximum = 1; maximum <= source.length; maximum += 1) {
9
+ const destination = Buffer.alloc(source.length + 7)
10
+ /** @type {number[]} */
11
+ const positions = []
12
+ const handle = {
13
+ async write(/** @type {Buffer} */ bytes, /** @type {number} */ offset, /** @type {number} */ length, /** @type {number} */ position) {
14
+ const bytesWritten = Math.min(maximum, length)
15
+ positions.push(position)
16
+ Buffer.from(bytes).copy(destination, position, offset, offset + bytesWritten)
17
+ return {bytesWritten, buffer: bytes}
18
+ }
18
19
  }
20
+ await writeAll(/** @type {import('node:fs/promises').FileHandle} */ (handle), source, 7)
21
+ expect(destination.subarray(7).equals(source)).toBe(true)
22
+ expect(positions).toEqual(Array.from({length: Math.ceil(source.length / maximum)}, (_, index) => 7 + index * maximum))
19
23
  }
20
- await writeAll(/** @type {import('node:fs/promises').FileHandle} */ (handle), source, 7)
21
- assert.deepEqual(destination.subarray(7), source)
22
- assert.deepEqual(positions, Array.from({length: Math.ceil(source.length / maximum)}, (_, index) => 7 + index * maximum))
23
- }
24
- })
24
+ })
25
25
 
26
- test("worker positional writes fail loudly on zero progress", async () => {
27
- const handle = {async write(/** @type {Buffer} */ bytes) { return {bytesWritten: 0, buffer: bytes} }}
28
- await assert.rejects(() => writeAll(/** @type {import('node:fs/promises').FileHandle} */ (handle), Buffer.from("frame"), 0), /no progress/)
26
+ it("worker positional writes fail loudly on zero progress", async () => {
27
+ const handle = {async write(/** @type {Buffer} */ bytes) { return {bytesWritten: 0, buffer: bytes} }}
28
+ await expect(writeAll(/** @type {import('node:fs/promises').FileHandle} */ (handle), Buffer.from("frame"), 0)).rejects.toThrow(/no progress/)
29
+ })
29
30
  })
@@ -1,175 +1,176 @@
1
- import assert from "node:assert/strict"
1
+ import {describe, expect, it, withTestCleanup} from "@velocious/testing"
2
2
  import {mkdtemp, readdir, rm, stat} from "node:fs/promises"
3
3
  import {tmpdir} from "node:os"
4
4
  import {join} from "node:path"
5
- import test from "node:test"
6
5
 
7
6
  import {readCompressedLogDirectory} from "../../../src/logging/compressed-log-reader.js"
8
7
  import {createCompressedJsonlFileLogger} from "../../../src/logging/compressed-log-writer.js"
9
8
 
10
- test("compressed logger durably acknowledges ordered records and closes asynchronously", async (t) => {
11
- const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-writer-"))
12
- const directory = join(root, "logs")
13
- let tick = 0
14
- const logger = createCompressedJsonlFileLogger({
15
- directory,
16
- role: "reverse-hub",
17
- now: () => new Date(1_795_000_000_000 + tick++),
18
- frameMaxBytes: 256,
19
- flushIntervalMs: 60_000,
20
- criticalFlushIntervalMs: 60_000,
21
- maxSegmentBytes: 2048
22
- })
23
- t.after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
9
+ describe("compressed log writer", () => {
10
+ it("compressed logger durably acknowledges ordered records and closes asynchronously", async () => withTestCleanup(async (after) => {
11
+ const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-writer-"))
12
+ const directory = join(root, "logs")
13
+ let tick = 0
14
+ const logger = createCompressedJsonlFileLogger({
15
+ directory,
16
+ role: "reverse-hub",
17
+ now: () => new Date(1_795_000_000_000 + tick++),
18
+ frameMaxBytes: 256,
19
+ flushIntervalMs: 60_000,
20
+ criticalFlushIntervalMs: 60_000,
21
+ maxSegmentBytes: 2048
22
+ })
23
+ after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
24
24
 
25
- assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub", token: "must-not-survive"}), "accepted")
26
- assert.equal(logger.log("runtime.status", {virtualStreams: 4}), "accepted")
27
- assert.equal(logger.log("runtime.status", {virtualStreams: 5}), "accepted")
28
- assert.equal(logger.status().acceptedHighWater, "3")
29
- await logger.flush()
25
+ expect(logger.log("process.started", {runtimeMode: "reverse-hub", token: "must-not-survive"})).toBe("accepted")
26
+ expect(logger.log("runtime.status", {virtualStreams: 4})).toBe("accepted")
27
+ expect(logger.log("runtime.status", {virtualStreams: 5})).toBe("accepted")
28
+ expect(logger.status().acceptedHighWater).toBe("3")
29
+ await logger.flush()
30
30
 
31
- const durable = logger.status()
32
- assert(Number(durable.durableHighWater) >= 3)
33
- assert.equal(durable.health, "healthy")
34
- const result = await readCompressedLogDirectory(directory)
35
- assert.equal(result.incomplete, false)
36
- assert.deepEqual(result.records.map((record) => record.eventSequence), Array.from({length: result.records.length}, (_, index) => String(index + 1)))
37
- assert.deepEqual(result.records.filter((record) => !String(record.event).startsWith("log_")).map((record) => record.event), ["process.started", "runtime.status", "runtime.status"])
38
- assert.equal(new Set(result.records.map((record) => record.writerEpoch)).size, 1)
39
- assert.doesNotMatch(JSON.stringify(result.records), /must-not-survive/)
31
+ const durable = logger.status()
32
+ expect(Number(durable.durableHighWater) >= 3).toBeTruthy()
33
+ expect(durable.health).toBe("healthy")
34
+ const result = await readCompressedLogDirectory(directory)
35
+ expect(result.incomplete).toBe(false)
36
+ expect(result.records.map((record) => record.eventSequence)).toEqual(Array.from({length: result.records.length}, (_, index) => String(index + 1)))
37
+ expect(result.records.filter((record) => !String(record.event).startsWith("log_")).map((record) => record.event)).toEqual(["process.started", "runtime.status", "runtime.status"])
38
+ expect(new Set(result.records.map((record) => record.writerEpoch)).size).toBe(1)
39
+ expect(JSON.stringify(result.records)).not.toMatch(/must-not-survive/)
40
40
 
41
- const filesBeforeClose = await readdir(directory)
42
- assert(filesBeforeClose.some((name) => name.endsWith(".jsonl.zst")))
43
- assert(filesBeforeClose.some((name) => name.endsWith(".jsonl.zst.idx")))
44
- assert.equal(filesBeforeClose.some((name) => name.endsWith(".jsonl")), false)
45
- assert.equal((await stat(directory)).mode & 0o777, 0o700)
46
- for (const name of filesBeforeClose) assert.equal((await stat(join(directory, name))).mode & 0o777, 0o600)
41
+ const filesBeforeClose = await readdir(directory)
42
+ expect(filesBeforeClose.some((name) => name.endsWith(".jsonl.zst"))).toBeTruthy()
43
+ expect(filesBeforeClose.some((name) => name.endsWith(".jsonl.zst.idx"))).toBeTruthy()
44
+ expect(filesBeforeClose.some((name) => name.endsWith(".jsonl"))).toBe(false)
45
+ expect((await stat(directory)).mode & 0o777).toBe(0o700)
46
+ for (const name of filesBeforeClose) expect((await stat(join(directory, name))).mode & 0o777).toBe(0o600)
47
47
 
48
- const firstClose = logger.close()
49
- const secondClose = logger.close()
50
- assert.equal(firstClose, secondClose)
51
- const finalStatus = await firstClose
52
- assert.equal(finalStatus.state, "closed")
53
- assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub"}), "dropped")
54
- assert((await readdir(directory)).some((name) => name.endsWith(".jsonl.zst.sealed")))
55
- const closed = await readCompressedLogDirectory(directory)
56
- assert(closed.records.some((record) => record.event === "log_batch_accepted"))
57
- assert(closed.records.some((record) => record.event === "log_frame_committed"))
58
- assert.equal(closed.records.at(-1)?.event, "log_closed")
59
- })
48
+ const firstClose = logger.close()
49
+ const secondClose = logger.close()
50
+ expect(firstClose).toBe(secondClose)
51
+ const finalStatus = await firstClose
52
+ expect(finalStatus.state).toBe("closed")
53
+ expect(logger.log("process.started", {runtimeMode: "reverse-hub"})).toBe("dropped")
54
+ expect((await readdir(directory)).some((name) => name.endsWith(".jsonl.zst.sealed"))).toBeTruthy()
55
+ const closed = await readCompressedLogDirectory(directory)
56
+ expect(closed.records.some((record) => record.event === "log_batch_accepted")).toBeTruthy()
57
+ expect(closed.records.some((record) => record.event === "log_frame_committed")).toBeTruthy()
58
+ expect(closed.records.at(-1)?.event).toBe("log_closed")
59
+ }))
60
60
 
61
- test("compressed logger enforces one shared bounded queue with a critical reserve", async (t) => {
62
- const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-pressure-"))
63
- const directory = join(root, "logs")
64
- // The reserve must cover the critical event's conservative charge (line bytes × 3 + 128);
65
- // an accepted-but-undelivered sequence can never be evicted without tearing the durable journal.
66
- const logger = createCompressedJsonlFileLogger({
67
- directory,
68
- role: "reverse-client",
69
- maxQueueBytes: 4096,
70
- criticalReserveBytes: 2048,
71
- frameMaxBytes: 1024,
72
- flushIntervalMs: 60_000,
73
- criticalFlushIntervalMs: 60_000
74
- })
75
- t.after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
61
+ it("compressed logger enforces one shared bounded queue with a critical reserve", async () => withTestCleanup(async (after) => {
62
+ const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-pressure-"))
63
+ const directory = join(root, "logs")
64
+ // The reserve must cover the critical event's conservative charge (line bytes × 3 + 128);
65
+ // an accepted-but-undelivered sequence can never be evicted without tearing the durable journal.
66
+ const logger = createCompressedJsonlFileLogger({
67
+ directory,
68
+ role: "reverse-client",
69
+ maxQueueBytes: 4096,
70
+ criticalReserveBytes: 2048,
71
+ frameMaxBytes: 1024,
72
+ flushIntervalMs: 60_000,
73
+ criticalFlushIntervalMs: 60_000
74
+ })
75
+ after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
76
76
 
77
- const outcomes = Array.from({length: 40}, () => logger.log("runtime.status", {virtualStreams: 1}))
78
- assert(outcomes.includes("dropped") || outcomes.includes("coalesced"))
79
- assert(logger.status().queuedBytes <= 4096)
80
- assert.equal(logger.log("transport.disconnected", {
81
- clientConnectionGeneration: 1,
82
- disconnectIncidentId: "AAAAAAAAAAAAAAAAAAAAAA",
83
- disconnectReason: "remote_eof",
84
- incidentOrigin: "reverse_client",
85
- physicalRole: "client",
86
- fanOutCount: 1,
87
- resumeSuccessCount: 0,
88
- terminalStreamCount: 0
89
- }), "accepted")
90
- assert(logger.status().queuedBytes <= 4096)
91
- await logger.flush()
92
- })
77
+ const outcomes = Array.from({length: 40}, () => logger.log("runtime.status", {virtualStreams: 1}))
78
+ expect(outcomes.includes("dropped") || outcomes.includes("coalesced")).toBeTruthy()
79
+ expect(logger.status().queuedBytes <= 4096).toBeTruthy()
80
+ expect(logger.log("transport.disconnected", {
81
+ clientConnectionGeneration: 1,
82
+ disconnectIncidentId: "AAAAAAAAAAAAAAAAAAAAAA",
83
+ disconnectReason: "remote_eof",
84
+ incidentOrigin: "reverse_client",
85
+ physicalRole: "client",
86
+ fanOutCount: 1,
87
+ resumeSuccessCount: 0,
88
+ terminalStreamCount: 0
89
+ })).toBe("accepted")
90
+ expect(logger.status().queuedBytes <= 4096).toBeTruthy()
91
+ await logger.flush()
92
+ }))
93
93
 
94
- test("compressed logger caps worker restarts and retains only bounded critical evidence after failure", async (t) => {
95
- const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-worker-death-"))
96
- const directory = join(root, "logs")
97
- const logger = createCompressedJsonlFileLogger({
98
- directory,
99
- role: "reverse-hub",
100
- frameMaxBytes: 256,
101
- maxSegmentBytes: 1024,
102
- maxQueueBytes: 65_536,
103
- criticalReserveBytes: 8192,
104
- flushIntervalMs: 60_000,
105
- criticalFlushIntervalMs: 60_000,
106
- stderr: {write: () => true}
107
- })
108
- t.after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
109
- logger.log("process.started", {runtimeMode: "reverse-hub"})
110
- await logger.flush()
111
- await rm(directory, {recursive: true, force: true})
94
+ it("compressed logger caps worker restarts and retains only bounded critical evidence after failure", async () => withTestCleanup(async (after) => {
95
+ const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-worker-death-"))
96
+ const directory = join(root, "logs")
97
+ const logger = createCompressedJsonlFileLogger({
98
+ directory,
99
+ role: "reverse-hub",
100
+ frameMaxBytes: 256,
101
+ maxSegmentBytes: 1024,
102
+ maxQueueBytes: 65_536,
103
+ criticalReserveBytes: 8192,
104
+ flushIntervalMs: 60_000,
105
+ criticalFlushIntervalMs: 60_000,
106
+ stderr: {write: () => true}
107
+ })
108
+ after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
109
+ logger.log("process.started", {runtimeMode: "reverse-hub"})
110
+ await logger.flush()
111
+ await rm(directory, {recursive: true, force: true})
112
112
 
113
- for (let index = 0; index < 20; index += 1) logger.log("process.started", {runtimeMode: "reverse-hub"})
114
- await assert.rejects(() => logger.flush())
115
- assert.equal(logger.status().state, "failed")
116
- assert.equal(logger.status().workerRestarts, 3)
117
- assert.equal(logger.log("runtime.status", {virtualStreams: 1}), "dropped")
118
- assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub"}), "accepted")
119
- assert(logger.status().allocatedBytes <= 65_536)
120
- })
113
+ for (let index = 0; index < 20; index += 1) logger.log("process.started", {runtimeMode: "reverse-hub"})
114
+ await expect(logger.flush()).rejects.toThrow()
115
+ expect(logger.status().state).toBe("failed")
116
+ expect(logger.status().workerRestarts).toBe(3)
117
+ expect(logger.log("runtime.status", {virtualStreams: 1})).toBe("dropped")
118
+ expect(logger.log("process.started", {runtimeMode: "reverse-hub"})).toBe("accepted")
119
+ expect(logger.status().allocatedBytes <= 65_536).toBeTruthy()
120
+ }))
121
121
 
122
- test("compressed logger flush is a caller-time durability barrier", async (t) => {
123
- const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-barrier-"))
124
- const logger = createCompressedJsonlFileLogger({
125
- directory: join(root, "logs"),
126
- role: "reverse-hub",
127
- frameMaxBytes: 256,
128
- flushIntervalMs: 60_000,
129
- criticalFlushIntervalMs: 60_000,
130
- maxQueueBytes: 1_000_000,
131
- criticalReserveBytes: 1024
132
- })
133
- t.after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
134
- await logger.flush()
135
- assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub"}), "accepted")
136
- const barrier = logger.flush()
137
- for (let index = 0; index < 8; index += 1) assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub"}), "accepted")
138
- await barrier
139
- const status = logger.status()
140
- assert(BigInt(status.durableHighWater) >= 1n)
141
- assert(BigInt(status.durableHighWater) < BigInt(status.acceptedHighWater))
142
- })
122
+ it("compressed logger flush is a caller-time durability barrier", async () => withTestCleanup(async (after) => {
123
+ const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-barrier-"))
124
+ const logger = createCompressedJsonlFileLogger({
125
+ directory: join(root, "logs"),
126
+ role: "reverse-hub",
127
+ frameMaxBytes: 256,
128
+ flushIntervalMs: 60_000,
129
+ criticalFlushIntervalMs: 60_000,
130
+ maxQueueBytes: 1_000_000,
131
+ criticalReserveBytes: 1024
132
+ })
133
+ after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
134
+ await logger.flush()
135
+ expect(logger.log("process.started", {runtimeMode: "reverse-hub"})).toBe("accepted")
136
+ const barrier = logger.flush()
137
+ for (let index = 0; index < 8; index += 1) expect(logger.log("process.started", {runtimeMode: "reverse-hub"})).toBe("accepted")
138
+ await barrier
139
+ const status = logger.status()
140
+ expect(BigInt(status.durableHighWater) >= 1n).toBeTruthy()
141
+ expect(BigInt(status.durableHighWater) < BigInt(status.acceptedHighWater)).toBeTruthy()
142
+ }))
143
143
 
144
- test("compressed logger flushes immediately at frame size and critical deadlines do not slide", async (t) => {
145
- const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-deadline-"))
146
- const directory = join(root, "logs")
147
- const logger = createCompressedJsonlFileLogger({
148
- directory,
149
- role: "reverse-hub",
150
- frameMaxBytes: 256,
151
- flushIntervalMs: 60_000,
152
- criticalFlushIntervalMs: 40,
153
- maxQueueBytes: 1_000_000,
154
- criticalReserveBytes: 1024
155
- })
156
- t.after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
157
- await logger.flush()
144
+ it("compressed logger flushes immediately at frame size and critical deadlines do not slide", async () => withTestCleanup(async (after) => {
145
+ const root = await mkdtemp(join(tmpdir(), "socketduct-compressed-deadline-"))
146
+ const directory = join(root, "logs")
147
+ const logger = createCompressedJsonlFileLogger({
148
+ directory,
149
+ role: "reverse-hub",
150
+ frameMaxBytes: 256,
151
+ flushIntervalMs: 60_000,
152
+ criticalFlushIntervalMs: 40,
153
+ maxQueueBytes: 1_000_000,
154
+ criticalReserveBytes: 1024
155
+ })
156
+ after(async () => { await logger.close().catch(() => {}); await rm(root, {recursive: true, force: true}) })
157
+ await logger.flush()
158
158
 
159
- assert.equal(logger.log("runtime.status", {virtualStreams: 1}), "accepted")
160
- assert.equal(logger.log("process.started", {runtimeMode: "reverse-hub"}), "accepted")
161
- await waitUntil(() => BigInt(logger.status().durableHighWater) > 0n, 1_000)
159
+ expect(logger.log("runtime.status", {virtualStreams: 1})).toBe("accepted")
160
+ expect(logger.log("process.started", {runtimeMode: "reverse-hub"})).toBe("accepted")
161
+ await waitUntil(() => BigInt(logger.status().durableHighWater) > 0n, 1_000)
162
162
 
163
- await logger.flush()
164
- const before = BigInt(logger.status().durableHighWater)
165
- const producer = setInterval(() => logger.log("process.started", {runtimeMode: "reverse-hub"}), 5)
166
- try {
167
- await waitUntil(() => BigInt(logger.status().durableHighWater) > before, 500)
168
- } finally {
169
- clearInterval(producer)
170
- }
171
- const durable = await readCompressedLogDirectory(directory)
172
- assert(durable.records.some((record) => record.event === "runtime.status"))
163
+ await logger.flush()
164
+ const before = BigInt(logger.status().durableHighWater)
165
+ const producer = setInterval(() => logger.log("process.started", {runtimeMode: "reverse-hub"}), 5)
166
+ try {
167
+ await waitUntil(() => BigInt(logger.status().durableHighWater) > before, 500)
168
+ } finally {
169
+ clearInterval(producer)
170
+ }
171
+ const durable = await readCompressedLogDirectory(directory)
172
+ expect(durable.records.some((record) => record.event === "runtime.status")).toBeTruthy()
173
+ }))
173
174
  })
174
175
 
175
176
  /**
@@ -179,7 +180,7 @@ test("compressed logger flushes immediately at frame size and critical deadlines
179
180
  async function waitUntil(predicate, timeoutMs) {
180
181
  const deadline = Date.now() + timeoutMs
181
182
  while (!predicate()) {
182
- if (Date.now() >= deadline) assert.fail("timed out waiting for compressed logger progress")
183
+ if (Date.now() >= deadline) throw new Error("timed out waiting for compressed logger progress")
183
184
  await new Promise((resolve) => setTimeout(resolve, 5))
184
185
  }
185
186
  }