diff --git a/CHANGELOG.md b/CHANGELOG.md index 4293fef6ff..df498dae70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `waitForRemotePeer` now accepts a `timeoutMs` parameter that rejects the promise if it is reached. By default, no timeout is applied. + ### Changed - `waitForRemotePeer` waits for a Relay peer by default instead of Relay and Store. diff --git a/src/lib/waku.node.spec.ts b/src/lib/waku.node.spec.ts index 96d80742bc..81f3b25d53 100644 --- a/src/lib/waku.node.spec.ts +++ b/src/lib/waku.node.spec.ts @@ -166,10 +166,13 @@ describe("Decryption Keys", () => { describe("Wait for remote peer / get peers", function () { let waku: Waku; - let nwaku: Nwaku; + let nwaku: Nwaku | undefined; afterEach(async function () { - !!nwaku && nwaku.stop(); + if (nwaku) { + nwaku.stop(); + nwaku = undefined; + } !!waku && waku.stop().catch((e) => console.log("Waku failed to stop", e)); }); @@ -214,6 +217,23 @@ describe("Wait for remote peer / get peers", function () { expect(peers.has(nimPeerId as string)).to.be.true; }); + it("Relay - times out", function (done) { + this.timeout(5000); + Waku.create({ + staticNoiseKey: NOISE_KEY_1, + }).then((waku) => { + waku.waitForRemotePeer([Protocols.Relay], 200).then( + () => { + throw "Promise expected to reject on time out"; + }, + (reason) => { + expect(reason).to.eq("Timed out waiting for a remote peer."); + done(); + } + ); + }); + }); + it("Store - dialed first", async function () { this.timeout(20_000); nwaku = new Nwaku(makeLogFileName(this)); @@ -238,7 +258,7 @@ describe("Wait for remote peer / get peers", function () { expect(peers.includes(nimPeerId as string)).to.be.true; }); - it("Store - dialed after", async function () { + it("Store - dialed after - with timeout", async function () { this.timeout(20_000); nwaku = new Nwaku(makeLogFileName(this)); await nwaku.start({ persistMessages: true }); @@ -247,7 +267,7 @@ describe("Wait for remote peer / get peers", function () { waku = await Waku.create({ staticNoiseKey: NOISE_KEY_1, }); - const waitPromise = waku.waitForRemotePeer([Protocols.Store]); + const waitPromise = waku.waitForRemotePeer([Protocols.Store], 2000); await delay(1000); await waku.dial(multiAddrWithId); await waitPromise; diff --git a/src/lib/waku.ts b/src/lib/waku.ts index fb20d16249..bd82fe3e76 100644 --- a/src/lib/waku.ts +++ b/src/lib/waku.ts @@ -329,12 +329,21 @@ export class Waku { * Wait for a remote peer to be ready given the passed protocols. * Useful when using the [[CreateOptions.bootstrap]] with [[Waku.create]]. * - * @default Remote peer must have Waku Relay enabled. + * @param protocols The protocols that need to be enabled by remote peers. + * @param timeoutMs A timeout value in milliseconds.. + * + * @returns A promise that **resolves** if all desired protocols are fulfilled by + * remote nodes, **rejects** if the timeoutMs is reached. + * + * @default Remote peer must have Waku Relay enabled and no time out is applied. */ - async waitForRemotePeer(protocols?: Protocols[]): Promise { + async waitForRemotePeer( + protocols?: Protocols[], + timeoutMs?: number + ): Promise { protocols = protocols ?? [Protocols.Relay]; - const promises = []; + const promises: Promise[] = []; if (protocols.includes(Protocols.Relay)) { const peers = this.relay.getPeers(); @@ -372,7 +381,15 @@ export class Waku { promises.push(lightPushPromise); } - await Promise.all(promises); + if (timeoutMs) { + await rejectOnTimeout( + Promise.all(promises), + timeoutMs, + "Timed out waiting for a remote peer." + ); + } else { + await Promise.all(promises); + } } private startKeepAlive( @@ -417,3 +434,13 @@ export class Waku { } } } + +const awaitTimeout = (ms: number, rejectReason: string): Promise => + new Promise((_resolve, reject) => setTimeout(() => reject(rejectReason), ms)); + +const rejectOnTimeout = ( + promise: Promise, + timeoutMs: number, + rejectReason: string +): Promise => + Promise.race([promise, awaitTimeout(timeoutMs, rejectReason)]);