From cd22aa3a514d72ef86c3cd1eb52d32b1ddcbc0ce Mon Sep 17 00:00:00 2001 From: Serhiy Morenko Date: Thu, 9 Jul 2026 21:52:24 +0200 Subject: [PATCH 1/4] test: reach 100% line coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'skips interface entries with no addresses' spec patched networkInterfaces on the CommonJS os object, which the module-mocked ESM binding never reads — the test passed against the real interfaces (false positive) and the undefined-entry guard in UDPWorker.selectNetworkInterface() stayed uncovered. The os mock now routes networkInterfaces through a mutable holder (networkInterfacesMock.impl) that tests swap at call time, so the mocked binding observes the fixture. Line coverage: 2567/2567 (100%). --- test/mocks/os.ts | 11 ++++++++++- test/unit/UDPWorker.spec.ts | 14 +++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/test/mocks/os.ts b/test/mocks/os.ts index d3484f2..ff7050b 100644 --- a/test/mocks/os.ts +++ b/test/mocks/os.ts @@ -22,7 +22,7 @@ import os from 'node:os'; import { mockBuiltin } from './mockBuiltin.js'; -export const networkInterfaces = () => { +const defaultInterfaces = () => { return { lo: [ { @@ -37,4 +37,13 @@ export const networkInterfaces = () => { }; }; +// mutable holder: the module mock binds `networkInterfaces` once at +// registration, so tests swap the implementation through this object +// (patching the CommonJS os object would not reach the mocked binding) +export const networkInterfacesMock: { impl: () => any } = { + impl: defaultInterfaces, +}; + +export const networkInterfaces = () => networkInterfacesMock.impl(); + mockBuiltin('os', { ...os, networkInterfaces }); diff --git a/test/unit/UDPWorker.spec.ts b/test/unit/UDPWorker.spec.ts index e18365d..69520fa 100644 --- a/test/unit/UDPWorker.spec.ts +++ b/test/unit/UDPWorker.spec.ts @@ -25,10 +25,10 @@ import '../mocks/index.js'; import { describe, it, mock } from 'node:test'; import assert from 'node:assert/strict'; import { EventEmitter } from 'node:events'; -// import-equals keeps the live (mocked) CJS module object (unlike -// `import * as`, which esModuleInterop turns into a copy), so patching -// os.networkInterfaces below is observed by the code under test -import os = require('node:os'); +// the module mock binds networkInterfaces once at registration, so tests +// swap implementations through the mock's mutable holder — patching the +// CommonJS os object would not reach the ESM binding the source uses +import { networkInterfacesMock } from '../mocks/index.js'; import { UDPWorker } from '../../src/UDPWorker.js'; const OPTIONS: any = { @@ -189,9 +189,9 @@ describe('UDPWorker', () => { }); it('skips interface entries with no addresses', () => { - const original = os.networkInterfaces; + const original = networkInterfacesMock.impl; - (os as any).networkInterfaces = () => ({ + networkInterfacesMock.impl = () => ({ empty: undefined, lo: [ { @@ -211,7 +211,7 @@ describe('UDPWorker', () => { assert.equal(worker.selectNetworkInterface(), '127.0.0.1'); } finally { - (os as any).networkInterfaces = original; + networkInterfacesMock.impl = original; } }); From e02097e6c031c059dc1ca7854d3677cfd67da96e Mon Sep 17 00:00:00 2001 From: Serhiy Morenko Date: Thu, 9 Jul 2026 21:56:16 +0200 Subject: [PATCH 2/4] test: replace import-equals with a plain default import The default import of a builtin module is the same live, mutable CommonJS module object require() returns (unlike an import-namespace, which is frozen), so the util.inspect patching keeps working and the emitted spec no longer needs a createRequire shim. --- test/unit/helpers/copyEventEmitter.spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/unit/helpers/copyEventEmitter.spec.ts b/test/unit/helpers/copyEventEmitter.spec.ts index 3f702df..727144d 100644 --- a/test/unit/helpers/copyEventEmitter.spec.ts +++ b/test/unit/helpers/copyEventEmitter.spec.ts @@ -23,10 +23,11 @@ import '../../mocks/index.js'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { EventEmitter } from 'node:events'; -// import-equals keeps the live CJS module object (unlike `import * as`, -// which esModuleInterop turns into a copy), so patching util.inspect below -// is observed by the code under test -import util = require('node:util'); +// the default import of a builtin is the live, mutable CommonJS module +// object (unlike `import * as`, whose namespace is frozen), so patching +// util.inspect below works; syncBuiltinESMExports() then publishes the +// patch to the named-import binding the code under test reads +import util from 'node:util'; import { syncBuiltinESMExports } from 'node:module'; import { copyEventEmitter } from '../../../src/helpers/index.js'; From 90997a7b62e1cf100151e1aa807cafa2986ec98f Mon Sep 17 00:00:00 2001 From: Serhiy Morenko Date: Thu, 9 Jul 2026 21:59:40 +0200 Subject: [PATCH 3/4] chore: sync lockfile engines and trim spec comments - package-lock.json: record the engines requirement (node >=22.12.0) introduced by the ESM migration - copyEventEmitter spec: drop redundant explanatory comments --- package-lock.json | 3 +++ test/unit/helpers/copyEventEmitter.spec.ts | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c65eafc..7e0ced2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,9 @@ "oxfmt": "0.57.0", "oxlint": "1.72.0", "typescript": "^7.0.2" + }, + "engines": { + "node": ">=22.12.0" } }, "node_modules/@ioredis/commands": { diff --git a/test/unit/helpers/copyEventEmitter.spec.ts b/test/unit/helpers/copyEventEmitter.spec.ts index 727144d..094807e 100644 --- a/test/unit/helpers/copyEventEmitter.spec.ts +++ b/test/unit/helpers/copyEventEmitter.spec.ts @@ -23,10 +23,6 @@ import '../../mocks/index.js'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { EventEmitter } from 'node:events'; -// the default import of a builtin is the live, mutable CommonJS module -// object (unlike `import * as`, whose namespace is frozen), so patching -// util.inspect below works; syncBuiltinESMExports() then publishes the -// patch to the named-import binding the code under test reads import util from 'node:util'; import { syncBuiltinESMExports } from 'node:module'; import { copyEventEmitter } from '../../../src/helpers/index.js'; @@ -184,7 +180,6 @@ describe('copyEventEmitter()', () => { source.on(eventName, mockListener as any); copyEventEmitter(source, target); - // Restore original inspect (util as any).inspect = originalInspect; syncBuiltinESMExports(); @@ -195,7 +190,6 @@ describe('copyEventEmitter()', () => { const source = new EventEmitter(); const target = new EventEmitter(); - // Create a mock listener that looks like onceWrapper and has a truthy listener property let called = 0; const realListener = () => { called++; From 21bc3615d5da1cbc3caefeb6e8489c770cd4e3be Mon Sep 17 00:00:00 2001 From: Serhiy Morenko Date: Thu, 9 Jul 2026 22:04:07 +0200 Subject: [PATCH 4/4] 3.2.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e0ced2..acfc149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imqueue/core", - "version": "3.2.0", + "version": "3.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imqueue/core", - "version": "3.2.0", + "version": "3.2.1", "license": "GPL-3.0-only", "dependencies": { "ioredis": "^5.11.1" diff --git a/package.json b/package.json index bf887e9..8b0ea07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imqueue/core", - "version": "3.2.0", + "version": "3.2.1", "description": "Simple JSON-based messaging queue for inter service communication", "keywords": [ "message-queue",