diff --git a/.eslintrc.js b/.eslintrc.js index 0d1652d95..56275b1cb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,7 +6,6 @@ module.exports = { commonjs: true, es2021: true, node: true, - mocha: true, }, extends: [ 'airbnb-base', diff --git a/.github/workflows/mqttjs-test.yml b/.github/workflows/mqttjs-test.yml index bf3fe168d..917924be1 100644 --- a/.github/workflows/mqttjs-test.yml +++ b/.github/workflows/mqttjs-test.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x, 18.x, 20.x] + node-version: [18.x, 20.x] fail-fast: false steps: @@ -38,7 +38,8 @@ jobs: npm run lint - name: Test NodeJS - run: npm run test:node # npx mocha test/unique_message_id_provider_client.js --exit + run: npm run test:node + timeout-minutes: 5 env: CI: true DEBUG: "${{ runner.debug == '1' && 'mqttjs:*' || '' }}" diff --git a/.mocharc.js b/.mocharc.js deleted file mode 100644 index 3899769be..000000000 --- a/.mocharc.js +++ /dev/null @@ -1,10 +0,0 @@ -// --check-leaks -// --timeout 10000 -// --exit - -module.exports = { - checkLeaks: true, - exit: true, - timeout: 10000, -}; - diff --git a/package-lock.json b/package-lock.json index 8c8c8e64a..7078d0fbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,6 @@ "@release-it/conventional-changelog": "^7.0.0", "@types/chai": "^4.3.5", "@types/duplexify": "^3.6.1", - "@types/mocha": "^10.0.1", "@types/node": "^20.5.0", "@types/sinon": "^10.0.16", "@types/tape": "^5.6.0", @@ -49,6 +48,7 @@ "codecov": "^3.8.2", "conventional-changelog-cli": "^3.0.0", "end-of-stream": "^1.4.4", + "esbuild-register": "^3.5.0", "eslint": "^8.46.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", @@ -56,8 +56,8 @@ "eslint-plugin-import": "^2.28.0", "eslint-plugin-prettier": "^5.0.0", "global": "^4.4.0", + "leaked-handles": "^5.2.0", "mkdirp": "^3.0.1", - "mocha": "^10.2.0", "mqtt-connection": "^4.1.0", "mqtt-level-store": "^3.1.0", "nyc": "^15.1.0", @@ -74,7 +74,7 @@ "typescript": "^5.1.6" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -433,6 +433,380 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1360,12 +1734,6 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, - "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true - }, "node_modules/@types/node": { "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", @@ -2186,15 +2554,6 @@ "string-width": "^4.1.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2980,12 +3339,6 @@ "resolve": "^1.17.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "node_modules/browserify": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", @@ -3400,10 +3753,24 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001312", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", - "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", - "dev": true + "version": "1.0.30001538", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", + "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, "node_modules/catering": { "version": "2.1.1", @@ -4763,15 +5130,6 @@ "node": ">=0.8.0" } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -5275,6 +5633,56 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "node_modules/esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "node_modules/esbuild-register": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz", + "integrity": "sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6182,15 +6590,6 @@ "node": ">=10" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -6341,6 +6740,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -7012,15 +7425,6 @@ "node": ">=8" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/help-me": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", @@ -7961,15 +8365,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -8582,6 +8977,26 @@ "node": ">=0.10.0" } }, + "node_modules/leaked-handles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/leaked-handles/-/leaked-handles-5.2.0.tgz", + "integrity": "sha512-H96tZP5iXGw/t34Hhb8+ttcPXb/w1y5d3T4DTb81021UNI6LbtxylQBtiOuUwCiPbnKggykEmHc38hX+E41eag==", + "dev": true, + "dependencies": { + "process": "^0.10.0", + "weakmap-shim": "^1.1.0", + "xtend": "^4.0.0" + } + }, + "node_modules/leaked-handles/node_modules/process": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz", + "integrity": "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/level": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/level/-/level-5.0.1.tgz", @@ -9655,155 +10070,58 @@ "node_modules/minipass": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "dependencies": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-object/node_modules/for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "node_modules/mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/mixin-object/node_modules/for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "bin": { + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -10124,18 +10442,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/nanoresource": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/nanoresource/-/nanoresource-1.3.0.tgz", @@ -13069,15 +13375,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -15110,6 +15407,12 @@ "defaults": "^1.0.3" } }, + "node_modules/weakmap-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/weakmap-shim/-/weakmap-shim-1.1.1.tgz", + "integrity": "sha512-/wNyG+1FpiHhnfQo+TuA/XAUpvOOkKVl0A4qpT+oGcj5SlZCLmM+M1Py/3Sj8sy+YrEauCVITOxCsZKo6sPbQg==", + "dev": true + }, "node_modules/web-streams-polyfill": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", @@ -15370,12 +15673,6 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -15589,39 +15886,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", @@ -15933,6 +16197,182 @@ } } }, + "@esbuild/android-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", + "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/android-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", + "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/android-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", + "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/darwin-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", + "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/darwin-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", + "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", + "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/freebsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", + "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-arm": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", + "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", + "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", + "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-loong64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", + "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-mips64el": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", + "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-ppc64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", + "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-riscv64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", + "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-s390x": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", + "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", + "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/netbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", + "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/openbsd-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", + "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/sunos-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", + "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-arm64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", + "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-ia32": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", + "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-x64": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", + "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", + "dev": true, + "optional": true, + "peer": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -16662,12 +17102,6 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, - "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true - }, "@types/node": { "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", @@ -17322,12 +17756,6 @@ "string-width": "^4.1.0" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -17946,12 +18374,6 @@ "resolve": "^1.17.0" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "browserify": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", @@ -18316,9 +18738,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001312", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", - "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "version": "1.0.30001538", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", + "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", "dev": true }, "catering": { @@ -19381,12 +19803,6 @@ "minimist": "^1.1.1" } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -19817,6 +20233,46 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "esbuild": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", + "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", + "dev": true, + "peer": true, + "requires": { + "@esbuild/android-arm": "0.19.3", + "@esbuild/android-arm64": "0.19.3", + "@esbuild/android-x64": "0.19.3", + "@esbuild/darwin-arm64": "0.19.3", + "@esbuild/darwin-x64": "0.19.3", + "@esbuild/freebsd-arm64": "0.19.3", + "@esbuild/freebsd-x64": "0.19.3", + "@esbuild/linux-arm": "0.19.3", + "@esbuild/linux-arm64": "0.19.3", + "@esbuild/linux-ia32": "0.19.3", + "@esbuild/linux-loong64": "0.19.3", + "@esbuild/linux-mips64el": "0.19.3", + "@esbuild/linux-ppc64": "0.19.3", + "@esbuild/linux-riscv64": "0.19.3", + "@esbuild/linux-s390x": "0.19.3", + "@esbuild/linux-x64": "0.19.3", + "@esbuild/netbsd-x64": "0.19.3", + "@esbuild/openbsd-x64": "0.19.3", + "@esbuild/sunos-x64": "0.19.3", + "@esbuild/win32-arm64": "0.19.3", + "@esbuild/win32-ia32": "0.19.3", + "@esbuild/win32-x64": "0.19.3" + } + }, + "esbuild-register": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz", + "integrity": "sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -20493,12 +20949,6 @@ "path-exists": "^4.0.0" } }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -20615,6 +21065,13 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -21140,12 +21597,6 @@ "type-fest": "^0.8.0" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, "help-me": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", @@ -21868,12 +22319,6 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -22351,6 +22796,25 @@ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true }, + "leaked-handles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/leaked-handles/-/leaked-handles-5.2.0.tgz", + "integrity": "sha512-H96tZP5iXGw/t34Hhb8+ttcPXb/w1y5d3T4DTb81021UNI6LbtxylQBtiOuUwCiPbnKggykEmHc38hX+E41eag==", + "dev": true, + "requires": { + "process": "^0.10.0", + "weakmap-shim": "^1.1.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "process": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz", + "integrity": "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==", + "dev": true + } + } + }, "level": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/level/-/level-5.0.1.tgz", @@ -23243,82 +23707,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -23615,12 +24003,6 @@ "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, "nanoresource": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/nanoresource/-/nanoresource-1.3.0.tgz", @@ -25855,15 +26237,6 @@ } } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -27518,6 +27891,12 @@ "defaults": "^1.0.3" } }, + "weakmap-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/weakmap-shim/-/weakmap-shim-1.1.1.tgz", + "integrity": "sha512-/wNyG+1FpiHhnfQo+TuA/XAUpvOOkKVl0A4qpT+oGcj5SlZCLmM+M1Py/3Sj8sy+YrEauCVITOxCsZKo6sPbQg==", + "dev": true + }, "web-streams-polyfill": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", @@ -27713,12 +28092,6 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -27873,32 +28246,6 @@ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } - } - }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", diff --git a/package.json b/package.json index ec1090fb4..10d895b5c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "build:browser": "rimraf dist/ && mkdirp dist/ && browserify build/mqtt.js --standalone mqtt > dist/mqtt.js && terser dist/mqtt.js --compress --mangle --output dist/mqtt.min.js", "build": "npm run build:ts && npm run build:browser", "prepare": "npm run build", - "unit-test:node": "node_modules/.bin/nyc --reporter=lcov --reporter=text ./node_modules/mocha/bin/_mocha -r ts-node/register test/*.ts --exit", + "unit-test:node": "node_modules/.bin/nyc --reporter=lcov --reporter=text node -r esbuild-register test/runTests.ts", "unit-test:browser": "airtap --server test/browser/server.js test/browser/test.js", "test:node": "npm run unit-test:node && codecov", "test:browser": "npm run build && npm run unit-test:browser", @@ -92,7 +92,7 @@ "lint" ], "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "browser": { "./mqtt.js": "./build/mqtt.js", @@ -122,7 +122,6 @@ "@release-it/conventional-changelog": "^7.0.0", "@types/chai": "^4.3.5", "@types/duplexify": "^3.6.1", - "@types/mocha": "^10.0.1", "@types/node": "^20.5.0", "@types/sinon": "^10.0.16", "@types/tape": "^5.6.0", @@ -136,6 +135,7 @@ "codecov": "^3.8.2", "conventional-changelog-cli": "^3.0.0", "end-of-stream": "^1.4.4", + "esbuild-register": "^3.5.0", "eslint": "^8.46.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", @@ -143,8 +143,8 @@ "eslint-plugin-import": "^2.28.0", "eslint-plugin-prettier": "^5.0.0", "global": "^4.4.0", + "leaked-handles": "^5.2.0", "mkdirp": "^3.0.1", - "mocha": "^10.2.0", "mqtt-connection": "^4.1.0", "mqtt-level-store": "^3.1.0", "nyc": "^15.1.0", diff --git a/test/abstract_client.ts b/test/abstract_client.ts index 5d0325a90..7d876b79b 100644 --- a/test/abstract_client.ts +++ b/test/abstract_client.ts @@ -7,7 +7,6 @@ import fs from 'fs' import levelStore from 'mqtt-level-store' import * as mqtt from '../src/mqtt' import Store from '../src/lib/store' -import ports from './helpers/port_list' import serverBuilder from './server_helpers_for_client_tests' import handlePubrel from '../src/lib/handlers/pubrel' import handle from '../src/lib/handlers/index' @@ -22,6 +21,7 @@ import { import { IPublishPacket, IPubrelPacket, ISubackPacket, QoS } from 'mqtt-packet' import { DoneCallback, ErrorWithReasonCode } from 'src/lib/shared' import { fail } from 'assert' +import { describe, it, beforeEach, afterEach } from 'node:test' /** * These tests try to be consistent with names for servers (brokers) and clients, @@ -44,7 +44,7 @@ import { fail } from 'assert' * */ -export default function abstractTest(server, config) { +export default function abstractTest(server, config, ports) { const version = config.protocolVersion || 4 function connect(opts?: IClientOptions | string) { @@ -56,7 +56,7 @@ export default function abstractTest(server, config) { } describe('closing', () => { - it('should emit close if stream closes', function _test(done) { + it('should emit close if stream closes', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -67,7 +67,7 @@ export default function abstractTest(server, config) { }) }) - it('should mark the client as disconnected', function _test(done) { + it('should mark the client as disconnected', function _test(t, done) { const client = connect() client.once('close', () => { @@ -85,7 +85,7 @@ export default function abstractTest(server, config) { }) }) - it('should stop ping timer if stream closes', function _test(done) { + it('should stop ping timer if stream closes', function _test(t, done) { const client = connect() client.once('close', () => { @@ -99,7 +99,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit close after end called', function _test(done) { + it('should emit close after end called', function _test(t, done) { const client = connect() client.once('close', () => { @@ -111,7 +111,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit end after end called and client must be disconnected', function _test(done) { + it('should emit end after end called and client must be disconnected', function _test(t, done) { const client = connect() client.once('end', () => { @@ -126,7 +126,7 @@ export default function abstractTest(server, config) { }) }) - it('should pass store close error to end callback but not to end listeners (incomingStore)', function _test(done) { + it('should pass store close error to end callback but not to end listeners (incomingStore)', function _test(t, done) { const store = new Store() const client = connect({ incomingStore: store }) @@ -150,7 +150,7 @@ export default function abstractTest(server, config) { }) }) - it('should pass store close error to end callback but not to end listeners (outgoingStore)', function _test(done) { + it('should pass store close error to end callback but not to end listeners (outgoingStore)', function _test(t, done) { const store = new Store() const client = connect({ outgoingStore: store }) @@ -174,7 +174,7 @@ export default function abstractTest(server, config) { }) }) - it('should return `this` if end called twice', function _test(done) { + it('should return `this` if end called twice', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -188,7 +188,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit end only on first client end', function _test(done) { + it('should emit end only on first client end', function _test(t, done) { const client = connect() client.once('end', () => { @@ -205,7 +205,7 @@ export default function abstractTest(server, config) { }) }) - it('should stop ping timer after end called', function _test(done) { + it('should stop ping timer after end called', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -217,7 +217,7 @@ export default function abstractTest(server, config) { }) }) - it('should be able to end even on a failed connection', function _test(done) { + it('should be able to end even on a failed connection', function _test(t, done) { const client = connect({ host: 'this_hostname_should_not_exist' }) const timeout = setTimeout(() => { @@ -232,7 +232,7 @@ export default function abstractTest(server, config) { }, 200) }) - it('should emit end even on a failed connection', function _test(done) { + it('should emit end even on a failed connection', function _test(t, done) { const client = connect({ host: 'this_hostname_should_not_exist' }) const timeout = setTimeout(() => { @@ -250,7 +250,7 @@ export default function abstractTest(server, config) { }, 200) }) - it.skip('should emit end only once for a reconnecting client', function _test(done) { + it.skip('should emit end only once for a reconnecting client', function _test(t, done) { // I want to fix this test, but it will take signficant work, so I am marking it as a skipping test right now. // Reason for it is that there are overlaps in the reconnectTimer and connectTimer. In the PR for this code // there will be gists showing the difference between a successful test here and a failed test. For now we @@ -278,7 +278,7 @@ export default function abstractTest(server, config) { }) describe('connecting', () => { - it('should connect to the broker', function _test(done) { + it('should connect to the broker', function _test(t, done) { const client = connect() client.on('error', done) @@ -287,7 +287,7 @@ export default function abstractTest(server, config) { }) }) - it('should send a default client id', function _test(done) { + it('should send a default client id', function _test(t, done) { const client = connect() client.on('error', done) @@ -299,7 +299,7 @@ export default function abstractTest(server, config) { }) }) - it('should send be clean by default', function _test(done) { + it('should send be clean by default', function _test(t, done) { const client = connect() client.on('error', done) @@ -311,7 +311,7 @@ export default function abstractTest(server, config) { }) }) - it('should connect with the given client id', function _test(done) { + it('should connect with the given client id', function _test(t, done) { const client = connect({ clientId: 'testclient' }) client.on('error', (err) => { throw err @@ -325,7 +325,7 @@ export default function abstractTest(server, config) { }) }) - it('should connect with the client id and unclean state', function _test(done) { + it('should connect with the client id and unclean state', function _test(t, done) { const client = connect({ clientId: 'testclient', clean: false }) client.on('error', (err) => { throw err @@ -340,7 +340,7 @@ export default function abstractTest(server, config) { }) }) - it('should require a clientId with clean=false', function _test(done) { + it('should require a clientId with clean=false', function _test(t, done) { try { const client = connect({ clean: false }) client.on('error', (err) => { @@ -355,7 +355,7 @@ export default function abstractTest(server, config) { } }) - it('should default to localhost', function _test(done) { + it('should default to localhost', function _test(t, done) { const client = connect({ clientId: 'testclient' }) client.on('error', (err) => { throw err @@ -369,7 +369,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit connect', function _test(done) { + it('should emit connect', function _test(t, done) { const client = connect() client.once('connect', (packet: mqtt.IConnackPacket) => { assert.equal(packet.cmd, 'connack') @@ -378,7 +378,7 @@ export default function abstractTest(server, config) { client.once('error', done) }) - it('should provide connack packet with connect event', function _test(done) { + it('should provide connack packet with connect event', function _test(t, done) { const connack = version === 5 ? { reasonCode: 0, sessionPresent: undefined } @@ -402,7 +402,7 @@ export default function abstractTest(server, config) { }) }) - it('should mark the client as connected', function _test(done) { + it('should mark the client as connected', function _test(t, done) { const client = connect() client.once('connect', () => { assert.isTrue(client.connected) @@ -410,7 +410,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit error on invalid clientId', function _test(done) { + it('should emit error on invalid clientId', function _test(t, done) { const client = connect({ clientId: 'invalid' }) client.once('connect', () => { done(new Error('Should not emit connect')) @@ -422,7 +422,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit error event if the socket refuses the connection', function _test(done) { + it('should emit error event if the socket refuses the connection', function _test(t, done) { // fake a port const client = connect({ port: 4557 }) @@ -432,7 +432,7 @@ export default function abstractTest(server, config) { }) }) - it('should have different client ids', function _test(done) { + it('should have different client ids', function _test(t, done) { // bug identified in this test: the client.end callback is invoked twice, once when the `end` // method completes closing the stores and invokes the callback, and another time when the // stream is closed. When the stream is closed, for some reason the closeStores method is called @@ -453,7 +453,7 @@ export default function abstractTest(server, config) { }) describe('handling offline states', () => { - it('should emit offline event once when the client transitions from connected states to disconnected ones', function _test(done) { + it('should emit offline event once when the client transitions from connected states to disconnected ones', function _test(t, done) { const client = connect({ reconnectPeriod: 20 }) client.on('connect', () => { @@ -465,7 +465,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit offline event once when the client (at first) can NOT connect to servers', function _test(done) { + it('should emit offline event once when the client (at first) can NOT connect to servers', function _test(t, done) { // fake a port const client = connect({ reconnectPeriod: 20, port: 4557 }) @@ -478,7 +478,7 @@ export default function abstractTest(server, config) { }) describe('topic validations when subscribing', () => { - it('should be ok for well-formated topics', function _test(done) { + it('should be ok for well-formated topics', function _test(t, done) { const client = connect() client.subscribe( [ @@ -505,7 +505,7 @@ export default function abstractTest(server, config) { ) }) - it('should return an error (via callbacks) for topic #/event', function _test(done) { + it('should return an error (via callbacks) for topic #/event', function _test(t, done) { const client = connect() client.subscribe(['#/event', 'event#', 'event+'], (err) => { client.end(false, () => { @@ -517,7 +517,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an empty array for duplicate subs', function _test(done) { + it('should return an empty array for duplicate subs', function _test(t, done) { const client = connect() client.subscribe('event', (err, granted1) => { if (err) { @@ -534,7 +534,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an error (via callbacks) for topic #/event', function _test(done) { + it('should return an error (via callbacks) for topic #/event', function _test(t, done) { const client = connect() client.subscribe('#/event', (err) => { client.end(() => { @@ -546,7 +546,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an error (via callbacks) for topic event#', function _test(done) { + it('should return an error (via callbacks) for topic event#', function _test(t, done) { const client = connect() client.subscribe('event#', (err) => { client.end(() => { @@ -558,7 +558,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an error (via callbacks) for topic system/#/event', function _test(done) { + it('should return an error (via callbacks) for topic system/#/event', function _test(t, done) { const client = connect() client.subscribe('system/#/event', (err) => { client.end(() => { @@ -570,7 +570,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an error (via callbacks) for empty topic list', function _test(done) { + it('should return an error (via callbacks) for empty topic list', function _test(t, done) { const client = connect() client.subscribe([], (subErr) => { client.end((endErr) => { @@ -582,7 +582,7 @@ export default function abstractTest(server, config) { }) }) - it('should return an error (via callbacks) for topic system/+/#/event', function _test(done) { + it('should return an error (via callbacks) for topic system/+/#/event', function _test(t, done) { const client = connect() client.subscribe('system/+/#/event', (subErr) => { client.end(true, (endErr) => { @@ -596,7 +596,7 @@ export default function abstractTest(server, config) { }) describe('offline messages', () => { - it('should queue message until connected', function _test(done) { + it('should queue message until connected', function _test(t, done) { const client = connect() client.publish('test', 'test') @@ -612,7 +612,7 @@ export default function abstractTest(server, config) { }) }) - it('should not queue qos 0 messages if queueQoSZero is false', function _test(done) { + it('should not queue qos 0 messages if queueQoSZero is false', function _test(t, done) { const client = connect({ queueQoSZero: false }) client.publish('test', 'test', { qos: 0 }) @@ -624,7 +624,7 @@ export default function abstractTest(server, config) { }) }) - it('should queue qos != 0 messages', function _test(done) { + it('should queue qos != 0 messages', function _test(t, done) { const client = connect({ queueQoSZero: false }) client.publish('test', 'test', { qos: 1 }) @@ -639,7 +639,7 @@ export default function abstractTest(server, config) { }) }) - it('should not interrupt messages', function _test(done) { + it('should not interrupt messages', function _test(t, done) { let client = null let publishCount = 0 const incomingStore = new mqtt.Store({ clean: false }) @@ -709,7 +709,7 @@ export default function abstractTest(server, config) { }) }) - it('should not overtake the messages stored in the level-db-store', function _test(done) { + it('should not overtake the messages stored in the level-db-store', function _test(t, done) { const storePath = fs.mkdtempSync('test-store_') const store = levelStore(storePath) let client = null @@ -793,7 +793,7 @@ export default function abstractTest(server, config) { }) }) - it('should call cb if an outgoing QoS 0 message is not sent', function _test(done) { + it('should call cb if an outgoing QoS 0 message is not sent', function _test(t, done) { const client = connect({ queueQoSZero: false }) let called = false @@ -809,7 +809,7 @@ export default function abstractTest(server, config) { }) }) - it('should delay ending up until all inflight messages are delivered', function _test(done) { + it('should delay ending up until all inflight messages are delivered', function _test(t, done) { const client = connect() let subscribeCalled = false @@ -826,7 +826,7 @@ export default function abstractTest(server, config) { }) }) - it('wait QoS 1 publish messages', function _test(done) { + it('wait QoS 1 publish messages', function _test(t, done) { const client = connect() let messageReceived = false @@ -852,14 +852,14 @@ export default function abstractTest(server, config) { }) }) - it('does not wait acks when force-closing', function _test(done) { + it('does not wait acks when force-closing', function _test(t, done) { // non-running broker const client = connect('mqtt://localhost:8993') client.publish('test', 'test', { qos: 1 }) client.end(true, done) }) - it('should call cb if store.put fails', function _test(done) { + it('should call cb if store.put fails', function _test(t, done) { const store = new Store() store.put = (packet, cb) => { process.nextTick(cb, new Error('oops there is an error')) @@ -878,7 +878,7 @@ export default function abstractTest(server, config) { }) describe('publishing', () => { - it('should publish a message (offline)', function _test(done) { + it('should publish a message (offline)', function _test(t, done) { const client = connect() const payload = 'test' const topic = 'test' @@ -902,7 +902,7 @@ export default function abstractTest(server, config) { } }) - it('should publish a message (online)', function _test(done) { + it('should publish a message (online)', function _test(t, done) { const client = connect() const payload = 'test' const topic = 'test' @@ -928,7 +928,7 @@ export default function abstractTest(server, config) { } }) - it('should publish a message (retain, offline)', function _test(done) { + it('should publish a message (retain, offline)', function _test(t, done) { const client = connect({ queueQoSZero: true }) const payload = 'test' const topic = 'test' @@ -950,7 +950,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetsend event', function _test(done) { + it('should emit a packetsend event', function _test(t, done) { const client = connect() const payload = 'test_payload' const topic = 'testTopic' @@ -970,7 +970,7 @@ export default function abstractTest(server, config) { client.publish(topic, payload) }) - it('should accept options', function _test(done) { + it('should accept options', function _test(t, done) { const client = connect() const payload = 'test' const topic = 'test' @@ -1005,7 +1005,7 @@ export default function abstractTest(server, config) { }) }) - it('should publish with the default options for an empty parameter', function _test(done) { + it('should publish with the default options for an empty parameter', function _test(t, done) { const client = connect() const payload = 'test' const topic = 'test' @@ -1039,7 +1039,7 @@ export default function abstractTest(server, config) { }) }) - it('should mark a message as duplicate when "dup" option is set', function _test(done) { + it('should mark a message as duplicate when "dup" option is set', function _test(t, done) { const client = connect() const payload = 'duplicated-test' const topic = 'test' @@ -1075,7 +1075,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback (qos 0)', function _test(done) { + it('should fire a callback (qos 0)', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -1087,7 +1087,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback (qos 1)', function _test(done) { + it('should fire a callback (qos 1)', function _test(t, done) { const client = connect() const opts: IClientPublishOptions = { qos: 1 } @@ -1099,7 +1099,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback (qos 1) on error', function _test(done) { + it('should fire a callback (qos 1) on error', function _test(t, done) { // 145 = Packet Identifier in use const pubackReasonCode = 145 const pubOpts = { qos: 1 } @@ -1157,7 +1157,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback (qos 2)', function _test(done) { + it('should fire a callback (qos 2)', function _test(t, done) { const client = connect() const opts: IClientPublishOptions = { qos: 2 } @@ -1169,7 +1169,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback (qos 2) on error', function _test(done) { + it('should fire a callback (qos 2) on error', function _test(t, done) { // 145 = Packet Identifier in use const pubrecReasonCode = 145 const pubOpts = { qos: 2 } @@ -1231,7 +1231,7 @@ export default function abstractTest(server, config) { }) }) - it('should support UTF-8 characters in topic', function _test(done) { + it('should support UTF-8 characters in topic', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -1241,7 +1241,7 @@ export default function abstractTest(server, config) { }) }) - it('should support UTF-8 characters in payload', function _test(done) { + it('should support UTF-8 characters in payload', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -1251,7 +1251,7 @@ export default function abstractTest(server, config) { }) }) - it('should publish 10 QoS 2 and receive them', function _test(done) { + it('should publish 10 QoS 2 and receive them', function _test(t, done) { const client = connect() let countSent = 0 let countReceived = 0 @@ -1343,12 +1343,12 @@ export default function abstractTest(server, config) { const qosTests = [0, 1, 2] qosTests.forEach((qos) => { - it(`should publish 10 QoS ${qos}and receive them only when \`handleMessage\` finishes`, function _test(done) { + it(`should publish 10 QoS ${qos}and receive them only when \`handleMessage\` finishes`, function _test(t, done) { testQosHandleMessage(qos, done) }) }) - it('should not send a `puback` if the execution of `handleMessage` fails for messages with QoS `1`', function _test(done) { + it('should not send a `puback` if the execution of `handleMessage` fails for messages with QoS `1`', function _test(t, done) { const client = connect() client.handleMessage = (packet, callback) => { @@ -1385,7 +1385,7 @@ export default function abstractTest(server, config) { it( 'should silently ignore errors thrown by `handleMessage` and return when no callback is passed ' + 'into `handlePublish` method', - function _test(done) { + function _test(t, done) { const client = connect() client.handleMessage = (packet, callback) => { @@ -1411,7 +1411,7 @@ export default function abstractTest(server, config) { }, ) - it('should handle error with async incoming store in QoS 1 `handlePublish` method', function _test(done) { + it('should handle error with async incoming store in QoS 1 `handlePublish` method', function _test(t, done) { class AsyncStore extends Store { put(packet, cb) { process.nextTick(() => { @@ -1446,7 +1446,7 @@ export default function abstractTest(server, config) { ) }) - it('should handle error with async incoming store in QoS 2 `handlePublish` method', function _test(done) { + it('should handle error with async incoming store in QoS 2 `handlePublish` method', function _test(t, done) { class AsyncStore extends Store { put(packet, cb) { process.nextTick(() => { @@ -1496,7 +1496,7 @@ export default function abstractTest(server, config) { ) }) - it('should handle error with async incoming store in QoS 2 `handlePubrel` method', function _test(done) { + it('should handle error with async incoming store in QoS 2 `handlePubrel` method', function _test(t, done) { class AsyncStore extends Store { put(packet, cb) { process.nextTick(() => { @@ -1543,7 +1543,7 @@ export default function abstractTest(server, config) { ) }) - it('should handle success with async incoming store in QoS 2 `handlePubrel` method', function _test(done) { + it('should handle success with async incoming store in QoS 2 `handlePubrel` method', function _test(t, done) { let delComplete = false class AsyncStore extends Store { put(packet, cb) { @@ -1592,7 +1592,7 @@ export default function abstractTest(server, config) { ) }) - it('should not send a `pubcomp` if the execution of `handleMessage` fails for messages with QoS `2`', function _test(done) { + it('should not send a `pubcomp` if the execution of `handleMessage` fails for messages with QoS `2`', function _test(t, done) { const store = new Store() const client = connect({ incomingStore: store }) @@ -1639,7 +1639,7 @@ export default function abstractTest(server, config) { it( 'should silently ignore errors thrown by `handleMessage` and return when no callback is passed ' + 'into `handlePubrel` method', - function _test(done) { + function _test(t, done) { const store = new Store() const client = connect({ incomingStore: store }) @@ -1683,7 +1683,7 @@ export default function abstractTest(server, config) { }, ) - it('should keep message order', function _test(done) { + it('should keep message order', function _test(t, done) { let publishCount = 0 let reconnect = false let client: mqtt.MqttClient @@ -1806,7 +1806,7 @@ export default function abstractTest(server, config) { callbackStorePutByQoSParameters.forEach((test) => { if (test.args[0] === 0) { // QoS 0 - it(`should not call cbStorePut when publishing message with QoS \`${test.args[0]}\` and clean \`${test.args[1]}\``, function _test(done) { + it(`should not call cbStorePut when publishing message with QoS \`${test.args[0]}\` and clean \`${test.args[1]}\``, function _test(t, done) { testCallbackStorePutByQoS( test.args[0] as number, test.args[1] as boolean, @@ -1816,7 +1816,7 @@ export default function abstractTest(server, config) { }) } else { // QoS 1 and 2 - it(`should call cbStorePut before publish completes when publishing message with QoS \`${test.args[0]}\` and clean \`${test.args[1]}\``, function _test(done) { + it(`should call cbStorePut before publish completes when publishing message with QoS \`${test.args[0]}\` and clean \`${test.args[1]}\``, function _test(t, done) { testCallbackStorePutByQoS( test.args[0] as number, test.args[1] as boolean, @@ -1829,7 +1829,7 @@ export default function abstractTest(server, config) { }) describe('unsubscribing', () => { - it('should send an unsubscribe packet (offline)', function _test(done) { + it('should send an unsubscribe packet (offline)', function _test(t, done) { const client = connect() let received = false @@ -1847,7 +1847,7 @@ export default function abstractTest(server, config) { }) }) - it('should send an unsubscribe packet', function _test(done) { + it('should send an unsubscribe packet', function _test(t, done) { const client = connect() const topic = 'topic' let received = false @@ -1868,7 +1868,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetsend event', function _test(done) { + it('should emit a packetsend event', function _test(t, done) { const client = connect() const testTopic = 'testTopic' @@ -1883,7 +1883,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetreceive event', function _test(done) { + it('should emit a packetreceive event', function _test(t, done) { const client = connect() const testTopic = 'testTopic' @@ -1898,7 +1898,7 @@ export default function abstractTest(server, config) { }) }) - it('should accept an array of unsubs', function _test(done) { + it('should accept an array of unsubs', function _test(t, done) { const client = connect() const topics = ['topic1', 'topic2'] let received = false @@ -1919,7 +1919,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback on unsuback', function _test(done) { + it('should fire a callback on unsuback', function _test(t, done) { const client = connect() const topic = 'topic' @@ -1938,7 +1938,7 @@ export default function abstractTest(server, config) { }) }) - it('should unsubscribe from a chinese topic', function _test(done) { + it('should unsubscribe from a chinese topic', function _test(t, done) { const client = connect() const topic = '中国' @@ -1959,7 +1959,7 @@ export default function abstractTest(server, config) { }) describe('keepalive', () => { - let clock + let clock: sinon.SinonFakeTimers // eslint-disable-next-line beforeEach(() => { @@ -1970,7 +1970,7 @@ export default function abstractTest(server, config) { clock.restore() }) - it('should checkPing at keepalive interval', function _test(done) { + it('should checkPing at keepalive interval', function _test(t, done) { const interval = 3 const client = connect({ keepalive: interval }) @@ -1991,7 +1991,7 @@ export default function abstractTest(server, config) { }) }) - it('should not checkPing if publishing at a higher rate than keepalive', function _test(done) { + it('should not checkPing if publishing at a higher rate than keepalive', function _test(t, done) { const intervalMs = 3000 const client = connect({ keepalive: intervalMs / 1000 }) @@ -2009,7 +2009,7 @@ export default function abstractTest(server, config) { }) }) - it('should checkPing if publishing at a higher rate than keepalive and reschedulePings===false', function _test(done) { + it('should checkPing if publishing at a higher rate than keepalive and reschedulePings===false', function _test(t, done) { const intervalMs = 3000 const client = connect({ keepalive: intervalMs / 1000, @@ -2032,7 +2032,7 @@ export default function abstractTest(server, config) { }) describe('pinging', () => { - it('should set a ping timer', function _test(done) { + it('should set a ping timer', function _test(t, done) { const client = connect({ keepalive: 3 }) client.once('connect', () => { assert.exists(client.pingTimer) @@ -2040,7 +2040,7 @@ export default function abstractTest(server, config) { }) }) - it('should not set a ping timer keepalive=0', function _test(done) { + it('should not set a ping timer keepalive=0', function _test(t, done) { const client = connect({ keepalive: 0 }) client.on('connect', () => { assert.notExists(client.pingTimer) @@ -2048,27 +2048,32 @@ export default function abstractTest(server, config) { }) }) - it('should reconnect if pingresp is not sent', function _test(done) { - this.timeout(4000) - const client = connect({ keepalive: 1, reconnectPeriod: 100 }) + it( + 'should reconnect if pingresp is not sent', + { + timeout: 4000, + }, + function _test(t, done) { + const client = connect({ keepalive: 1, reconnectPeriod: 100 }) - // Fake no pingresp being send by stubbing the _handlePingresp function - client.on('packetreceive', (packet) => { - if (packet.cmd === 'pingresp') { - setImmediate(() => { - client.pingResp = false - }) - } - }) + // Fake no pingresp being send by stubbing the _handlePingresp function + client.on('packetreceive', (packet) => { + if (packet.cmd === 'pingresp') { + setImmediate(() => { + client.pingResp = false + }) + } + }) - client.once('connect', () => { client.once('connect', () => { - client.end(true, done) + client.once('connect', () => { + client.end(true, done) + }) }) - }) - }) + }, + ) - it('should not reconnect if pingresp is successful', function _test(done) { + it('should not reconnect if pingresp is successful', function _test(t, done) { const client = connect({ keepalive: 100 }) client.once('close', () => { done(new Error('Client closed connection')) @@ -2079,7 +2084,7 @@ export default function abstractTest(server, config) { }, 1000) }) - it('should defer the next ping when sending a control packet', function _test(done) { + it('should defer the next ping when sending a control packet', function _test(t, done) { const client = connect({ keepalive: 1 }) client.once('connect', () => { @@ -2106,7 +2111,7 @@ export default function abstractTest(server, config) { }) describe('subscribing', () => { - it('should send a subscribe message (offline)', function _test(done) { + it('should send a subscribe message (offline)', function _test(t, done) { const client = connect() client.subscribe('test') @@ -2118,7 +2123,7 @@ export default function abstractTest(server, config) { }) }) - it('should send a subscribe message', function _test(done) { + it('should send a subscribe message', function _test(t, done) { const client = connect() const topic = 'test' @@ -2143,7 +2148,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetsend event', function _test(done) { + it('should emit a packetsend event', function _test(t, done) { const client = connect() const testTopic = 'testTopic' @@ -2158,7 +2163,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetreceive event', function _test(done) { + it('should emit a packetreceive event', function _test(t, done) { const client = connect() const testTopic = 'testTopic' @@ -2173,7 +2178,7 @@ export default function abstractTest(server, config) { }) }) - it('should accept an array of subscriptions', function _test(done) { + it('should accept an array of subscriptions', function _test(t, done) { const client = connect() const subs = ['test1', 'test2'] @@ -2203,7 +2208,7 @@ export default function abstractTest(server, config) { }) }) - it('should accept a hash of subscriptions', function _test(done) { + it('should accept a hash of subscriptions', function _test(t, done) { const client = connect() const topics: ISubscriptionMap = { test1: { qos: 0 }, @@ -2239,7 +2244,7 @@ export default function abstractTest(server, config) { }) }) - it('should accept an options parameter', function _test(done) { + it('should accept an options parameter', function _test(t, done) { const client = connect() const topic = 'test' const opts: IClientSubscribeOptions = { qos: 1 } @@ -2269,7 +2274,7 @@ export default function abstractTest(server, config) { }) }) - it('should subscribe with the default options for an empty options parameter', function _test(done) { + it('should subscribe with the default options for an empty options parameter', function _test(t, done) { const client = connect() const topic = 'test' const defaultOpts = { qos: 0 } @@ -2296,7 +2301,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback on suback', function _test(done) { + it('should fire a callback on suback', function _test(t, done) { const client = connect() const topic = 'test' @@ -2323,7 +2328,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback with error if disconnected (options provided)', function _test(done) { + it('should fire a callback with error if disconnected (options provided)', function _test(t, done) { const client = connect() const topic = 'test' client.once('connect', () => { @@ -2337,7 +2342,7 @@ export default function abstractTest(server, config) { }) }) - it('should fire a callback with error if disconnected (options not provided)', function _test(done) { + it('should fire a callback with error if disconnected (options not provided)', function _test(t, done) { const client = connect() const topic = 'test' @@ -2352,7 +2357,7 @@ export default function abstractTest(server, config) { }) }) - it('should subscribe with a chinese topic', function _test(done) { + it('should subscribe with a chinese topic', function _test(t, done) { const client = connect() const topic = '中国' @@ -2379,7 +2384,7 @@ export default function abstractTest(server, config) { }) describe('receiving messages', () => { - it('should fire the message event', function _test(done) { + it('should fire the message event', function _test(t, done) { const client = connect() const testPacket = { topic: 'test', @@ -2408,7 +2413,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a packetreceive event', function _test(done) { + it('should emit a packetreceive event', function _test(t, done) { const client = connect() const testPacket = { topic: 'test', @@ -2439,7 +2444,7 @@ export default function abstractTest(server, config) { }) }) - it('should support binary data', function _test(done) { + it('should support binary data', function _test(t, done) { const client = connect({ encoding: 'binary' }) const testPacket = { topic: 'test', @@ -2465,7 +2470,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a message event (qos=2)', function _test(done) { + it('should emit a message event (qos=2)', function _test(t, done) { const client = connect() const testPacket = { topic: 'test', @@ -2493,7 +2498,7 @@ export default function abstractTest(server, config) { }) }) - it('should emit a message event (qos=2) - repeated publish', function _test(done) { + it('should emit a message event (qos=2) - repeated publish', function _test(t, done) { const client = connect() const testPacket = { topic: 'test', @@ -2529,7 +2534,7 @@ export default function abstractTest(server, config) { }) }) - it('should support a chinese topic', function _test(done) { + it('should support a chinese topic', function _test(t, done) { const client = connect({ encoding: 'binary' }) const testPacket = { topic: '国', @@ -2558,7 +2563,7 @@ export default function abstractTest(server, config) { }) describe('qos handling', () => { - it('should follow qos 0 semantics (trivial)', function _test(done) { + it('should follow qos 0 semantics (trivial)', function _test(t, done) { const client = connect() const testTopic = 'test' const testMessage = 'message' @@ -2581,7 +2586,7 @@ export default function abstractTest(server, config) { }) }) - it('should follow qos 1 semantics', function _test(done) { + it('should follow qos 1 semantics', function _test(t, done) { const client = connect() const testTopic = 'test' const testMessage = 'message' @@ -2608,7 +2613,7 @@ export default function abstractTest(server, config) { }) }) - it('should follow qos 2 semantics', function _test(done) { + it('should follow qos 2 semantics', function _test(t, done) { const client = connect() const testTopic = 'test' const testMessage = 'message' @@ -2705,7 +2710,7 @@ export default function abstractTest(server, config) { }) }) - it('should should empty the incoming store after a qos 2 handshake is completed', function _test(done) { + it('should should empty the incoming store after a qos 2 handshake is completed', function _test(t, done) { const client = connect() const testTopic = 'test' const testMessage = 'message' @@ -2842,17 +2847,17 @@ export default function abstractTest(server, config) { }) } - it('handle qos 2 messages exactly once when multiple pubrel received', function _test(done) { + it('handle qos 2 messages exactly once when multiple pubrel received', function _test(t, done) { testMultiplePubrel(false, done) }) - it('handle qos 2 messages exactly once when multiple pubrel received and sending pubcomp fails on client', function _test(done) { + it('handle qos 2 messages exactly once when multiple pubrel received and sending pubcomp fails on client', function _test(t, done) { testMultiplePubrel(true, done) }) }) describe('auto reconnect', () => { - it('should mark the client disconnecting if #end called', function _test(done) { + it('should mark the client disconnecting if #end called', function _test(t, done) { const client = connect() client.end(true, (err) => { @@ -2861,7 +2866,7 @@ export default function abstractTest(server, config) { }) }) - it('should reconnect after stream disconnect', function _test(done) { + it('should reconnect after stream disconnect', function _test(t, done) { const client = connect() let tryReconnect = true @@ -2876,7 +2881,7 @@ export default function abstractTest(server, config) { }) }) - it("should emit 'reconnect' when reconnecting", function _test(done) { + it("should emit 'reconnect' when reconnecting", function _test(t, done) { const client = connect() let tryReconnect = true let reconnectEvent = false @@ -2896,7 +2901,7 @@ export default function abstractTest(server, config) { }) }) - it("should emit 'offline' after going offline", function _test(done) { + it("should emit 'offline' after going offline", function _test(t, done) { const client = connect() let tryReconnect = true @@ -2917,7 +2922,7 @@ export default function abstractTest(server, config) { }) }) - it('should not reconnect if it was ended by the user', function _test(done) { + it('should not reconnect if it was ended by the user', function _test(t, done) { const client = connect() client.on('connect', () => { @@ -2926,7 +2931,7 @@ export default function abstractTest(server, config) { }) }) - it('should setup a reconnect timer on disconnect', function _test(done) { + it('should setup a reconnect timer on disconnect', function _test(t, done) { const client = connect() client.once('connect', () => { @@ -2946,44 +2951,49 @@ export default function abstractTest(server, config) { { period: 4000 }, ] reconnectPeriodTests.forEach((test) => { - it(`should allow specification of a reconnect period (${test.period}ms)`, function _test(done) { - this.timeout(10000) - let end - const reconnectSlushTime = 200 - const client = connect({ reconnectPeriod: test.period }) - let reconnect = false - const start = Date.now() - - client.on('connect', () => { - if (!reconnect) { - client.stream.end() - reconnect = true - } else { - end = Date.now() - client.end(() => { - const reconnectPeriodDuringTest = end - start - if ( - reconnectPeriodDuringTest >= - test.period - reconnectSlushTime && - reconnectPeriodDuringTest <= - test.period + reconnectSlushTime - ) { - // give the connection a 200 ms slush window - done() - } else { - done( - new Error( - `Strange reconnect period: ${reconnectPeriodDuringTest}`, - ), - ) - } - }) - } - }) - }) + it( + `should allow specification of a reconnect period (${test.period}ms)`, + { + timeout: 10000, + }, + function _test(t, done) { + let end + const reconnectSlushTime = 200 + const client = connect({ reconnectPeriod: test.period }) + let reconnect = false + const start = Date.now() + + client.on('connect', () => { + if (!reconnect) { + client.stream.end() + reconnect = true + } else { + end = Date.now() + client.end(() => { + const reconnectPeriodDuringTest = end - start + if ( + reconnectPeriodDuringTest >= + test.period - reconnectSlushTime && + reconnectPeriodDuringTest <= + test.period + reconnectSlushTime + ) { + // give the connection a 200 ms slush window + done() + } else { + done( + new Error( + `Strange reconnect period: ${reconnectPeriodDuringTest}`, + ), + ) + } + }) + } + }) + }, + ) }) - it('should always cleanup successfully on reconnection', function _test(done) { + it('should always cleanup successfully on reconnection', function _test(t, done) { const client = connect({ host: 'this_hostname_should_not_exist', connectTimeout: 0, @@ -2995,40 +3005,45 @@ export default function abstractTest(server, config) { }, 50) }) - it('should resend in-flight QoS 1 publish messages from the client', function _test(done) { - this.timeout(4000) - const client = connect({ reconnectPeriod: 200 }) - let serverPublished = false - let clientCalledBack = false + it( + 'should resend in-flight QoS 1 publish messages from the client', + { + timeout: 4000, + }, + function _test(t, done) { + const client = connect({ reconnectPeriod: 200 }) + let serverPublished = false + let clientCalledBack = false - server.once('client', (serverClient) => { - serverClient.on('connect', () => { - setImmediate(() => { - serverClient.stream.destroy() + server.once('client', (serverClient) => { + serverClient.on('connect', () => { + setImmediate(() => { + serverClient.stream.destroy() + }) }) - }) - server.once('client', (serverClientNew) => { - serverClientNew.on('publish', () => { - serverPublished = true - check() + server.once('client', (serverClientNew) => { + serverClientNew.on('publish', () => { + serverPublished = true + check() + }) }) }) - }) - client.publish('hello', 'world', { qos: 1 }, () => { - clientCalledBack = true - check() - }) + client.publish('hello', 'world', { qos: 1 }, () => { + clientCalledBack = true + check() + }) - function check() { - if (serverPublished && clientCalledBack) { - client.end(true, done) + function check() { + if (serverPublished && clientCalledBack) { + client.end(true, done) + } } - } - }) + }, + ) - it('should not resend in-flight publish messages if disconnecting', function _test(done) { + it('should not resend in-flight publish messages if disconnecting', function _test(t, done) { const client = connect({ reconnectPeriod: 200 }) let serverPublished = false let clientCalledBack = false @@ -3054,7 +3069,7 @@ export default function abstractTest(server, config) { }) }) - it('should resend in-flight QoS 2 publish messages from the client', function _test(done) { + it('should resend in-flight QoS 2 publish messages from the client', function _test(t, done) { const client = connect({ reconnectPeriod: 200 }) let serverPublished = false let clientCalledBack = false @@ -3088,7 +3103,7 @@ export default function abstractTest(server, config) { } }) - it('should not resend in-flight QoS 1 removed publish messages from the client', function _test(done) { + it('should not resend in-flight QoS 1 removed publish messages from the client', function _test(t, done) { const client = connect({ reconnectPeriod: 200 }) let clientCalledBack = false @@ -3127,7 +3142,7 @@ export default function abstractTest(server, config) { }) }) - it('should not resend in-flight QoS 2 removed publish messages from the client', function _test(done) { + it('should not resend in-flight QoS 2 removed publish messages from the client', function _test(t, done) { const client = connect({ reconnectPeriod: 200 }) let clientCalledBack = false @@ -3159,7 +3174,7 @@ export default function abstractTest(server, config) { client.end(true, done) }) - it('should resubscribe when reconnecting', function _test(done) { + it('should resubscribe when reconnecting', function _test(t, done) { const client = connect({ reconnectPeriod: 100 }) let tryReconnect = true let reconnectEvent = false @@ -3187,7 +3202,7 @@ export default function abstractTest(server, config) { }) }) - it('should resubscribe when clean=false and sessionPresent=false', function _test(done) { + it('should resubscribe when clean=false and sessionPresent=false', function _test(t, done) { const client = connect({ clientId: 'test', reconnectPeriod: 100, @@ -3220,7 +3235,7 @@ export default function abstractTest(server, config) { }) }) - it('should not resubscribe when reconnecting if resubscribe is disabled', function _test(done) { + it('should not resubscribe when reconnecting if resubscribe is disabled', function _test(t, done) { const client = connect({ reconnectPeriod: 100, resubscribe: false }) let tryReconnect = true let reconnectEvent = false @@ -3253,7 +3268,7 @@ export default function abstractTest(server, config) { }) }) - it('should not resubscribe when reconnecting if suback is error', function _test(done) { + it('should not resubscribe when reconnecting if suback is error', function _test(t, done) { let tryReconnect = true let reconnectEvent = false const server2 = serverBuilder(config.protocol, (serverClient) => { @@ -3310,7 +3325,7 @@ export default function abstractTest(server, config) { }) }) - it('should preserved incomingStore after disconnecting if clean is false', function _test(done) { + it('should preserved incomingStore after disconnecting if clean is false', function _test(t, done) { let reconnect = false let client: mqtt.MqttClient const incomingStore = new mqtt.Store({ clean: false }) @@ -3376,7 +3391,7 @@ export default function abstractTest(server, config) { }) }) - it('should clear outgoing if close from server', function _test(done) { + it('should clear outgoing if close from server', function _test(t, done) { let reconnect = false let client: mqtt.MqttClient const server2 = serverBuilder(config.protocol, (serverClient) => { @@ -3430,7 +3445,7 @@ export default function abstractTest(server, config) { }) }) - it('should resend in-flight QoS 1 publish messages from the client if clean is false', function _test(done) { + it('should resend in-flight QoS 1 publish messages from the client if clean is false', function _test(t, done) { let reconnect = false let client: mqtt.MqttClient const incomingStore = new mqtt.Store({ clean: false }) @@ -3478,7 +3493,7 @@ export default function abstractTest(server, config) { }) }) - it('should resend in-flight QoS 2 publish messages from the client if clean is false', function _test(done) { + it('should resend in-flight QoS 2 publish messages from the client if clean is false', function _test(t, done) { let reconnect = false let client: mqtt.MqttClient const incomingStore = new mqtt.Store({ clean: false }) @@ -3526,7 +3541,7 @@ export default function abstractTest(server, config) { }) }) - it('should resend in-flight QoS 2 pubrel messages from the client if clean is false', function _test(done) { + it('should resend in-flight QoS 2 pubrel messages from the client if clean is false', function _test(t, done) { let reconnect = false let client: mqtt.MqttClient const incomingStore = new mqtt.Store({ clean: false }) @@ -3588,7 +3603,7 @@ export default function abstractTest(server, config) { }) }) - it('should resend in-flight publish messages by published order', function _test(done) { + it('should resend in-flight publish messages by published order', function _test(t, done) { let publishCount = 0 let reconnect = false let disconnectOnce = true @@ -3668,7 +3683,7 @@ export default function abstractTest(server, config) { }) }) - it('should be able to pub/sub if reconnect() is called at close handler', function _test(done) { + it('should be able to pub/sub if reconnect() is called at close handler', function _test(t, done) { const client = connect({ reconnectPeriod: 0 }) let tryReconnect = true let reconnectEvent = false @@ -3698,7 +3713,7 @@ export default function abstractTest(server, config) { }) }) - it('should be able to pub/sub if reconnect() is called at out of close handler', function _test(done) { + it('should be able to pub/sub if reconnect() is called at out of close handler', function _test(t, done) { const client = connect({ reconnectPeriod: 0 }) let tryReconnect = true let reconnectEvent = false @@ -3730,7 +3745,7 @@ export default function abstractTest(server, config) { }) }) - context('with alternate server client', () => { + describe('with alternate server client', () => { let cachedClientListeners const connack = version === 5 ? { reasonCode: 0 } : { returnCode: 0 } @@ -3747,7 +3762,7 @@ export default function abstractTest(server, config) { }) }) - it('should resubscribe even if disconnect is before suback', function _test(done) { + it('should resubscribe even if disconnect is before suback', function _test(t, done) { const client = connect({ reconnectPeriod: 100, ...config }) let subscribeCount = 0 let connectCount = 0 @@ -3778,7 +3793,7 @@ export default function abstractTest(server, config) { client.subscribe('hello') }) - it('should resubscribe exactly once', function _test(done) { + it('should resubscribe exactly once', function _test(t, done) { const client = connect({ reconnectPeriod: 100, ...config }) let subscribeCount = 0 @@ -3809,7 +3824,7 @@ export default function abstractTest(server, config) { }) describe('message id to subscription topic mapping', () => { - it('should not create a mapping if resubscribe is disabled', function _test(done) { + it('should not create a mapping if resubscribe is disabled', function _test(t, done) { const client = connect({ resubscribe: false }) client.subscribe('test1') client.subscribe('test2') @@ -3817,7 +3832,7 @@ export default function abstractTest(server, config) { client.end(true, done) }) - it('should create a mapping for each subscribe call', function _test(done) { + it('should create a mapping for each subscribe call', function _test(t, done) { const client = connect() client.subscribe('test1') assert.strictEqual(Object.keys(client.messageIdToTopic).length, 1) @@ -3832,7 +3847,7 @@ export default function abstractTest(server, config) { client.end(true, done) }) - it('should remove the mapping after suback', function _test(done) { + it('should remove the mapping after suback', function _test(t, done) { const client = connect() client.once('connect', () => { client.subscribe('test1', { qos: 2 }, () => { diff --git a/test/abstract_store.ts b/test/abstract_store.ts index 1823344dc..3a1ea4a45 100644 --- a/test/abstract_store.ts +++ b/test/abstract_store.ts @@ -1,25 +1,29 @@ import { IPublishPacket, IPubrelPacket } from 'mqtt-packet' import { IStore } from '../src/lib/store' import 'should' +import { it, beforeEach, afterEach } from 'node:test' export default function abstractStoreTest( build: (cb: (err?: Error, store?: IStore) => void) => void, ) { let store: IStore - // eslint-disable-next-line - beforeEach(function (done) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + beforeEach((_ctx, done) => { build((err, _store) => { store = _store done(err) }) }) - afterEach(function test(done) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + afterEach((_ctx, done) => { store.close(done) }) - it('should put and stream in-flight packets', function test(done) { + it('should put and stream in-flight packets', function _test(t, done) { const packet: IPublishPacket = { topic: 'hello', payload: 'world', @@ -38,7 +42,7 @@ export default function abstractStoreTest( }) }) - it('should support destroying the stream', function test(done) { + it('should support destroying the stream', function _test(t, done) { const packet: IPublishPacket = { topic: 'hello', payload: 'world', @@ -56,7 +60,7 @@ export default function abstractStoreTest( }) }) - it('should add and del in-flight packets', function test(done) { + it('should add and del in-flight packets', function _test(t, done) { const packet: IPublishPacket = { topic: 'hello', payload: 'world', @@ -79,7 +83,7 @@ export default function abstractStoreTest( }) }) - it('should replace a packet when doing put with the same messageId', function test(done) { + it('should replace a packet when doing put with the same messageId', function _test(t, done) { const packet1: IPublishPacket = { cmd: 'publish', // added topic: 'hello', @@ -105,7 +109,7 @@ export default function abstractStoreTest( }) }) - it('should return the original packet on del', function test(done) { + it('should return the original packet on del', function _test(t, done) { const packet: IPublishPacket = { topic: 'hello', payload: 'world', @@ -127,7 +131,7 @@ export default function abstractStoreTest( }) }) - it('should get a packet with the same messageId', function test(done) { + it('should get a packet with the same messageId', function _test(t, done) { const packet: IPublishPacket = { topic: 'hello', payload: 'world', diff --git a/test/client.ts b/test/client.ts index a1fe30787..06696bc16 100644 --- a/test/client.ts +++ b/test/client.ts @@ -8,19 +8,25 @@ import mqttPacket from 'mqtt-packet' import { Duplex } from 'readable-stream' import Connection from 'mqtt-connection' import util from 'util' -import ports from './helpers/port_list' +import getPorts from './helpers/port_list' import serverBuilder from './server_helpers_for_client_tests' import _debug from 'debug' import { MqttServer } from './server' import abstractClientTests from './abstract_client' import { IClientOptions } from 'src/lib/client' +import { describe, it, after } from 'node:test' const debug = _debug('mqttjs:client-test') +const ports = getPorts(2) + describe('MqttClient', () => { let client: mqtt.MqttClient const server = serverBuilder('mqtt') - const config: IClientOptions = { protocol: 'mqtt', port: ports.PORT } + const config: IClientOptions = { + protocol: 'mqtt', + port: ports.PORT, + } server.listen(ports.PORT) after(() => { @@ -28,12 +34,14 @@ describe('MqttClient', () => { if (server.listening) { server.close() } + + process.exit(0) }) - abstractClientTests(server, config) + abstractClientTests(server, config, ports) describe('creating', () => { - it('should allow instantiation of MqttClient', function test(done) { + it('should allow instantiation of MqttClient', function _test(t, done) { try { client = new mqtt.MqttClient(() => { throw Error('break') @@ -45,7 +53,7 @@ describe('MqttClient', () => { } }) - it('should disable number cache if specified in options', function test(done) { + it('should disable number cache if specified in options', function _test(t, done) { try { assert.isTrue(mqttPacket.writeToStream.cacheNumbers) client = new mqtt.MqttClient( @@ -63,7 +71,7 @@ describe('MqttClient', () => { }) describe('message ids', () => { - it('should increment the message id', function test(done) { + it('should increment the message id', function _test(t, done) { client = mqtt.connect(config) const currentId = client['_nextId']() @@ -71,7 +79,7 @@ describe('MqttClient', () => { client.end((err) => done(err)) }) - it("should not throw an error if packet's messageId is not found when receiving a pubrel packet", function test(done) { + it("should not throw an error if packet's messageId is not found when receiving a pubrel packet", function _test(t, done) { const server2 = new MqttServer((serverClient) => { serverClient.on('connect', (packet) => { serverClient.connack({ returnCode: 0 }) @@ -99,7 +107,7 @@ describe('MqttClient', () => { }) }) - it('should not go overflow if the TCP frame contains a lot of PUBLISH packets', function test(done) { + it('should not go overflow if the TCP frame contains a lot of PUBLISH packets', function _test(t, done) { const parser = mqttPacket.parser() const max = 1000 let count = 0 @@ -112,7 +120,7 @@ describe('MqttClient', () => { }) client = new mqtt.MqttClient(() => duplex, {}) - client.on('message', (t, p, packet) => { + client.on('message', (topic, p, packet) => { if (++count === max) { // BUGBUG: the client.end callback never gets called here // client.end((err) => done(err)) @@ -154,440 +162,511 @@ describe('MqttClient', () => { }) describe('flushing', () => { - it('should attempt to complete pending unsub and send on ping timeout', function test(done) { - this.timeout(10000) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ returnCode: 0 }) - }) - }).listen(ports.PORTAND72) + it( + 'should attempt to complete pending unsub and send on ping timeout', + { + timeout: 10000, + }, + function _test(t, done) { + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ returnCode: 0 }) + }) + }).listen(ports.PORTAND72) - let pubCallbackCalled = false - let unsubscribeCallbackCalled = false - client = mqtt.connect({ - port: ports.PORTAND72, - host: 'localhost', - keepalive: 1, - connectTimeout: 350, - reconnectPeriod: 0, - }) - client.once('connect', () => { - client.publish( - 'fakeTopic', - 'fakeMessage', - { qos: 1 }, - (err) => { - assert.exists(err) - pubCallbackCalled = true - }, - ) - client.unsubscribe('fakeTopic', (err, result) => { - assert.exists(err) - unsubscribeCallbackCalled = true + let pubCallbackCalled = false + let unsubscribeCallbackCalled = false + client = mqtt.connect({ + port: ports.PORTAND72, + host: 'localhost', + keepalive: 1, + connectTimeout: 350, + reconnectPeriod: 0, }) - setTimeout(() => { - client.end((err1) => { - assert.strictEqual( - pubCallbackCalled && unsubscribeCallbackCalled, - true, - 'callbacks not invoked', - ) - server2.close((err2) => { - done(err1 || err2) - }) + client.once('connect', () => { + client.publish( + 'fakeTopic', + 'fakeMessage', + { qos: 1 }, + (err) => { + assert.exists(err) + pubCallbackCalled = true + }, + ) + client.unsubscribe('fakeTopic', (err, result) => { + assert.exists(err) + unsubscribeCallbackCalled = true }) - }, 5000) - }) - }) + setTimeout(() => { + client.end((err1) => { + assert.strictEqual( + pubCallbackCalled && unsubscribeCallbackCalled, + true, + 'callbacks not invoked', + ) + server2.close((err2) => { + done(err1 || err2) + }) + }) + }, 5000) + }) + }, + ) }) describe('reconnecting', () => { - it('should attempt to reconnect once server is down', function test(done) { - this.timeout(30000) - - const innerServer = fork( - path.join(__dirname, 'helpers', 'server_process.ts'), - { - execArgv: ['--inspect', '-r', 'ts-node/register'], - }, - ) - innerServer.on('close', (code) => { - if (code) { - done(util.format('child process closed with code %d', code)) - } - }) - - innerServer.on('exit', (code) => { - if (code) { - done(util.format('child process exited with code %d', code)) + it( + 'should attempt to reconnect once server is down', + { + timeout: 30000, + }, + function _test(t, done) { + const args = ['-r', 'ts-node/register'] + + if (process.env.DEBUG_SERVER) { + args.unshift('--inspect') } - }) - - client = mqtt.connect({ - port: 3481, - host: 'localhost', - keepalive: 1, - }) - client.once('connect', () => { - innerServer.kill('SIGINT') // mocks server shutdown - client.once('close', () => { - assert.exists(client['reconnectTimer']) - client.end(true, (err) => done(err)) + const innerServer = fork( + path.join(__dirname, 'helpers', 'server_process.ts'), + { + execArgv: args, + }, + ) + innerServer.on('close', (code) => { + if (code) { + done( + util.format( + 'child process closed with code %d', + code, + ), + ) + } }) - }) - }) - it('should reconnect if a connack is not received in an interval', function test(done) { - this.timeout(2000) - - const server2 = net.createServer().listen(ports.PORTAND43) - - server2.on('connection', (c) => { - eos(c, () => { - server2.close() + innerServer.on('exit', (code) => { + if (code) { + done( + util.format( + 'child process exited with code %d', + code, + ), + ) + } }) - }) - server2.on('listening', () => { client = mqtt.connect({ - servers: [ - { port: ports.PORTAND43, host: 'localhost_fake' }, - { port: ports.PORT, host: 'localhost' }, - ], - connectTimeout: 500, + port: 3481, + host: 'localhost', + keepalive: 1, }) - - server.once('client', () => { - client.end(false, (err) => { - done(err) + client.once('connect', () => { + innerServer.kill('SIGINT') // mocks server shutdown + client.once('close', () => { + assert.exists(client['reconnectTimer']) + client.end(true, (err) => done(err)) }) }) - - client.once('connect', () => { - client.stream.destroy() + }, + ) + + it( + 'should reconnect if a connack is not received in an interval', + { + timeout: 2000, + }, + function _test(t, done) { + const server2 = net.createServer().listen(ports.PORTAND43) + + server2.on('connection', (c) => { + eos(c, () => { + server2.close() + }) }) - }) - }) - - it('should not be cleared by the connack timer', function test(done) { - this.timeout(4000) - const server2 = net.createServer().listen(ports.PORTAND44) + server2.on('listening', () => { + client = mqtt.connect({ + servers: [ + { port: ports.PORTAND43, host: 'localhost_fake' }, + { port: ports.PORT, host: 'localhost' }, + ], + connectTimeout: 500, + }) - server2.on('connection', (c) => { - c.destroy() - }) + server.once('client', () => { + client.end(false, (err) => { + done(err) + }) + }) - server2.once('listening', () => { - const connectTimeout = 1000 - const reconnectPeriod = 100 - const expectedReconnects = Math.floor( - connectTimeout / reconnectPeriod, - ) - let reconnects = 0 - client = mqtt.connect({ - port: ports.PORTAND44, - host: 'localhost', - connectTimeout, - reconnectPeriod, + client.once('connect', () => { + client.stream.destroy() + }) }) - - client.on('reconnect', () => { - reconnects++ - if (reconnects >= expectedReconnects) { - client.end(true, (err) => done(err)) - } + }, + ) + + it( + 'should not be cleared by the connack timer', + { + timeout: 4000, + }, + function _test(t, done) { + const server2 = net.createServer().listen(ports.PORTAND44) + + server2.on('connection', (c) => { + c.destroy() }) - }) - }) - it('should not keep requeueing the first message when offline', function test(done) { - this.timeout(2500) - - const server2 = serverBuilder('mqtt').listen(ports.PORTAND45) - client = mqtt.connect({ - port: ports.PORTAND45, - host: 'localhost', - connectTimeout: 350, - reconnectPeriod: 300, - }) - - server2.on('client', (serverClient) => { - client.publish('hello', 'world', { qos: 1 }, () => { - serverClient.destroy() - server2.close(() => { - debug('now publishing message in an offline state') - client.publish('hello', 'world', { qos: 1 }) + server2.once('listening', () => { + const connectTimeout = 1000 + const reconnectPeriod = 100 + const expectedReconnects = Math.floor( + connectTimeout / reconnectPeriod, + ) + let reconnects = 0 + client = mqtt.connect({ + port: ports.PORTAND44, + host: 'localhost', + connectTimeout, + reconnectPeriod, }) - }) - }) - - setTimeout(() => { - if (client.queue.length === 0) { - debug('calling final client.end()') - client.end(true, (err) => done(err)) - } else { - debug('calling client.end()') - // Do not call done. We want to trigger a reconnect here. - client.end(true) - } - }, 2000) - }) - - it('should not send the same subscribe multiple times on a flaky connection', function test(done) { - this.timeout(3500) - - const KILL_COUNT = 4 - const subIds = {} - let killedConnections = 0 - client = mqtt.connect({ - port: ports.PORTAND46, - host: 'localhost', - connectTimeout: 350, - reconnectPeriod: 300, - }) - const server2 = new MqttServer((serverClient) => { - debug('client received on server2.') - debug('subscribing to topic `topic`') - client.subscribe('topic', () => { - debug( - 'once subscribed to topic, end client, destroy serverClient, and close server.', - ) - serverClient.destroy() - server2.close(() => { - client.end(true, (err) => done(err)) + client.on('reconnect', () => { + reconnects++ + if (reconnects >= expectedReconnects) { + client.end(true, (err) => done(err)) + } }) }) + }, + ) + + it( + 'should not keep requeueing the first message when offline', + { + timeout: 2500, + }, + function _test(t, done) { + const server2 = serverBuilder('mqtt').listen(ports.PORTAND45) + client = mqtt.connect({ + port: ports.PORTAND45, + host: 'localhost', + connectTimeout: 350, + reconnectPeriod: 300, + }) - serverClient.on('subscribe', (packet) => { - if (killedConnections < KILL_COUNT) { - // Kill the first few sub attempts to simulate a flaky connection - killedConnections++ + server2.on('client', (serverClient) => { + client.publish('hello', 'world', { qos: 1 }, () => { serverClient.destroy() - } else { - // Keep track of acks - if (!subIds[packet.messageId]) { - subIds[packet.messageId] = 0 - } - subIds[packet.messageId]++ - if (subIds[packet.messageId] > 1) { - done( - new Error( - `Multiple duplicate acked subscriptions received for messageId ${packet.messageId}`, - ), - ) - client.end(true) - serverClient.end() - server2.close() - } - - serverClient.suback({ - messageId: packet.messageId, - granted: packet.subscriptions.map((e) => e.qos), + server2.close(() => { + debug('now publishing message in an offline state') + client.publish('hello', 'world', { qos: 1 }) }) - } + }) }) - }).listen(ports.PORTAND46) - }) - it('should not fill the queue of subscribes if it cannot connect', function test(done) { - this.timeout(2500) - const server2 = net.createServer((stream) => { - const serverClient = new Connection(stream) - - serverClient.on('error', (e) => { - /* do nothing */ - }) - serverClient.on('connect', (packet) => { - serverClient.connack({ returnCode: 0 }) - serverClient.destroy() - }) - }) - - server2.listen(ports.PORTAND48, () => { + setTimeout(() => { + if (client.queue.length === 0) { + debug('calling final client.end()') + client.end(true, (err) => done(err)) + } else { + debug('calling client.end()') + // Do not call done. We want to trigger a reconnect here. + client.end(true) + } + }, 2000) + }, + ) + + it( + 'should not send the same subscribe multiple times on a flaky connection', + { + timeout: 3500, + }, + function _test(t, done) { + const KILL_COUNT = 4 + const subIds = {} + let killedConnections = 0 client = mqtt.connect({ - port: ports.PORTAND48, + port: ports.PORTAND46, host: 'localhost', connectTimeout: 350, reconnectPeriod: 300, }) - client.subscribe('hello') - - setTimeout(() => { - assert.equal(client.queue.length, 1) - client.end(true, (err) => done(err)) - }, 1000) - }) - }) - - it('should not send the same publish multiple times on a flaky connection', function test(done) { - this.timeout(3500) - - const KILL_COUNT = 4 - let killedConnections = 0 - const pubIds = {} - client = mqtt.connect({ - port: ports.PORTAND47, - host: 'localhost', - connectTimeout: 350, - reconnectPeriod: 300, - }) + const server2 = new MqttServer((serverClient) => { + debug('client received on server2.') + debug('subscribing to topic `topic`') + client.subscribe('topic', () => { + debug( + 'once subscribed to topic, end client, destroy serverClient, and close server.', + ) + serverClient.destroy() + server2.close(() => { + client.end(true, (err) => done(err)) + }) + }) - const server2 = net - .createServer((stream) => { - const serverClient = new Connection(stream) - serverClient.on('error', () => {}) - serverClient.on('connect', (packet) => { - if (packet.clientId === 'invalid') { - serverClient.connack({ returnCode: 2 }) + serverClient.on('subscribe', (packet) => { + if (killedConnections < KILL_COUNT) { + // Kill the first few sub attempts to simulate a flaky connection + killedConnections++ + serverClient.destroy() } else { - serverClient.connack({ returnCode: 0 }) + // Keep track of acks + if (!subIds[packet.messageId]) { + subIds[packet.messageId] = 0 + } + subIds[packet.messageId]++ + if (subIds[packet.messageId] > 1) { + done( + new Error( + `Multiple duplicate acked subscriptions received for messageId ${packet.messageId}`, + ), + ) + client.end(true) + serverClient.end() + server2.close() + } + + serverClient.suback({ + messageId: packet.messageId, + granted: packet.subscriptions.map((e) => e.qos), + }) } }) + }).listen(ports.PORTAND46) + }, + ) + + it( + 'should not fill the queue of subscribes if it cannot connect', + { + timeout: 2500, + }, + function _test(t, done) { + const server2 = net.createServer((stream) => { + const serverClient = new Connection(stream) - server2.emit('client', serverClient) + serverClient.on('error', (e) => { + /* do nothing */ + }) + serverClient.on('connect', (packet) => { + serverClient.connack({ returnCode: 0 }) + serverClient.destroy() + }) }) - .listen(ports.PORTAND47) - server2.on('client', (serverClient) => { - client.publish('topic', 'data', { qos: 1 }, () => { - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) + server2.listen(ports.PORTAND48, () => { + client = mqtt.connect({ + port: ports.PORTAND48, + host: 'localhost', + connectTimeout: 350, + reconnectPeriod: 300, }) + + client.subscribe('hello') + + setTimeout(() => { + assert.equal(client.queue.length, 1) + client.end(true, (err) => done(err)) + }, 1000) + }) + }, + ) + + it( + 'should not send the same publish multiple times on a flaky connection', + { + timeout: 3500, + }, + function _test(t, done) { + const KILL_COUNT = 4 + let killedConnections = 0 + const pubIds = {} + client = mqtt.connect({ + port: ports.PORTAND47, + host: 'localhost', + connectTimeout: 350, + reconnectPeriod: 300, }) - serverClient.on('publish', function onPublish(packet) { - if (killedConnections < KILL_COUNT) { - // Kill the first few pub attempts to simulate a flaky connection - killedConnections++ - serverClient.destroy() + const server2 = net + .createServer((stream) => { + const serverClient = new Connection(stream) + serverClient.on('error', () => {}) + serverClient.on('connect', (packet) => { + if (packet.clientId === 'invalid') { + serverClient.connack({ returnCode: 2 }) + } else { + serverClient.connack({ returnCode: 0 }) + } + }) - // to avoid receiving inflight messages - serverClient.removeListener('publish', onPublish) - } else { - // Keep track of acks - if (!pubIds[packet.messageId]) { - pubIds[packet.messageId] = 0 - } + server2.emit('client', serverClient) + }) + .listen(ports.PORTAND47) - pubIds[packet.messageId]++ + server2.on('client', (serverClient) => { + client.publish('topic', 'data', { qos: 1 }, () => { + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) + }) - if (pubIds[packet.messageId] > 1) { - done( - new Error( - `Multiple duplicate acked publishes received for messageId ${packet.messageId}`, - ), - ) - client.end(true) + serverClient.on('publish', function onPublish(packet) { + if (killedConnections < KILL_COUNT) { + // Kill the first few pub attempts to simulate a flaky connection + killedConnections++ serverClient.destroy() - server2.close() - } - serverClient.puback(packet) - } + // to avoid receiving inflight messages + serverClient.removeListener('publish', onPublish) + } else { + // Keep track of acks + if (!pubIds[packet.messageId]) { + pubIds[packet.messageId] = 0 + } + + pubIds[packet.messageId]++ + + if (pubIds[packet.messageId] > 1) { + done( + new Error( + `Multiple duplicate acked publishes received for messageId ${packet.messageId}`, + ), + ) + client.end(true) + serverClient.destroy() + server2.close() + } + + serverClient.puback(packet) + } + }) }) - }) - }) + }, + ) }) - it('check emit error on checkDisconnection w/o callback', function test(done) { - this.timeout(15000) - - const server2 = new MqttServer((c) => { - c.on('connect', (packet) => { - c.connack({ - reasonCode: 0, + it( + 'check emit error on checkDisconnection w/o callback', + { + timeout: 15000, + }, + function _test(t, done) { + const server2 = new MqttServer((c) => { + c.on('connect', (packet) => { + c.connack({ + reasonCode: 0, + }) }) - }) - c.on('publish', (packet) => { - setImmediate(() => { - packet.reasonCode = 0 - c.puback(packet) + c.on('publish', (packet) => { + setImmediate(() => { + packet.reasonCode = 0 + c.puback(packet) + }) }) - }) - }).listen(ports.PORTAND118) + }).listen(ports.PORTAND118) - const opts: IClientOptions = { - host: 'localhost', - port: ports.PORTAND118, - protocolVersion: 5, - } - client = mqtt.connect(opts) + const opts: IClientOptions = { + host: 'localhost', + port: ports.PORTAND118, + protocolVersion: 5, + } + client = mqtt.connect(opts) - // wait for the client to receive an error... - client.on('error', (error) => { - assert.equal(error.message, 'client disconnecting') - server2.close((err) => done(err)) - }) - client.on('connect', () => { - client.end(() => { - client['_checkDisconnecting']() + // wait for the client to receive an error... + client.on('error', (error) => { + assert.equal(error.message, 'client disconnecting') + server2.close((err) => done(err)) }) - }) - }) + client.on('connect', () => { + client.end(() => { + client['_checkDisconnecting']() + }) + }) + }, + ) describe('async methods', () => { - it('connect-subscribe-unsubscribe-end', function test() { - this.timeout(5000) - - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - server.once('client', (serverClient) => { - serverClient.on('publish', async (packet) => { - assert.equal(packet.topic, 'hello') - assert.equal(packet.payload.toString(), 'world') - await client.unsubscribeAsync('hello') - await client.endAsync() - resolve() + it( + 'connect-subscribe-unsubscribe-end', + { + timeout: 15000, + }, + function _test(t) { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + server.once('client', (serverClient) => { + serverClient.on('publish', async (packet) => { + assert.equal(packet.topic, 'hello') + assert.equal(packet.payload.toString(), 'world') + await client.unsubscribeAsync('hello') + await client.endAsync() + resolve() + }) }) - }) - - client = await mqtt.connectAsync(config) - const sub = await client.subscribeAsync('hello') + client = await mqtt.connectAsync(config) - assert.equal(sub[0].topic, 'hello') - assert.equal(sub[0].qos, 0) - - await client.publishAsync('hello', 'world') - }) - }) + const sub = await client.subscribeAsync('hello') - it('connect should throw error', async function test() { - this.timeout(5000) - let error = false + assert.equal(sub[0].topic, 'hello') + assert.equal(sub[0].qos, 0) - try { - await mqtt.connectAsync({ - port: 1000, - host: '127.0.0.1', + await client.publishAsync('hello', 'world') }) - } catch (err) { - error = true - assert.isTrue(err.message.includes('ECONNREFUSED')) - } - - assert.isTrue(error) - }) + }, + ) + + it( + 'connect should throw error', + { + timeout: 5000, + }, + async function _test(t) { + let error = false + + try { + await mqtt.connectAsync({ + port: 1000, + host: '127.0.0.1', + reconnectPeriod: 0, + }) + } catch (err) { + error = true + assert.isTrue(err.message.includes('ECONNREFUSED')) + } - it('publish should throw error', async function test() { - this.timeout(5000) - let error = false + assert.isTrue(error) + }, + ) + + it( + 'publish should throw error', + { + timeout: 5000, + }, + async function _test(t) { + let error = false + + try { + client = await mqtt.connectAsync(config) + client.disconnecting = true + await client.publishAsync('#/#', 'world') + } catch (err) { + error = true + client.disconnecting = false + assert.equal(err.message, 'client disconnecting') + } - try { - client = await mqtt.connectAsync(config) - client.disconnecting = true - await client.publishAsync('#/#', 'world') - } catch (err) { - error = true - assert.equal(err.message, 'client disconnecting') - } + await client.endAsync() - assert.isTrue(error) - }) + assert.isTrue(error) + }, + ) }) }) diff --git a/test/client_mqtt5.ts b/test/client_mqtt5.ts index d17c48266..6e1d709ca 100644 --- a/test/client_mqtt5.ts +++ b/test/client_mqtt5.ts @@ -3,8 +3,11 @@ import * as mqtt from '../src/mqtt' import abstractClientTests from './abstract_client' import { MqttServer } from './server' import serverBuilder from './server_helpers_for_client_tests' -import ports from './helpers/port_list' +import getPorts from './helpers/port_list' import { ErrorWithReasonCode } from '../src/lib/shared' +import { after, describe, it } from 'node:test' + +const ports = getPorts(1) describe('MQTT 5.0', () => { const server = serverBuilder('mqtt').listen(ports.PORTAND115) @@ -15,195 +18,95 @@ describe('MQTT 5.0', () => { properties: { maximumPacketSize: 200 }, } - abstractClientTests(server, config) - - it('topic should be complemented on receive', function test(done) { - this.timeout(15000) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - properties: { - topicAliasMaximum: 3, - }, + after(() => { + // clean up and make sure the server is no longer listening... + if (server.listening) { + server.close() } - const client = mqtt.connect(opts) - let publishCount = 0 - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - assert.strictEqual(packet.properties.topicAliasMaximum, 3) - serverClient.connack({ - reasonCode: 0, - }) - // register topicAlias - serverClient.publish({ - messageId: 0, - topic: 'test1', - payload: 'Message', - qos: 0, - properties: { topicAlias: 1 }, - }) - // use topicAlias - serverClient.publish({ - messageId: 0, - topic: '', - payload: 'Message', - qos: 0, - properties: { topicAlias: 1 }, - }) - // overwrite registered topicAlias - serverClient.publish({ - messageId: 0, - topic: 'test2', - payload: 'Message', - qos: 0, - properties: { topicAlias: 1 }, - }) - // use topicAlias - serverClient.publish({ - messageId: 0, - topic: '', - payload: 'Message', - qos: 0, - properties: { topicAlias: 1 }, - }) - }) - }).listen(ports.PORTAND103) - - client.on('message', (topic, messagee, packet) => { - switch (publishCount++) { - case 0: - assert.strictEqual(topic, 'test1') - assert.strictEqual(packet.topic, 'test1') - assert.strictEqual(packet.properties.topicAlias, 1) - break - case 1: - assert.strictEqual(topic, 'test1') - assert.strictEqual(packet.topic, '') - assert.strictEqual(packet.properties.topicAlias, 1) - break - case 2: - assert.strictEqual(topic, 'test2') - assert.strictEqual(packet.topic, 'test2') - assert.strictEqual(packet.properties.topicAlias, 1) - break - case 3: - assert.strictEqual(topic, 'test2') - assert.strictEqual(packet.topic, '') - assert.strictEqual(packet.properties.topicAlias, 1) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) - }) - break - } - }) - }) - it('registered topic alias should automatically used if autoUseTopicAlias is true', function test(done) { - this.timeout(15000) + process.exit(0) + }) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - autoUseTopicAlias: true, - } - const client = mqtt.connect(opts) - - let publishCount = 0 - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - properties: { - topicAliasMaximum: 3, - }, + abstractClientTests(server, config, ports) + + it( + 'topic should be complemented on receive', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + properties: { + topicAliasMaximum: 3, + }, + } + const client = mqtt.connect(opts) + let publishCount = 0 + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + assert.strictEqual(packet.properties.topicAliasMaximum, 3) + serverClient.connack({ + reasonCode: 0, + }) + // register topicAlias + serverClient.publish({ + messageId: 0, + topic: 'test1', + payload: 'Message', + qos: 0, + properties: { topicAlias: 1 }, + }) + // use topicAlias + serverClient.publish({ + messageId: 0, + topic: '', + payload: 'Message', + qos: 0, + properties: { topicAlias: 1 }, + }) + // overwrite registered topicAlias + serverClient.publish({ + messageId: 0, + topic: 'test2', + payload: 'Message', + qos: 0, + properties: { topicAlias: 1 }, + }) + // use topicAlias + serverClient.publish({ + messageId: 0, + topic: '', + payload: 'Message', + qos: 0, + properties: { topicAlias: 1 }, + }) }) - }) - serverClient.on('publish', (packet) => { + }).listen(ports.PORTAND103) + + client.on('message', (topic, messagee, packet) => { switch (publishCount++) { case 0: + assert.strictEqual(topic, 'test1') assert.strictEqual(packet.topic, 'test1') assert.strictEqual(packet.properties.topicAlias, 1) break case 1: + assert.strictEqual(topic, 'test1') assert.strictEqual(packet.topic, '') assert.strictEqual(packet.properties.topicAlias, 1) break case 2: - assert.strictEqual(packet.topic, '') - assert.strictEqual(packet.properties.topicAlias, 1) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) - }) - break - } - }) - }).listen(ports.PORTAND103) - - client.on('connect', () => { - // register topicAlias - client.publish('test1', 'Message', { - properties: { topicAlias: 1 }, - }) - // use topicAlias - client.publish('', 'Message', { properties: { topicAlias: 1 } }) - // use topicAlias by autoApplyTopicAlias - client.publish('test1', 'Message') - }) - }) - - it('topicAlias is automatically used if autoAssignTopicAlias is true', function test(done) { - this.timeout(15000) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - autoAssignTopicAlias: true, - } - const client = mqtt.connect(opts) - - let publishCount = 0 - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - properties: { - topicAliasMaximum: 3, - }, - }) - }) - serverClient.on('publish', (packet) => { - switch (publishCount++) { - case 0: - assert.strictEqual(packet.topic, 'test1') - assert.strictEqual(packet.properties.topicAlias, 1) - break - case 1: + assert.strictEqual(topic, 'test2') assert.strictEqual(packet.topic, 'test2') - assert.strictEqual(packet.properties.topicAlias, 2) - break - case 2: - assert.strictEqual(packet.topic, 'test3') - assert.strictEqual(packet.properties.topicAlias, 3) + assert.strictEqual(packet.properties.topicAlias, 1) break case 3: + assert.strictEqual(topic, 'test2') assert.strictEqual(packet.topic, '') assert.strictEqual(packet.properties.topicAlias, 1) - break - case 4: - assert.strictEqual(packet.topic, '') - assert.strictEqual(packet.properties.topicAlias, 3) - break - case 5: - assert.strictEqual(packet.topic, 'test4') - assert.strictEqual(packet.properties.topicAlias, 2) client.end(true, (err1) => { server2.close((err2) => { done(err1 || err2) @@ -212,562 +115,803 @@ describe('MQTT 5.0', () => { break } }) - }).listen(ports.PORTAND103) - - client.on('connect', () => { - // register topicAlias - client.publish('test1', 'Message') - client.publish('test2', 'Message') - client.publish('test3', 'Message') + }, + ) + + it( + 'registered topic alias should automatically used if autoUseTopicAlias is true', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + autoUseTopicAlias: true, + } + const client = mqtt.connect(opts) + + let publishCount = 0 + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + properties: { + topicAliasMaximum: 3, + }, + }) + }) + serverClient.on('publish', (packet) => { + switch (publishCount++) { + case 0: + assert.strictEqual(packet.topic, 'test1') + assert.strictEqual(packet.properties.topicAlias, 1) + break + case 1: + assert.strictEqual(packet.topic, '') + assert.strictEqual(packet.properties.topicAlias, 1) + break + case 2: + assert.strictEqual(packet.topic, '') + assert.strictEqual(packet.properties.topicAlias, 1) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) + break + } + }) + }).listen(ports.PORTAND103) - // use topicAlias - client.publish('test1', 'Message') - client.publish('test3', 'Message') + client.on('connect', () => { + // register topicAlias + client.publish('test1', 'Message', { + properties: { topicAlias: 1 }, + }) + // use topicAlias + client.publish('', 'Message', { properties: { topicAlias: 1 } }) + // use topicAlias by autoApplyTopicAlias + client.publish('test1', 'Message') + }) + }, + ) + + it( + 'topicAlias is automatically used if autoAssignTopicAlias is true', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + autoAssignTopicAlias: true, + } + const client = mqtt.connect(opts) + + let publishCount = 0 + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + properties: { + topicAliasMaximum: 3, + }, + }) + }) + serverClient.on('publish', (packet) => { + switch (publishCount++) { + case 0: + assert.strictEqual(packet.topic, 'test1') + assert.strictEqual(packet.properties.topicAlias, 1) + break + case 1: + assert.strictEqual(packet.topic, 'test2') + assert.strictEqual(packet.properties.topicAlias, 2) + break + case 2: + assert.strictEqual(packet.topic, 'test3') + assert.strictEqual(packet.properties.topicAlias, 3) + break + case 3: + assert.strictEqual(packet.topic, '') + assert.strictEqual(packet.properties.topicAlias, 1) + break + case 4: + assert.strictEqual(packet.topic, '') + assert.strictEqual(packet.properties.topicAlias, 3) + break + case 5: + assert.strictEqual(packet.topic, 'test4') + assert.strictEqual(packet.properties.topicAlias, 2) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) + break + } + }) + }).listen(ports.PORTAND103) - // renew LRU topicAlias - client.publish('test4', 'Message') - }) - }) + client.on('connect', () => { + // register topicAlias + client.publish('test1', 'Message') + client.publish('test2', 'Message') + client.publish('test3', 'Message') - it('topicAlias should be removed and topic restored on resend', function test(done) { - this.timeout(15000) - - const incomingStore = new mqtt.Store({ clean: false }) - const outgoingStore = new mqtt.Store({ clean: false }) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - clientId: 'cid1', - incomingStore, - outgoingStore, - clean: false, - reconnectPeriod: 100, - } - const client = mqtt.connect(opts) + // use topicAlias + client.publish('test1', 'Message') + client.publish('test3', 'Message') - let connectCount = 0 - let publishCount = 0 - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - switch (connectCount++) { - case 0: - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - properties: { - topicAliasMaximum: 3, - }, - }) - break - case 1: - serverClient.connack({ - reasonCode: 0, - sessionPresent: true, - properties: { - topicAliasMaximum: 3, - }, - }) - break - } + // renew LRU topicAlias + client.publish('test4', 'Message') }) - serverClient.on('publish', (packet) => { - switch (publishCount++) { - case 0: - assert.strictEqual(packet.topic, 'test1') - assert.strictEqual(packet.properties.topicAlias, 1) - break - case 1: - assert.strictEqual(packet.topic, '') - assert.strictEqual(packet.properties.topicAlias, 1) - setImmediate(() => { - serverClient.stream.destroy() - }) - break - case 2: { - assert.strictEqual(packet.topic, 'test1') - let alias1 - if (packet.properties) { - alias1 = packet.properties.topicAlias - } - assert.strictEqual(alias1, undefined) - serverClient.puback({ messageId: packet.messageId }) - break + }, + ) + + it( + 'topicAlias should be removed and topic restored on resend', + { + timeout: 15000, + }, + function _test(t, done) { + const incomingStore = new mqtt.Store({ clean: false }) + const outgoingStore = new mqtt.Store({ clean: false }) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + clientId: 'cid1', + incomingStore, + outgoingStore, + clean: false, + reconnectPeriod: 100, + } + const client = mqtt.connect(opts) + + let connectCount = 0 + let publishCount = 0 + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + switch (connectCount++) { + case 0: + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + properties: { + topicAliasMaximum: 3, + }, + }) + break + case 1: + serverClient.connack({ + reasonCode: 0, + sessionPresent: true, + properties: { + topicAliasMaximum: 3, + }, + }) + break } - case 3: { - assert.strictEqual(packet.topic, 'test1') - let alias2 - if (packet.properties) { - alias2 = packet.properties.topicAlias + }) + serverClient.on('publish', (packet) => { + switch (publishCount++) { + case 0: + assert.strictEqual(packet.topic, 'test1') + assert.strictEqual(packet.properties.topicAlias, 1) + break + case 1: + assert.strictEqual(packet.topic, '') + assert.strictEqual(packet.properties.topicAlias, 1) + setImmediate(() => { + serverClient.stream.destroy() + }) + break + case 2: { + assert.strictEqual(packet.topic, 'test1') + let alias1 + if (packet.properties) { + alias1 = packet.properties.topicAlias + } + assert.strictEqual(alias1, undefined) + serverClient.puback({ messageId: packet.messageId }) + break } - assert.strictEqual(alias2, undefined) - serverClient.puback({ messageId: packet.messageId }) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) + case 3: { + assert.strictEqual(packet.topic, 'test1') + let alias2 + if (packet.properties) { + alias2 = packet.properties.topicAlias + } + assert.strictEqual(alias2, undefined) + serverClient.puback({ messageId: packet.messageId }) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) }) - }) - break + break + } } - } - }) - }).listen(ports.PORTAND103) - - client.once('connect', () => { - // register topicAlias - client.publish('test1', 'Message', { - qos: 1, - properties: { topicAlias: 1 }, - }) - // use topicAlias - client.publish('', 'Message', { - qos: 1, - properties: { topicAlias: 1 }, - }) - }) - }) - - it('topicAlias should be removed and topic restored on offline publish', function test(done) { - this.timeout(15000) - - const incomingStore = new mqtt.Store({ clean: false }) - const outgoingStore = new mqtt.Store({ clean: false }) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - clientId: 'cid1', - incomingStore, - outgoingStore, - clean: false, - reconnectPeriod: 100, - } - const client = mqtt.connect(opts) + }) + }).listen(ports.PORTAND103) - let connectCount = 0 - let publishCount = 0 - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - switch (connectCount++) { - case 0: - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - properties: { - topicAliasMaximum: 3, - }, - }) - setImmediate(() => { - serverClient.stream.destroy() - }) - break - case 1: - serverClient.connack({ - reasonCode: 0, - sessionPresent: true, - properties: { - topicAliasMaximum: 3, - }, - }) - break - } + client.once('connect', () => { + // register topicAlias + client.publish('test1', 'Message', { + qos: 1, + properties: { topicAlias: 1 }, + }) + // use topicAlias + client.publish('', 'Message', { + qos: 1, + properties: { topicAlias: 1 }, + }) }) - serverClient.on('publish', (packet) => { - switch (publishCount++) { - case 0: { - assert.strictEqual(packet.topic, 'test1') - let alias1 - if (packet.properties) { - alias1 = packet.properties.topicAlias - } - assert.strictEqual(alias1, undefined) - assert.strictEqual(packet.qos, 1) - serverClient.puback({ messageId: packet.messageId }) - break + }, + ) + + it( + 'topicAlias should be removed and topic restored on offline publish', + { + timeout: 15000, + }, + function _test(t, done) { + const incomingStore = new mqtt.Store({ clean: false }) + const outgoingStore = new mqtt.Store({ clean: false }) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + clientId: 'cid1', + incomingStore, + outgoingStore, + clean: false, + reconnectPeriod: 100, + } + const client = mqtt.connect(opts) + + let connectCount = 0 + let publishCount = 0 + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + switch (connectCount++) { + case 0: + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + properties: { + topicAliasMaximum: 3, + }, + }) + setImmediate(() => { + serverClient.stream.destroy() + }) + break + case 1: + serverClient.connack({ + reasonCode: 0, + sessionPresent: true, + properties: { + topicAliasMaximum: 3, + }, + }) + break } - case 1: { - assert.strictEqual(packet.topic, 'test1') - let alias2 - if (packet.properties) { - alias2 = packet.properties.topicAlias + }) + serverClient.on('publish', (packet) => { + switch (publishCount++) { + case 0: { + assert.strictEqual(packet.topic, 'test1') + let alias1 + if (packet.properties) { + alias1 = packet.properties.topicAlias + } + assert.strictEqual(alias1, undefined) + assert.strictEqual(packet.qos, 1) + serverClient.puback({ messageId: packet.messageId }) + break } - assert.strictEqual(alias2, undefined) - assert.strictEqual(packet.qos, 0) - break - } - case 2: { - assert.strictEqual(packet.topic, 'test1') - let alias3 - if (packet.properties) { - alias3 = packet.properties.topicAlias + case 1: { + assert.strictEqual(packet.topic, 'test1') + let alias2 + if (packet.properties) { + alias2 = packet.properties.topicAlias + } + assert.strictEqual(alias2, undefined) + assert.strictEqual(packet.qos, 0) + break + } + case 2: { + assert.strictEqual(packet.topic, 'test1') + let alias3 + if (packet.properties) { + alias3 = packet.properties.topicAlias + } + assert.strictEqual(alias3, undefined) + assert.strictEqual(packet.qos, 0) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) + break } - assert.strictEqual(alias3, undefined) - assert.strictEqual(packet.qos, 0) + } + }) + }).listen(ports.PORTAND103) + + client.once('close', () => { + // register topicAlias + client.publish('test1', 'Message', { + qos: 0, + properties: { topicAlias: 1 }, + }) + // use topicAlias + client.publish('', 'Message', { + qos: 0, + properties: { topicAlias: 1 }, + }) + client.publish('', 'Message', { + qos: 1, + properties: { topicAlias: 1 }, + }) + }) + }, + ) + + it( + 'should error cb call if PUBLISH out of range topicAlias', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + } + const client = mqtt.connect(opts) + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + properties: { + topicAliasMaximum: 3, + }, + }) + }) + }).listen(ports.PORTAND103) + + client.on('connect', () => { + // register topicAlias + client.publish( + 'test1', + 'Message', + { properties: { topicAlias: 4 } }, + (error) => { + assert.strictEqual( + error.message, + 'Sending Topic Alias out of range', + ) client.end(true, (err1) => { server2.close((err2) => { done(err1 || err2) }) }) - break - } - } - }) - }).listen(ports.PORTAND103) - - client.once('close', () => { - // register topicAlias - client.publish('test1', 'Message', { - qos: 0, - properties: { topicAlias: 1 }, - }) - // use topicAlias - client.publish('', 'Message', { - qos: 0, - properties: { topicAlias: 1 }, - }) - client.publish('', 'Message', { - qos: 1, - properties: { topicAlias: 1 }, + }, + ) }) - }) - }) - - it('should error cb call if PUBLISH out of range topicAlias', function test(done) { - this.timeout(15000) + }, + ) + + it( + 'should error cb call if PUBLISH out of range topicAlias on topicAlias disabled by broker', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + } + const client = mqtt.connect(opts) + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) + }) + }).listen(ports.PORTAND103) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - properties: { - topicAliasMaximum: 3, + client.on('connect', () => { + // register topicAlias + client.publish( + 'test1', + 'Message', + { properties: { topicAlias: 1 } }, + (error) => { + assert.strictEqual( + error.message, + 'Sending Topic Alias out of range', + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) }, - }) + ) }) - }).listen(ports.PORTAND103) - - client.on('connect', () => { - // register topicAlias - client.publish( - 'test1', - 'Message', - { properties: { topicAlias: 4 } }, - (error) => { - assert.strictEqual( - error.message, - 'Sending Topic Alias out of range', - ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) - }) + }, + ) + + it( + 'should throw an error if broker PUBLISH out of range topicAlias', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + properties: { + topicAliasMaximum: 3, }, - ) - }) - }) - - it('should error cb call if PUBLISH out of range topicAlias on topicAlias disabled by broker', function test(done) { - this.timeout(15000) + } + const client = mqtt.connect(opts) + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) + // register out of range topicAlias + serverClient.publish({ + messageId: 0, + topic: 'test1', + payload: 'Message', + qos: 0, + properties: { topicAlias: 4 }, + }) + }) + }).listen(ports.PORTAND103) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'Received Topic Alias is out of range', + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) }) }) - }).listen(ports.PORTAND103) - - client.on('connect', () => { - // register topicAlias - client.publish( - 'test1', - 'Message', - { properties: { topicAlias: 1 } }, - (error) => { - assert.strictEqual( - error.message, - 'Sending Topic Alias out of range', - ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) - }) + }, + ) + + it( + 'should throw an error if broker PUBLISH topicAlias:0', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + properties: { + topicAliasMaximum: 3, }, - ) - }) - }) - - it('should throw an error if broker PUBLISH out of range topicAlias', function test(done) { - this.timeout(15000) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - properties: { - topicAliasMaximum: 3, - }, - } - const client = mqtt.connect(opts) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - }) - // register out of range topicAlias - serverClient.publish({ - messageId: 0, - topic: 'test1', - payload: 'Message', - qos: 0, - properties: { topicAlias: 4 }, + } + const client = mqtt.connect(opts) + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) + // register out of range topicAlias + serverClient.publish({ + messageId: 0, + topic: 'test1', + payload: 'Message', + qos: 0, + properties: { topicAlias: 0 }, + }) }) - }) - }).listen(ports.PORTAND103) + }).listen(ports.PORTAND103) - client.on('error', (error) => { - assert.strictEqual( - error.message, - 'Received Topic Alias is out of range', - ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'Received Topic Alias is out of range', + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) }) }) - }) - }) - - it('should throw an error if broker PUBLISH topicAlias:0', function test(done) { - this.timeout(15000) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - properties: { - topicAliasMaximum: 3, - }, - } - const client = mqtt.connect(opts) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, + }, + ) + + it( + 'should throw an error if broker PUBLISH unregistered topicAlias', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND103, + protocolVersion: 5, + properties: { + topicAliasMaximum: 3, + }, + } + const client = mqtt.connect(opts) + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) + // register out of range topicAlias + serverClient.publish({ + messageId: 0, + topic: '', // use topic alias + payload: 'Message', + qos: 0, + properties: { topicAlias: 1 }, // in range topic alias + }) }) - // register out of range topicAlias - serverClient.publish({ - messageId: 0, - topic: 'test1', - payload: 'Message', - qos: 0, - properties: { topicAlias: 0 }, + }).listen(ports.PORTAND103) + + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'Received unregistered Topic Alias', + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) }) }) - }).listen(ports.PORTAND103) - - client.on('error', (error) => { - assert.strictEqual( - error.message, - 'Received Topic Alias is out of range', - ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) + }, + ) + + it( + 'should throw an error if there is Auth Data with no Auth Method', + { + timeout: 5000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND115, + protocolVersion: 5, + properties: { authenticationData: Buffer.from([1, 2, 3, 4]) }, + } + console.log('client connecting') + const client = mqtt.connect(opts) + client.on('error', (error) => { + console.log('error hit') + assert.strictEqual( + error.message, + 'Packet has no Authentication Method', + ) + // client will not be connected, so we will call done. + assert.isTrue( + client.disconnected, + 'validate client is disconnected', + ) + client.end(true, done) }) - }) - }) - - it('should throw an error if broker PUBLISH unregistered topicAlias', function test(done) { - this.timeout(15000) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND103, - protocolVersion: 5, - properties: { - topicAliasMaximum: 3, - }, - } - const client = mqtt.connect(opts) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - }) - // register out of range topicAlias - serverClient.publish({ - messageId: 0, - topic: '', // use topic alias - payload: 'Message', - qos: 0, - properties: { topicAlias: 1 }, // in range topic alias + }, + ) + + it( + 'auth packet', + { + timeout: 2500, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND115, + protocolVersion: 5, + properties: { authenticationMethod: 'json' }, + authPacket: {}, + manualConnect: true, + } + let authSent = false + + const client = mqtt.connect(opts) + server.once('client', (c) => { + // this test is flaky, there is a race condition + // that could make the test fail as the auth packet + // is sent by the client even before connack so it could arrive before + // the clientServer is listening for the auth packet. To avoid this + // if the event is not emitted we simply check if + // the auth packet is sent after 1 second. + let closeTimeout = setTimeout(() => { + assert.isTrue(authSent) + closeTimeout = null + client.end(true, done) + }, 1000) + + c.on('auth', (packet) => { + if (closeTimeout) { + clearTimeout(closeTimeout) + client.end(done) + } }) }) - }).listen(ports.PORTAND103) - - client.on('error', (error) => { - assert.strictEqual( - error.message, - 'Received unregistered Topic Alias', - ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) + client.on('packetsend', (packet) => { + if (packet.cmd === 'auth') { + authSent = true + } }) - }) - }) - - it('should throw an error if there is Auth Data with no Auth Method', function test(done) { - this.timeout(5000) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND115, - protocolVersion: 5, - properties: { authenticationData: Buffer.from([1, 2, 3, 4]) }, - } - console.log('client connecting') - const client = mqtt.connect(opts) - client.on('error', (error) => { - console.log('error hit') - assert.strictEqual( - error.message, - 'Packet has no Authentication Method', - ) - // client will not be connected, so we will call done. - assert.isTrue( - client.disconnected, - 'validate client is disconnected', - ) - client.end(true, done) - }) - }) - it('auth packet', function test(done) { - this.timeout(2500) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND115, - protocolVersion: 5, - properties: { authenticationMethod: 'json' }, - authPacket: {}, - manualConnect: true, - } - let authSent = false - - const client = mqtt.connect(opts) - server.once('client', (c) => { - // this test is flaky, there is a race condition - // that could make the test fail as the auth packet - // is sent by the client even before connack so it could arrive before - // the clientServer is listening for the auth packet. To avoid this - // if the event is not emitted we simply check if - // the auth packet is sent after 1 second. - let closeTimeout = setTimeout(() => { - assert.isTrue(authSent) - closeTimeout = null + client.connect() + }, + ) + + it( + 'Maximum Packet Size', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND115, + protocolVersion: 5, + properties: { maximumPacketSize: 1 }, + } + const client = mqtt.connect(opts) + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'exceeding packets size connack', + ) client.end(true, done) - }, 1000) - - c.on('auth', (packet) => { - if (closeTimeout) { - clearTimeout(closeTimeout) - client.end(done) - } }) - }) - client.on('packetsend', (packet) => { - if (packet.cmd === 'auth') { - authSent = true + }, + ) + + it( + 'Change values of some properties by server response', + { + timeout: 15000, + }, + function _test(t, done) { + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + properties: { + serverKeepAlive: 16, + maximumPacketSize: 95, + }, + }) + }) + }).listen(ports.PORTAND116) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND116, + protocolVersion: 5, + properties: { + topicAliasMaximum: 10, + // serverKeepAlive: 11, + maximumPacketSize: 100, + }, } - }) - - client.connect() - }) - - it('Maximum Packet Size', function test(done) { - this.timeout(15000) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND115, - protocolVersion: 5, - properties: { maximumPacketSize: 1 }, - } - const client = mqtt.connect(opts) - client.on('error', (error) => { - assert.strictEqual(error.message, 'exceeding packets size connack') - client.end(true, done) - }) - }) - - it('Change values of some properties by server response', function test(done) { - this.timeout(15000) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - properties: { - serverKeepAlive: 16, - maximumPacketSize: 95, - }, + const client = mqtt.connect(opts) + client.on('connect', () => { + assert.strictEqual(client.options.keepalive, 16) + assert.strictEqual( + client.options.properties.maximumPacketSize, + 95, + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) }) }) - }).listen(ports.PORTAND116) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND116, - protocolVersion: 5, - properties: { - topicAliasMaximum: 10, - // serverKeepAlive: 11, - maximumPacketSize: 100, - }, - } - const client = mqtt.connect(opts) - client.on('connect', () => { - assert.strictEqual(client.options.keepalive, 16) - assert.strictEqual(client.options.properties.maximumPacketSize, 95) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) + }, + ) + + it( + 'should resubscribe when reconnecting with protocolVersion 5 and Session Present flag is false', + { + timeout: 15000, + }, + function _test(t, done) { + let tryReconnect = true + let reconnectEvent = false + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) + serverClient.on('subscribe', () => { + if (!tryReconnect) { + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) + } + }) }) + }).listen(ports.PORTAND316) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND316, + protocolVersion: 5, + } + const client = mqtt.connect(opts) + + client.on('reconnect', () => { + reconnectEvent = true }) - }) - }) - it('should resubscribe when reconnecting with protocolVersion 5 and Session Present flag is false', function test(done) { - this.timeout(15000) - let tryReconnect = true - let reconnectEvent = false - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, + client.on('connect', (connack) => { + assert.isFalse(connack.sessionPresent) + if (tryReconnect) { + client.subscribe('hello', () => { + client.stream.end() + }) + + tryReconnect = false + } else { + assert.isTrue(reconnectEvent) + } + }) + }, + ) + + it( + 'should resubscribe when reconnecting with protocolVersion 5 and properties', + { + // timeout: 15000, + }, + function _test(t, done) { + // this.timeout(15000) + let tryReconnect = true + let reconnectEvent = false + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + sessionPresent: false, + }) }) - serverClient.on('subscribe', () => { - if (!tryReconnect) { + serverClient.on('subscribe', (packet) => { + if (!reconnectEvent) { + serverClient.suback({ + messageId: packet.messageId, + granted: packet.subscriptions.map((e) => e.qos), + }) + } else if (!tryReconnect) { + assert.strictEqual( + packet.properties.userProperties.test, + 'test', + ) client.end(true, (err1) => { server2.close((err2) => { done(err1 || err2) @@ -775,92 +919,37 @@ describe('MQTT 5.0', () => { }) } }) - }) - }).listen(ports.PORTAND316) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND316, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - - client.on('reconnect', () => { - reconnectEvent = true - }) + }).listen(ports.PORTAND326) - client.on('connect', (connack) => { - assert.isFalse(connack.sessionPresent) - if (tryReconnect) { - client.subscribe('hello', () => { - client.stream.end() - }) - - tryReconnect = false - } else { - assert.isTrue(reconnectEvent) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND326, + protocolVersion: 5, } - }) - }) + const client = mqtt.connect(opts) - it('should resubscribe when reconnecting with protocolVersion 5 and properties', function test(done) { - // this.timeout(15000) - let tryReconnect = true - let reconnectEvent = false - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - sessionPresent: false, - }) + client.on('reconnect', () => { + reconnectEvent = true }) - serverClient.on('subscribe', (packet) => { - if (!reconnectEvent) { - serverClient.suback({ - messageId: packet.messageId, - granted: packet.subscriptions.map((e) => e.qos), - }) - } else if (!tryReconnect) { - assert.strictEqual( - packet.properties.userProperties.test, - 'test', + + client.on('connect', (connack) => { + assert.isFalse(connack.sessionPresent) + if (tryReconnect) { + client.subscribe( + 'hello', + { properties: { userProperties: { test: 'test' } } }, + () => { + client.stream.end() + }, ) - client.end(true, (err1) => { - server2.close((err2) => { - done(err1 || err2) - }) - }) + + tryReconnect = false + } else { + assert.isTrue(reconnectEvent) } }) - }).listen(ports.PORTAND326) - - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND326, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - - client.on('reconnect', () => { - reconnectEvent = true - }) - - client.on('connect', (connack) => { - assert.isFalse(connack.sessionPresent) - if (tryReconnect) { - client.subscribe( - 'hello', - { properties: { userProperties: { test: 'test' } } }, - () => { - client.stream.end() - }, - ) - - tryReconnect = false - } else { - assert.isTrue(reconnectEvent) - } - }) - }) + }, + ) const serverThatSendsErrors = new MqttServer((serverClient) => { serverClient.on('connect', (packet) => { @@ -894,371 +983,428 @@ describe('MQTT 5.0', () => { }) }) - it('Subscribe properties', function test(done) { - this.timeout(15000) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND119, - protocolVersion: 5, - } - const subOptions = { properties: { subscriptionIdentifier: 1234 } } - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, + it( + 'Subscribe properties', + { + timeout: 15000, + }, + function _test(t, done) { + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND119, + protocolVersion: 5, + } + const subOptions = { properties: { subscriptionIdentifier: 1234 } } + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, + }) + }) + serverClient.on('subscribe', (packet) => { + assert.strictEqual( + packet.properties.subscriptionIdentifier, + subOptions.properties.subscriptionIdentifier, + ) + client.end(true, (err1) => { + server2.close((err2) => { + done(err1 || err2) + }) + }) }) + }).listen(ports.PORTAND119) + + const client = mqtt.connect(opts) + client.on('connect', () => { + client.subscribe('a/b', subOptions) }) - serverClient.on('subscribe', (packet) => { - assert.strictEqual( - packet.properties.subscriptionIdentifier, - subOptions.properties.subscriptionIdentifier, + }, + ) + + it( + 'puback handling errors check', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + } + const client = mqtt.connect(opts) + client.once('connect', () => { + client.publish( + 'a/b', + 'message', + { qos: 1 }, + (err: ErrorWithReasonCode) => { + assert.strictEqual( + err.message, + 'Publish error: Session taken over', + ) + assert.strictEqual(err.code, 142) + }, ) client.end(true, (err1) => { - server2.close((err2) => { + serverThatSendsErrors.close((err2) => { done(err1 || err2) }) }) }) - }).listen(ports.PORTAND119) - - const client = mqtt.connect(opts) - client.on('connect', () => { - client.subscribe('a/b', subOptions) - }) - }) - - it('puback handling errors check', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - client.once('connect', () => { - client.publish( - 'a/b', - 'message', - { qos: 1 }, - (err: ErrorWithReasonCode) => { - assert.strictEqual( - err.message, - 'Publish error: Session taken over', - ) - assert.strictEqual(err.code, 142) - }, - ) - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) + }, + ) + + it( + 'pubrec handling errors check', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND118) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND118, + protocolVersion: 5, + } + const client = mqtt.connect(opts) + client.once('connect', () => { + client.publish( + 'a/b', + 'message', + { qos: 2 }, + (err: ErrorWithReasonCode) => { + assert.strictEqual( + err.message, + 'Publish error: Session taken over', + ) + assert.strictEqual(err.code, 142) + }, + ) + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) }) }) - }) - }) - - it('pubrec handling errors check', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND118) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND118, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - client.once('connect', () => { - client.publish( - 'a/b', - 'message', - { qos: 2 }, - (err: ErrorWithReasonCode) => { - assert.strictEqual( - err.message, - 'Publish error: Session taken over', - ) - assert.strictEqual(err.code, 142) + }, + ) + + it( + 'puback handling custom reason code', + { + // timeout: 15000, + }, + function _test(t, done) { + // this.timeout(15000) + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + let code = 0 + if (topic === 'a/b') { + code = 128 + } + cb(code) }, - ) - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) - }) - }) - }) - }) + } - it('puback handling custom reason code', function test(done) { - // this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - let code = 0 - if (topic === 'a/b') { - code = 128 - } - cb(code) - }, - } + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 1, + messageId: 1, + }) + }) - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 1, - messageId: 1, + serverClient.on('puback', (packet) => { + assert.strictEqual(packet.reasonCode, 128) + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) + }) }) }) - serverClient.on('puback', (packet) => { - assert.strictEqual(packet.reasonCode, 128) - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) + const client = mqtt.connect(opts) + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) + }) + }, + ) + + it( + 'server side disconnect', + { + timeout: 15000, + }, + function _test(t, done) { + const server2 = new MqttServer((serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ + reasonCode: 0, }) + serverClient.disconnect({ reasonCode: 128 }) + server2.close() }) }) - }) - - const client = mqtt.connect(opts) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) - }) + server2.listen(ports.PORTAND327) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND327, + protocolVersion: 5, + } - it('server side disconnect', function test(done) { - this.timeout(15000) - const server2 = new MqttServer((serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ - reasonCode: 0, - }) - serverClient.disconnect({ reasonCode: 128 }) - server2.close() + const client = mqtt.connect(opts) + client.once( + 'disconnect', + (disconnectPacket: mqtt.IDisconnectPacket) => { + assert.strictEqual(disconnectPacket.reasonCode, 128) + client.end(true, (err) => done(err)) + }, + ) + }, + ) + + it( + 'pubrec handling custom reason code', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + let code = 0 + if (topic === 'a/b') { + code = 128 + } + cb(code) + }, + } + const client = mqtt.connect(opts) + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) }) - }) - server2.listen(ports.PORTAND327) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND327, - protocolVersion: 5, - } - const client = mqtt.connect(opts) - client.once( - 'disconnect', - (disconnectPacket: mqtt.IDisconnectPacket) => { - assert.strictEqual(disconnectPacket.reasonCode, 128) - client.end(true, (err) => done(err)) - }, - ) - }) + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 2, + messageId: 1, + }) + }) - it('pubrec handling custom reason code', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - let code = 0 - if (topic === 'a/b') { - code = 128 - } - cb(code) - }, - } - const client = mqtt.connect(opts) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) + serverClient.on('pubrec', (packet) => { + assert.strictEqual(packet.reasonCode, 128) + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) + }) + }) + }) + }, + ) + + it( + 'puback handling custom reason code with error', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + const code = 0 + if (topic === 'a/b') { + cb(new Error('a/b is not valid')) + } + cb(code) + }, + } - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 2, - messageId: 1, + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 1, + messageId: 1, + }) }) }) - serverClient.on('pubrec', (packet) => { - assert.strictEqual(packet.reasonCode, 128) + const client = mqtt.connect(opts) + client.on('error', (error) => { + assert.strictEqual(error.message, 'a/b is not valid') client.end(true, (err1) => { serverThatSendsErrors.close((err2) => { done(err1 || err2) }) }) }) - }) - }) - - it('puback handling custom reason code with error', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - const code = 0 - if (topic === 'a/b') { - cb(new Error('a/b is not valid')) - } - cb(code) - }, - } - - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 1, - messageId: 1, - }) + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) }) - }) + }, + ) + + it( + 'pubrec handling custom reason code with error', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + const code = 0 + if (topic === 'a/b') { + cb(new Error('a/b is not valid')) + } + cb(code) + }, + } - const client = mqtt.connect(opts) - client.on('error', (error) => { - assert.strictEqual(error.message, 'a/b is not valid') - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 2, + messageId: 1, + }) }) }) - }) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) - }) - - it('pubrec handling custom reason code with error', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - const code = 0 - if (topic === 'a/b') { - cb(new Error('a/b is not valid')) - } - cb(code) - }, - } - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 2, - messageId: 1, + const client = mqtt.connect(opts) + client.on('error', (error) => { + assert.strictEqual(error.message, 'a/b is not valid') + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) }) }) - }) - - const client = mqtt.connect(opts) - client.on('error', (error) => { - assert.strictEqual(error.message, 'a/b is not valid') - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) - }) + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) }) - }) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) - }) - - it('puback handling custom invalid reason code', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - let code = 0 - if (topic === 'a/b') { - code = 124124 - } - cb(code) - }, - } + }, + ) + + it( + 'puback handling custom invalid reason code', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + let code = 0 + if (topic === 'a/b') { + code = 124124 + } + cb(code) + }, + } - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 1, - messageId: 1, + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 1, + messageId: 1, + }) }) }) - }) - const client = mqtt.connect(opts) - client.on('error', (error) => { - assert.strictEqual(error.message, 'Wrong reason code for puback') - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) + const client = mqtt.connect(opts) + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'Wrong reason code for puback', + ) + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) }) }) - }) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) - }) - - it('pubrec handling custom invalid reason code', function test(done) { - this.timeout(15000) - serverThatSendsErrors.listen(ports.PORTAND117) - const opts: mqtt.IClientOptions = { - host: 'localhost', - port: ports.PORTAND117, - protocolVersion: 5, - customHandleAcks(topic, message, packet, cb) { - let code = 0 - if (topic === 'a/b') { - code = 34535 - } - cb(code) - }, - } + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) + }) + }, + ) + + it( + 'pubrec handling custom invalid reason code', + { + timeout: 15000, + }, + function _test(t, done) { + serverThatSendsErrors.listen(ports.PORTAND117) + const opts: mqtt.IClientOptions = { + host: 'localhost', + port: ports.PORTAND117, + protocolVersion: 5, + customHandleAcks(topic, message, packet, cb) { + let code = 0 + if (topic === 'a/b') { + code = 34535 + } + cb(code) + }, + } - serverThatSendsErrors.once('client', (serverClient) => { - serverClient.once('subscribe', () => { - serverClient.publish({ - topic: 'a/b', - payload: 'payload', - qos: 2, - messageId: 1, + serverThatSendsErrors.once('client', (serverClient) => { + serverClient.once('subscribe', () => { + serverClient.publish({ + topic: 'a/b', + payload: 'payload', + qos: 2, + messageId: 1, + }) }) }) - }) - const client = mqtt.connect(opts) - client.on('error', (error) => { - assert.strictEqual(error.message, 'Wrong reason code for pubrec') - client.end(true, (err1) => { - serverThatSendsErrors.close((err2) => { - done(err1 || err2) + const client = mqtt.connect(opts) + client.on('error', (error) => { + assert.strictEqual( + error.message, + 'Wrong reason code for pubrec', + ) + client.end(true, (err1) => { + serverThatSendsErrors.close((err2) => { + done(err1 || err2) + }) }) }) - }) - client.once('connect', () => { - client.subscribe('a/b', { qos: 1 }) - }) - }) + client.once('connect', () => { + client.subscribe('a/b', { qos: 1 }) + }) + }, + ) }) diff --git a/test/helpers/leaked.ts b/test/helpers/leaked.ts new file mode 100644 index 000000000..413497be6 --- /dev/null +++ b/test/helpers/leaked.ts @@ -0,0 +1,8 @@ +// include this as first module when looking for leaked handles +import leaked from 'leaked-handles' + +leaked.set({ + fullStack: true, + timeout: 15000, + debugSockets: true, +}) diff --git a/test/helpers/port_list.ts b/test/helpers/port_list.ts index b4aed27d1..af2f7dbfe 100644 --- a/test/helpers/port_list.ts +++ b/test/helpers/port_list.ts @@ -1,51 +1,36 @@ -const PORT = 9876 -const PORTAND40 = PORT + 40 -const PORTAND41 = PORT + 41 -const PORTAND42 = PORT + 42 -const PORTAND43 = PORT + 43 -const PORTAND44 = PORT + 44 -const PORTAND45 = PORT + 45 -const PORTAND46 = PORT + 46 -const PORTAND47 = PORT + 47 -const PORTAND48 = PORT + 48 -const PORTAND49 = PORT + 49 -const PORTAND50 = PORT + 50 -const PORTAND72 = PORT + 72 -const PORTAND103 = PORT + 103 -const PORTAND114 = PORT + 114 -const PORTAND115 = PORT + 115 -const PORTAND116 = PORT + 116 -const PORTAND117 = PORT + 117 -const PORTAND118 = PORT + 118 -const PORTAND119 = PORT + 119 -const PORTAND316 = PORT + 316 -const PORTAND326 = PORT + 326 -const PORTAND327 = PORT + 327 -const PORTAND400 = PORT + 400 +/** + * Method used to get ports for testing + * @param i Index to shift the ports by + * @returns + */ +export default function getPorts(i = 0) { + const PORT = 10000 + i * 400 + const ports = { + PORT, + PORTAND40: PORT + 40, + PORTAND41: PORT + 41, + PORTAND42: PORT + 42, + PORTAND43: PORT + 43, + PORTAND44: PORT + 44, + PORTAND45: PORT + 45, + PORTAND46: PORT + 46, + PORTAND47: PORT + 47, + PORTAND48: PORT + 48, + PORTAND49: PORT + 49, + PORTAND50: PORT + 50, + PORTAND72: PORT + 72, + PORTAND103: PORT + 103, + PORTAND114: PORT + 114, + PORTAND115: PORT + 115, + PORTAND116: PORT + 116, + PORTAND117: PORT + 117, + PORTAND118: PORT + 118, + PORTAND119: PORT + 119, + PORTAND316: PORT + 316, + PORTAND326: PORT + 326, + PORTAND327: PORT + 327, + PORTAND400: PORT + 400, + } -export default { - PORT, - PORTAND40, - PORTAND41, - PORTAND42, - PORTAND43, - PORTAND44, - PORTAND45, - PORTAND46, - PORTAND47, - PORTAND48, - PORTAND49, - PORTAND50, - PORTAND72, - PORTAND103, - PORTAND114, - PORTAND115, - PORTAND116, - PORTAND117, - PORTAND118, - PORTAND119, - PORTAND316, - PORTAND326, - PORTAND327, - PORTAND400, + return ports } diff --git a/test/message-id-provider.ts b/test/message-id-provider.ts index 8068604aa..c45265b06 100644 --- a/test/message-id-provider.ts +++ b/test/message-id-provider.ts @@ -1,10 +1,11 @@ import { assert } from 'chai' import DefaultMessageIdProvider from '../src/lib/default-message-id-provider' import UniqueMessageIdProvider from '../src/lib/unique-message-id-provider' +import { describe, it } from 'node:test' describe('message id provider', () => { describe('default', () => { - it('should return 1 once the internal counter reached limit', () => { + it('should return 1 once the internal counter reached limit', (t) => { const provider = new DefaultMessageIdProvider() provider['nextId'] = 65535 @@ -12,7 +13,7 @@ describe('message id provider', () => { assert.equal(provider.allocate(), 1) }) - it('should return 65535 for last message id once the internal counter reached limit', () => { + it('should return 65535 for last message id once the internal counter reached limit', (t) => { const provider = new DefaultMessageIdProvider() provider['nextId'] = 65535 @@ -21,32 +22,32 @@ describe('message id provider', () => { assert.equal(provider.allocate(), 1) assert.equal(provider.getLastAllocated(), 1) }) - it('should return true when register with non allocated messageId', () => { + it('should return true when register with non allocated messageId', (t) => { const provider = new DefaultMessageIdProvider() assert.equal(provider.register(10), true) }) }) describe('unique', () => { - it('should return 1, 2, 3.., when allocate', () => { + it('should return 1, 2, 3.., when allocate', (t) => { const provider = new UniqueMessageIdProvider() assert.equal(provider.allocate(), 1) assert.equal(provider.allocate(), 2) assert.equal(provider.allocate(), 3) }) - it('should skip registerd messageId', () => { + it('should skip registerd messageId', (t) => { const provider = new UniqueMessageIdProvider() assert.equal(provider.register(2), true) assert.equal(provider.allocate(), 1) assert.equal(provider.allocate(), 3) }) - it('should return false register allocated messageId', () => { + it('should return false register allocated messageId', (t) => { const provider = new UniqueMessageIdProvider() assert.equal(provider.allocate(), 1) assert.equal(provider.register(1), false) assert.equal(provider.register(5), true) assert.equal(provider.register(5), false) }) - it('should retrun correct last messageId', () => { + it('should retrun correct last messageId', (t) => { const provider = new UniqueMessageIdProvider() assert.equal(provider.allocate(), 1) assert.equal(provider.getLastAllocated(), 1) @@ -55,7 +56,7 @@ describe('message id provider', () => { assert.equal(provider.allocate(), 3) assert.equal(provider.getLastAllocated(), 3) }) - it('should be reusable deallocated messageId', () => { + it('should be reusable deallocated messageId', (t) => { const provider = new UniqueMessageIdProvider() assert.equal(provider.allocate(), 1) assert.equal(provider.allocate(), 2) @@ -63,7 +64,7 @@ describe('message id provider', () => { provider.deallocate(2) assert.equal(provider.allocate(), 2) }) - it('should allocate all messageId and then return null', () => { + it('should allocate all messageId and then return null', (t) => { const provider = new UniqueMessageIdProvider() for (let i = 1; i <= 65535; i++) { assert.equal(provider.allocate(), i) @@ -73,7 +74,7 @@ describe('message id provider', () => { assert.equal(provider.allocate(), 10000) assert.equal(provider.allocate(), null) }) - it('should all messageId reallocatable after clear', () => { + it('should all messageId reallocatable after clear', (t) => { const provider = new UniqueMessageIdProvider() for (let i = 1; i <= 65535; i++) { assert.equal(provider.allocate(), i) diff --git a/test/mqtt.ts b/test/mqtt.ts index 52e97eca4..77df013e6 100644 --- a/test/mqtt.ts +++ b/test/mqtt.ts @@ -2,10 +2,12 @@ import fs from 'fs' import path from 'path' import * as mqtt from '../src/mqtt' import { IClientOptions } from '../src/lib/client' +import { describe, it } from 'node:test' +import 'should' describe('mqtt', () => { describe('#connect', () => { - it('should return an MqttClient when connect is called with mqtt:/ url', function test(done) { + it('should return an MqttClient when connect is called with mqtt:/ url', function _test(t, done) { const c = mqtt.connect('mqtt://localhost:1883') c.should.be.instanceOf(mqtt.MqttClient) @@ -24,7 +26,7 @@ describe('mqtt', () => { }).should.throw('Missing protocol') }) - it('should return an MqttClient with username option set', function test(done) { + it('should return an MqttClient with username option set', function _test(t, done) { const c = mqtt.connect('mqtt://user:pass@localhost:1883') c.should.be.instanceOf(mqtt.MqttClient) @@ -33,7 +35,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with username and password options set', function test(done) { + it('should return an MqttClient with username and password options set', function _test(t, done) { const c = mqtt.connect('mqtt://user@localhost:1883') c.should.be.instanceOf(mqtt.MqttClient) @@ -41,7 +43,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid with random value', function test(done) { + it('should return an MqttClient with the clientid with random value', function _test(t, done) { const c = mqtt.connect('mqtt://user@localhost:1883') c.should.be.instanceOf(mqtt.MqttClient) @@ -49,7 +51,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid with empty string', function test(done) { + it('should return an MqttClient with the clientid with empty string', function _test(t, done) { const c = mqtt.connect('mqtt://user@localhost:1883?clientId=') c.should.be.instanceOf(mqtt.MqttClient) @@ -57,7 +59,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid option set', function test(done) { + it('should return an MqttClient with the clientid option set', function _test(t, done) { const c = mqtt.connect('mqtt://user@localhost:1883?clientId=123') c.should.be.instanceOf(mqtt.MqttClient) @@ -65,14 +67,14 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient when connect is called with tcp:/ url', function test(done) { + it('should return an MqttClient when connect is called with tcp:/ url', function _test(t, done) { const c = mqtt.connect('tcp://localhost') c.should.be.instanceOf(mqtt.MqttClient) c.end((err) => done(err)) }) - it('should return an MqttClient with correct host when called with a host and port', function test(done) { + it('should return an MqttClient with correct host when called with a host and port', function _test(t, done) { const c = mqtt.connect('tcp://user:pass@localhost:1883') c.options.should.have.property('hostname', 'localhost') @@ -86,7 +88,7 @@ describe('mqtt', () => { caPaths: [path.join(__dirname, 'helpers', 'public-cert.pem')], } - it('should return an MqttClient when connect is called with mqtts:/ url', function test(done) { + it('should return an MqttClient when connect is called with mqtts:/ url', function _test(t, done) { const c = mqtt.connect('mqtts://localhost', sslOpts) c.options.should.have.property('protocol', 'mqtts') @@ -97,7 +99,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient when connect is called with ssl:/ url', function test(done) { + it('should return an MqttClient when connect is called with ssl:/ url', function _test(t, done) { const c = mqtt.connect('ssl://localhost', sslOpts) c.options.should.have.property('protocol', 'ssl') @@ -108,7 +110,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient when connect is called with ws:/ url', function test(done) { + it('should return an MqttClient when connect is called with ws:/ url', function _test(t, done) { const c = mqtt.connect('ws://localhost', sslOpts) c.options.should.have.property('protocol', 'ws') @@ -119,7 +121,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient when connect is called with wss:/ url', function test(done) { + it('should return an MqttClient when connect is called with wss:/ url', function _test(t, done) { const c = mqtt.connect('wss://localhost', sslOpts) c.options.should.have.property('protocol', 'wss') @@ -158,7 +160,7 @@ describe('mqtt', () => { }).should.throw() }) - it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtt', function test(done) { + it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtt', function _test(t, done) { sslOpts2.protocol = 'mqtt' const c = mqtt.connect(sslOpts2) @@ -170,7 +172,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtts', function test(done) { + it('should return a MqttClient with mqtts set when connect is called key and cert set and protocol mqtts', function _test(t, done) { sslOpts2.protocol = 'mqtts' const c = mqtt.connect(sslOpts2) @@ -182,7 +184,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return a MqttClient with wss set when connect is called key and cert set and protocol ws', function test(done) { + it('should return a MqttClient with wss set when connect is called key and cert set and protocol ws', function _test(t, done) { sslOpts2.protocol = 'ws' const c = mqtt.connect(sslOpts2) @@ -194,7 +196,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return a MqttClient with wss set when connect is called key and cert set and protocol wss', function test(done) { + it('should return a MqttClient with wss set when connect is called key and cert set and protocol wss', function _test(t, done) { sslOpts2.protocol = 'wss' const c = mqtt.connect(sslOpts2) @@ -206,7 +208,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid with option of clientId as empty string', function test(done) { + it('should return an MqttClient with the clientid with option of clientId as empty string', function _test(t, done) { const c = mqtt.connect('mqtt://localhost:1883', { clientId: '', }) @@ -216,7 +218,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid with option of clientId empty', function test(done) { + it('should return an MqttClient with the clientid with option of clientId empty', function _test(t, done) { const c = mqtt.connect('mqtt://localhost:1883') c.should.be.instanceOf(mqtt.MqttClient) @@ -224,7 +226,7 @@ describe('mqtt', () => { c.end((err) => done(err)) }) - it('should return an MqttClient with the clientid with option of with specific clientId', function test(done) { + it('should return an MqttClient with the clientid with option of with specific clientId', function _test(t, done) { const c = mqtt.connect('mqtt://localhost:1883', { clientId: '123', }) diff --git a/test/mqtt_store.ts b/test/mqtt_store.ts index 4e68b7091..62eeab752 100644 --- a/test/mqtt_store.ts +++ b/test/mqtt_store.ts @@ -1,7 +1,9 @@ import Store from '../src/lib/store' +import { describe, it } from 'node:test' +import 'should' describe('store in lib/connect/index.js (webpack entry point)', () => { - it('should create store', function test(done) { + it('should create store', function test(t, done) { const store = new Store() store.should.be.instanceOf(Store) done() diff --git a/test/runTests.ts b/test/runTests.ts new file mode 100644 index 000000000..57c5663b6 --- /dev/null +++ b/test/runTests.ts @@ -0,0 +1,57 @@ +import { readdirSync } from 'node:fs' +import { run } from 'node:test' +import process from 'node:process' +import { spec as Spec } from 'node:test/reporters' +import { basename } from 'node:path' +import { cpus } from 'node:os' + +const spec = new Spec() + +let exitCode = 0 + +const files = readdirSync(__dirname) + .filter((f) => f.endsWith('.ts') && f !== basename(__filename)) + .map((f) => `${__dirname}/${f}`) + +const start = Date.now() + +const testStream = run({ + files, + timeout: 60 * 1000, + concurrency: cpus().length, +}) + +testStream.compose(spec).pipe(process.stdout) + +const summary: string[] = [] + +testStream.on('test:fail', (data) => { + exitCode = 1 + const error = data.details.error + + summary.push( + `${data.file} - "${data.name}" (${Math.round( + data.details.duration_ms, + )}ms)\n${error.toString()} `, + ) +}) + +testStream.on('test:stderr', (data) => { + summary.push(`${data.file} - Error:\n${data.message} `) +}) + +testStream.once('end', () => { + const duration = Date.now() - start + // print duration in blue + console.log( + '\x1b[34m%s\x1b[0m', + `\nℹ Duration: ${duration / 1000}s\n`, + '\x1b[0m', + ) + if (summary.length > 0) { + console.error('\x1b[31m%s\x1b', '\n✖ failing tests:\n') + console.error(summary.join('\n')) + console.error('\n------', '\x1b[0m\n') + } + process.exit(exitCode) +}) diff --git a/test/secure_client.ts b/test/secure_client.ts index f0506734a..eec731561 100644 --- a/test/secure_client.ts +++ b/test/secure_client.ts @@ -5,8 +5,12 @@ import abstractClientTests from './abstract_client' import { MqttSecureServer, MqttServerListener } from './server' import { assert } from 'chai' import 'should' +import { describe, it, after } from 'node:test' +import getPorts from './helpers/port_list' -const port = 9899 +const ports = getPorts(5) + +const port = ports.PORT const KEY = path.join(__dirname, 'helpers', 'tls-key.pem') const CERT = path.join(__dirname, 'helpers', 'tls-cert.pem') const WRONG_CERT = path.join(__dirname, 'helpers', 'wrong-cert.pem') @@ -78,10 +82,20 @@ const server = new MqttSecureServer( describe('MqttSecureClient', () => { const config = { protocol: 'mqtts', port, rejectUnauthorized: false } - abstractClientTests(server, config) + + after(() => { + // clean up and make sure the server is no longer listening... + if (server.listening) { + server.close() + } + + process.exit(0) + }) + + abstractClientTests(server, config, ports) describe('with secure parameters', () => { - it('should validate successfully the CA', function test(done) { + it('should validate successfully the CA', function _test(t, done) { const client = mqtt.connect({ protocol: 'mqtts', port, @@ -98,7 +112,7 @@ describe('MqttSecureClient', () => { }) }) - it('should validate successfully the CA using URI', function test(done) { + it('should validate successfully the CA using URI', function _test(t, done) { const client = mqtt.connect(`mqtts://localhost:${port}`, { ca: [fs.readFileSync(CERT)], rejectUnauthorized: true, @@ -113,7 +127,7 @@ describe('MqttSecureClient', () => { }) }) - it('should validate successfully the CA using URI with path', function test(done) { + it('should validate successfully the CA using URI with path', function _test(t, done) { const client = mqtt.connect(`mqtts://localhost:${port}/`, { ca: [fs.readFileSync(CERT)], rejectUnauthorized: true, @@ -128,7 +142,7 @@ describe('MqttSecureClient', () => { }) }) - it('should validate unsuccessfully the CA', function test(done) { + it('should validate unsuccessfully the CA', function _test(t, done) { const client = mqtt.connect({ protocol: 'mqtts', port, @@ -142,7 +156,7 @@ describe('MqttSecureClient', () => { }) }) - it('should emit close on TLS error', function test(done) { + it('should emit close on TLS error', function _test(t, done) { const client = mqtt.connect({ protocol: 'mqtts', port, @@ -157,7 +171,7 @@ describe('MqttSecureClient', () => { }) }) - it('should support SNI on the TLS connection', function test(done) { + it('should support SNI on the TLS connection', function _test(t, done) { const hostname = 'localhost' server.removeAllListeners('secureConnection') // clear eventHandler diff --git a/test/server_helpers_for_client_tests.ts b/test/server_helpers_for_client_tests.ts index 27b962cf5..b82a0ff34 100644 --- a/test/server_helpers_for_client_tests.ts +++ b/test/server_helpers_for_client_tests.ts @@ -24,7 +24,9 @@ export default function serverBuilder( protocol: string, handler?: MqttServerListener, ): Server { + const sockets = [] const defaultHandler: MqttServerListener = (serverClient) => { + sockets.push(serverClient) serverClient.on('auth', (packet) => { if (serverClient.writable) return false const rc = 'reasonCode' @@ -111,6 +113,10 @@ export default function serverBuilder( serverClient.on('end', () => { debug('disconnected from server') + const index = sockets.findIndex((s) => s === serverClient) + if (index !== -1) { + sockets.splice(index, 1) + } }) } @@ -118,11 +124,13 @@ export default function serverBuilder( handler = defaultHandler } + let mqttServer = null + if (protocol === 'mqtt') { - return new MqttServer(handler) + mqttServer = new MqttServer(handler) } if (protocol === 'mqtts') { - return new MqttSecureServer( + mqttServer = new MqttSecureServer( { key: fs.readFileSync(KEY), cert: fs.readFileSync(CERT), @@ -153,6 +161,16 @@ export default function serverBuilder( // httpServer.connectionList = [] attachWebsocketServer(httpServer) httpServer.on('client', handler) - return httpServer + mqttServer = httpServer + } + + const originalClose = mqttServer.close + mqttServer.close = (cb) => { + sockets.forEach((socket) => { + socket.destroy() + }) + originalClose.call(mqttServer, cb) } + + return mqttServer } diff --git a/test/store.ts b/test/store.ts index 5e37ef01e..24c6cd56f 100644 --- a/test/store.ts +++ b/test/store.ts @@ -1,5 +1,6 @@ import Store from '../src/lib/store' import abstractTest from './abstract_store' +import { describe } from 'node:test' describe('in-memory store', () => { abstractTest(function test(done) { diff --git a/test/unique_message_id_provider_client.ts b/test/unique_message_id_provider_client.ts index 7a11467ad..eaedfb351 100644 --- a/test/unique_message_id_provider_client.ts +++ b/test/unique_message_id_provider_client.ts @@ -1,8 +1,11 @@ import abstractClientTests from './abstract_client' import serverBuilder from './server_helpers_for_client_tests' import UniqueMessageIdProvider from '../src/lib/unique-message-id-provider' -import ports from './helpers/port_list' +import getPorts from './helpers/port_list' import { IClientOptions } from 'src/mqtt' +import { describe, after } from 'node:test' + +const ports = getPorts(3) describe('UniqueMessageIdProviderMqttClient', () => { const server = serverBuilder('mqtt') @@ -18,7 +21,9 @@ describe('UniqueMessageIdProviderMqttClient', () => { if (server.listening) { server.close() } + + process.exit(0) }) - abstractClientTests(server, config) + abstractClientTests(server, config, ports) }) diff --git a/test/websocket_client.ts b/test/websocket_client.ts index a26028282..af37c5839 100644 --- a/test/websocket_client.ts +++ b/test/websocket_client.ts @@ -3,10 +3,13 @@ import WebSocket from 'ws' import MQTTConnection from 'mqtt-connection' import assert from 'assert' import abstractClientTests from './abstract_client' -import ports from './helpers/port_list' +import getPorts from './helpers/port_list' import { MqttServerNoWait } from './server' import * as mqtt from '../src/mqtt' import { IClientOptions } from '../src/lib/client' +import { after, describe, it } from 'node:test' + +const ports = getPorts(4) const port = 9999 const httpServer = http.createServer() @@ -97,7 +100,16 @@ describe('Websocket Client', () => { return { ...baseConfig, ...(custom || {}) } } - it('should use mqtt as the protocol by default', function test(done) { + after(() => { + // clean up and make sure the server is no longer listening... + if (httpServer.listening) { + httpServer.close() + } + + process.exit(0) + }) + + it('should use mqtt as the protocol by default', function _test(t, done) { httpServer.once('client', (client) => { assert.strictEqual(client.protocol, 'mqtt') }) @@ -108,7 +120,7 @@ describe('Websocket Client', () => { }) }) - it('should be able to transform the url (for e.g. to sign it)', function test(done) { + it('should be able to transform the url (for e.g. to sign it)', function _test(t, done) { const baseUrl = 'ws://localhost:9999/mqtt' const sig = '?AUTH=token' const expected = baseUrl + sig @@ -136,7 +148,7 @@ describe('Websocket Client', () => { }) }) - it('should be able to create custom Websocket instance', function test(done) { + it('should be able to create custom Websocket instance', function _test(t, done) { const baseUrl = 'ws://localhost:9999/mqtt' let urlInCallback: string const opts = makeOptions({ @@ -164,7 +176,7 @@ describe('Websocket Client', () => { }) }) - it('should use mqttv3.1 as the protocol if using v3.1', function test(done) { + it('should use mqttv3.1 as the protocol if using v3.1', function _test(t, done) { httpServer.once('client', (client) => { assert.strictEqual(client.protocol, 'mqttv3.1') }) @@ -182,67 +194,72 @@ describe('Websocket Client', () => { }) describe('reconnecting', () => { - it('should reconnect to multiple host-ports-protocol combinations if servers is passed', function test(done) { - let serverPort42Connected = false - const handler = (serverClient) => { - serverClient.on('connect', (packet) => { - serverClient.connack({ returnCode: 0 }) - }) - } - this.timeout(15000) - const actualURL41 = 'wss://localhost:9917/' - const actualURL42 = 'ws://localhost:9918/' - const serverPort41 = new MqttServerNoWait(handler).listen( - ports.PORTAND41, - ) - const serverPort42 = new MqttServerNoWait(handler).listen( - ports.PORTAND42, - ) - - serverPort42.on('listening', () => { - const client = mqtt.connect({ - protocol: 'wss', - servers: [ - { - port: ports.PORTAND42, - host: 'localhost', - protocol: 'ws', - }, - { port: ports.PORTAND41, host: 'localhost' }, - ], - keepalive: 50, - }) - serverPort41.once('client', (c) => { - assert.equal( - (client.stream as any).url, - actualURL41, - 'Protocol for second client should use the default protocol: wss, on port: port + 41.', - ) - assert(serverPort42Connected) - c.stream.destroy() - client.end(true, (err1) => { - serverPort41.close((err2) => { - done(err1 || err2) + it( + 'should reconnect to multiple host-ports-protocol combinations if servers is passed', + { + timeout: 15000, + }, + function _test(t, done) { + let serverPort42Connected = false + const handler = (serverClient) => { + serverClient.on('connect', (packet) => { + serverClient.connack({ returnCode: 0 }) + }) + } + const actualURL41 = `wss://localhost:${ports.PORTAND41}/` + const actualURL42 = `ws://localhost:${ports.PORTAND42}/` + const serverPort41 = new MqttServerNoWait(handler).listen( + ports.PORTAND41, + ) + const serverPort42 = new MqttServerNoWait(handler).listen( + ports.PORTAND42, + ) + + serverPort42.on('listening', () => { + const client = mqtt.connect({ + protocol: 'wss', + servers: [ + { + port: ports.PORTAND42, + host: 'localhost', + protocol: 'ws', + }, + { port: ports.PORTAND41, host: 'localhost' }, + ], + keepalive: 50, + }) + serverPort41.once('client', (c) => { + assert.equal( + (client.stream as any).url, + actualURL41, + 'Protocol for second client should use the default protocol: wss, on port: port + 41.', + ) + assert(serverPort42Connected) + c.stream.destroy() + client.end(true, (err1) => { + serverPort41.close((err2) => { + done(err1 || err2) + }) }) }) - }) - serverPort42.once('client', (c) => { - serverPort42Connected = true - assert.equal( - (client.stream as any).url, - actualURL42, - 'Protocol for connection should use ws, on port: port + 42.', - ) - c.stream.destroy() - serverPort42.close() - }) + serverPort42.once('client', (c) => { + serverPort42Connected = true + assert.equal( + (client.stream as any).url, + actualURL42, + 'Protocol for connection should use ws, on port: port + 42.', + ) + c.stream.destroy() + serverPort42.close() + }) - client.once('connect', () => { - client.stream.destroy() + client.once('connect', () => { + client.stream.destroy() + }) }) - }) - }) + }, + ) }) - abstractClientTests(httpServer, makeOptions()) + abstractClientTests(httpServer, makeOptions(), ports) }) diff --git a/tsconfig.json b/tsconfig.json index 827f3b0ed..5633fc730 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,6 @@ ], "types": [ "node", - "mocha" ], }, "include": [