diff --git a/package-lock.json b/package-lock.json index 248227722f..4254509997 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5925,6 +5925,45 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" + }, "node_modules/@sitespeed.io/tracium": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sitespeed.io/tracium/-/tracium-0.3.3.tgz", @@ -11091,7 +11130,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, "engines": { "node": ">=0.3.1" } @@ -14202,7 +14240,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -16083,6 +16120,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, "node_modules/karma": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", @@ -17238,6 +17280,11 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -19405,6 +19452,26 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -23937,6 +24004,19 @@ "node": "14 || >=16.14" } }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -27857,6 +27937,34 @@ "node": ">=4" } }, + "node_modules/sinon": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sirv": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", @@ -31455,6 +31563,7 @@ "dockerode": "^3.3.5", "p-timeout": "^6.1.0", "portfinder": "^1.0.32", + "sinon": "^15.2.0", "tail": "^2.2.6" }, "devDependencies": { @@ -35857,6 +35966,47 @@ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "requires": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "requires": { + "type-detect": "4.0.8" + } + } + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" + }, "@sitespeed.io/tracium": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sitespeed.io/tracium/-/tracium-0.3.3.tgz", @@ -37281,6 +37431,7 @@ "p-timeout": "^6.1.0", "portfinder": "^1.0.32", "prettier": "^2.8.8", + "sinon": "*", "tail": "^2.2.6", "typescript": "^5.0.4" } @@ -40160,8 +40311,7 @@ "diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" }, "dir-glob": { "version": "3.0.1", @@ -42456,8 +42606,7 @@ "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 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-own-prop": { "version": "2.0.0", @@ -43780,6 +43929,11 @@ "integrity": "sha512-ojtSU++zLJ3jQG9bAYjg94w+/DOJtRyD7nPaerMFrBhmdVmiV5/exYH5t4uHga4G/95nT6hr1OJoKIFbYbrW5w==", "dev": true }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, "karma": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", @@ -44692,6 +44846,11 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -46213,6 +46372,28 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "requires": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "requires": { + "type-detect": "4.0.8" + } + } + } + }, "node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -49392,6 +49573,21 @@ } } }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -52140,6 +52336,29 @@ } } }, + "sinon": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "sirv": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", diff --git a/packages/core/src/lib/connection_manager.ts b/packages/core/src/lib/connection_manager.ts index 4a43c8081c..e44c6ab8b1 100644 --- a/packages/core/src/lib/connection_manager.ts +++ b/packages/core/src/lib/connection_manager.ts @@ -191,8 +191,8 @@ export class ConnectionManager { } private startPeerDiscoveryListener(): void { - this.libp2pComponents.peerStore.addEventListener( - "peer", + this.libp2pComponents.addEventListener( + "peer:discovery", this.onEventHandlers["peer:discovery"] ); } @@ -286,15 +286,15 @@ export class ConnectionManager { if (isConnected) return false; - const isBootstrap = (await this.getTagNamesForPeer(peerId)).some( - (tagName) => tagName === Tags.BOOTSTRAP - ); + const tagNames = await this.getTagNamesForPeer(peerId); + + const isBootstrap = tagNames.some((tagName) => tagName === Tags.BOOTSTRAP); if (isBootstrap) { const currentBootstrapConnections = this.libp2pComponents .getConnections() .filter((conn) => { - conn.tags.find((name) => name === Tags.BOOTSTRAP); + return conn.tags.find((name) => name === Tags.BOOTSTRAP); }).length; if (currentBootstrapConnections < this.options.maxBootstrapPeersAllowed) return true; diff --git a/packages/tests/package.json b/packages/tests/package.json index 31f9880c4d..09b0707f21 100644 --- a/packages/tests/package.json +++ b/packages/tests/package.json @@ -59,6 +59,7 @@ "dockerode": "^3.3.5", "p-timeout": "^6.1.0", "portfinder": "^1.0.32", + "sinon": "^15.2.0", "tail": "^2.2.6" }, "devDependencies": { @@ -71,9 +72,12 @@ "@types/mocha": "^10.0.1", "@types/tail": "^2.2.1", "@waku/sdk": "*", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.59.8", "@waku/dns-discovery": "*", "@waku/message-encryption": "*", "@waku/peer-exchange": "*", + "@waku/sdk": "*", "chai": "^4.3.7", "cspell": "^6.31.1", "debug": "^4.3.4", diff --git a/packages/tests/tests/connection_manager.spec.ts b/packages/tests/tests/connection_manager.spec.ts new file mode 100644 index 0000000000..aebc3b3a29 --- /dev/null +++ b/packages/tests/tests/connection_manager.spec.ts @@ -0,0 +1,196 @@ +import { CustomEvent } from "@libp2p/interfaces/events"; +import { ConnectionManager, KeepAliveOptions } from "@waku/core"; +import { LightNode, Tags } from "@waku/interfaces"; +import { createLightNode } from "@waku/sdk"; +import { expect } from "chai"; +import sinon, { SinonSpy, SinonStub } from "sinon"; + +import { delay } from "../dist/delay.js"; + +const KEEP_ALIVE_OPTIONS: KeepAliveOptions = { + pingKeepAlive: 0, + relayKeepAlive: 5 * 1000, +}; +const TEST_TIMEOUT = 10_000; +const DELAY_MS = 1_000; + +describe("ConnectionManager", function () { + let connectionManager: ConnectionManager | undefined; + let waku: LightNode; + let peerId: string; + let getConnectionsStub: SinonStub; + let getTagNamesForPeerStub: SinonStub; + let dialPeerStub: SinonStub; + + beforeEach(async function () { + waku = await createLightNode(); + peerId = Math.random().toString(36).substring(7); + connectionManager = ConnectionManager.create( + peerId, + waku.libp2p, + KEEP_ALIVE_OPTIONS + ); + }); + + afterEach(async () => { + await waku.stop(); + sinon.restore(); + }); + + describe("attemptDial method", function () { + let attemptDialSpy: SinonSpy; + + beforeEach(function () { + attemptDialSpy = sinon.spy(connectionManager as any, "attemptDial"); + }); + + afterEach(function () { + attemptDialSpy.restore(); + }); + + it("should be called on all `peer:discovery` events", async function () { + this.timeout(TEST_TIMEOUT); + + const totalPeerIds = 1; + for (let i = 1; i <= totalPeerIds; i++) { + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { detail: `peer-id-${i}` }) + ); + } + + expect(attemptDialSpy.callCount).to.equal( + totalPeerIds, + "attemptDial should be called once for each peer:discovery event" + ); + }); + }); + + describe("dialPeer method", function () { + beforeEach(function () { + getConnectionsStub = sinon.stub( + (connectionManager as any).libp2pComponents, + "getConnections" + ); + getTagNamesForPeerStub = sinon.stub( + connectionManager as any, + "getTagNamesForPeer" + ); + dialPeerStub = sinon.stub(connectionManager as any, "dialPeer"); + }); + + afterEach(function () { + dialPeerStub.restore(); + getTagNamesForPeerStub.restore(); + getConnectionsStub.restore(); + }); + + describe("For bootstrap peers", function () { + it("should be called for bootstrap peers", async function () { + this.timeout(TEST_TIMEOUT); + + // simulate that the peer is not connected + getConnectionsStub.returns([]); + + // simulate that the peer is a bootstrap peer + getTagNamesForPeerStub.resolves([Tags.BOOTSTRAP]); + + // emit a peer:discovery event + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { detail: "bootstrap-peer" }) + ); + + // wait for the async function calls within attemptDial to finish + await delay(DELAY_MS); + + // check that dialPeer was called once + expect(dialPeerStub.callCount).to.equal( + 1, + "dialPeer should be called for bootstrap peers" + ); + }); + + it("should not be called more than DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED times for bootstrap peers", async function () { + this.timeout(TEST_TIMEOUT); + + // simulate that the peer is not connected + getConnectionsStub.returns([]); + + // simulate that the peer is a bootstrap peer + getTagNamesForPeerStub.resolves([Tags.BOOTSTRAP]); + + // emit first peer:discovery event + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { detail: "bootstrap-peer" }) + ); + + // simulate that the peer is connected + getConnectionsStub.returns([{ tags: [{ name: Tags.BOOTSTRAP }] }]); + + // emit multiple peer:discovery events + const totalBootstrapPeers = 5; + for (let i = 1; i <= totalBootstrapPeers; i++) { + await delay(500); + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { + detail: `bootstrap-peer-id-${i}`, + }) + ); + } + + // check that dialPeer was called only once + expect(dialPeerStub.callCount).to.equal( + 1, + "dialPeer should not be called more than once for bootstrap peers" + ); + }); + }); + + describe("For peer-exchange peers", function () { + it("should be called for peers with PEER_EXCHANGE tags", async function () { + this.timeout(TEST_TIMEOUT); + + // simulate that the peer is not connected + getConnectionsStub.returns([]); + + // simulate that the peer has a PEER_EXCHANGE tag + getTagNamesForPeerStub.resolves([Tags.PEER_EXCHANGE]); + + // emit a peer:discovery event + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { detail: "px-peer" }) + ); + + // wait for the async function calls within attemptDial to finish + await delay(DELAY_MS); + + // check that dialPeer was called once + expect(dialPeerStub.callCount).to.equal( + 1, + "dialPeer should be called for peers with PEER_EXCHANGE tags" + ); + }); + + it("should be called for every peer with PEER_EXCHANGE tags", async function () { + this.timeout(TEST_TIMEOUT); + + // simulate that the peer is not connected + getConnectionsStub.returns([]); + + // simulate that the peer has a PEER_EXCHANGE tag + getTagNamesForPeerStub.resolves([Tags.PEER_EXCHANGE]); + + // emit multiple peer:discovery events + const totalPxPeers = 5; + for (let i = 0; i < totalPxPeers; i++) { + waku.libp2p.dispatchEvent( + new CustomEvent("peer:discovery", { detail: `px-peer-id-${i}` }) + ); + await delay(500); + } + + // check that dialPeer was called for each peer with PEER_EXCHANGE tags + expect(dialPeerStub.callCount).to.equal(totalPxPeers); + }); + }); + }); +});