From 3a3a0ed1104265404ee160d8b9f330eda9b994df Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 4 Apr 2024 12:03:08 -0600 Subject: [PATCH 1/4] add integration tests for JSON stringification of TDs --- test/integration/node-specific/errors.test.ts | 29 ++++++++++++- .../topology_description.test.ts | 43 +++++++++++++++---- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/test/integration/node-specific/errors.test.ts b/test/integration/node-specific/errors.test.ts index c0890083eb..60cfa12caa 100644 --- a/test/integration/node-specific/errors.test.ts +++ b/test/integration/node-specific/errors.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { MongoClient, MongoServerSelectionError } from '../../mongodb'; +import { MongoClient, MongoServerSelectionError, ReadPreference } from '../../mongodb'; describe('Error (Integration)', function () { it('NODE-5296: handles aggregate errors from dns lookup', async function () { @@ -10,4 +10,31 @@ describe('Error (Integration)', function () { expect(error).to.be.instanceOf(MongoServerSelectionError); expect(error.message).not.to.be.empty; }); + + context('when a server selection error is stringified', function () { + it('the error"s topology description correctly displays the `servers`', async function () { + const client: MongoClient = this.configuration.newClient({ serverSelectionTimeoutMS: 1000 }); + try { + await client.connect(); + + const error = await client + .db('foo') + .collection('bar') + .find( + {}, + { + // Use meaningless read preference tags to ensure that the server selection fails + readPreference: new ReadPreference('secondary', [{ ny: 'ny' }]) + } + ) + .toArray() + .catch(e => JSON.parse(JSON.stringify(e))); + + const servers = error.reason.servers; + expect(Object.keys(servers).length > 0).to.be.true; + } finally { + await client.close(); + } + }); + }); }); diff --git a/test/integration/server-discovery-and-monitoring/topology_description.test.ts b/test/integration/server-discovery-and-monitoring/topology_description.test.ts index 1169176ffb..1b1cfed547 100644 --- a/test/integration/server-discovery-and-monitoring/topology_description.test.ts +++ b/test/integration/server-discovery-and-monitoring/topology_description.test.ts @@ -14,7 +14,22 @@ describe('TopologyDescription (integration tests)', function () { await client.close(); }); + beforeEach(async function () { + client = this.configuration.newClient(); + await client.connect(); + }); + context('options', function () { + let client: MongoClient; + + afterEach(async function () { + await client.close(); + }); + + beforeEach(async function () { + client = this.configuration.newClient(); + }); + context('localThresholdMS', function () { it('should default to 15ms', async function () { const options: MongoClientOptions = {}; @@ -35,15 +50,6 @@ describe('TopologyDescription (integration tests)', function () { }); context('topology types', function () { - let client: MongoClient; - beforeEach(async function () { - client = this.configuration.newClient(); - }); - - afterEach(async function () { - await client.close(); - }); - const topologyTypesMap = new Map([ ['single', TopologyType.Single], ['replicaset', TopologyType.ReplicaSetWithPrimary], @@ -65,4 +71,23 @@ describe('TopologyDescription (integration tests)', function () { ); } }); + + describe('json stringification', function () { + it('can be stringified without error', function () { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain + const description = client.topology?.description!; + expect(description).to.exist; + + expect(() => JSON.stringify(description)).not.to.throw; + }); + + it('properly stringifies the server description map', function () { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain + const description = client.topology?.description!; + expect(description).to.exist; + + const { servers } = JSON.parse(JSON.stringify(description)); + expect(Object.keys(servers).length > 0).to.be.true; + }); + }); }); From 78c84afd7d358992093ca0f813114531f509a0b5 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 5 Apr 2024 09:33:37 -0600 Subject: [PATCH 2/4] add JSON serialization --- src/sdam/topology_description.ts | 6 +++++- .../topology_description.test.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sdam/topology_description.ts b/src/sdam/topology_description.ts index 5073429866..062fbadd73 100644 --- a/src/sdam/topology_description.ts +++ b/src/sdam/topology_description.ts @@ -1,4 +1,4 @@ -import type { ObjectId } from '../bson'; +import { EJSON, type ObjectId } from '../bson'; import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants'; import { type MongoError, MongoRuntimeError } from '../error'; import { compareObjectId, shuffle } from '../utils'; @@ -342,6 +342,10 @@ export class TopologyDescription { hasServer(address: string): boolean { return this.servers.has(address); } + + toJSON() { + return EJSON.serialize(this); + } } function topologyTypeForServerType(serverType: ServerType): TopologyType { diff --git a/test/integration/server-discovery-and-monitoring/topology_description.test.ts b/test/integration/server-discovery-and-monitoring/topology_description.test.ts index 1b1cfed547..291e13dd70 100644 --- a/test/integration/server-discovery-and-monitoring/topology_description.test.ts +++ b/test/integration/server-discovery-and-monitoring/topology_description.test.ts @@ -87,7 +87,7 @@ describe('TopologyDescription (integration tests)', function () { expect(description).to.exist; const { servers } = JSON.parse(JSON.stringify(description)); - expect(Object.keys(servers).length > 0).to.be.true; + expect(Object.keys(servers).length > 0, '`servers` stringified with no servers.').to.be.true; }); }); }); From 26d2130492a1b66f17f50a8a6909df37a7598e25 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 5 Apr 2024 10:10:37 -0600 Subject: [PATCH 3/4] restrict integration test to only run on replsets --- test/integration/node-specific/errors.test.ts | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/test/integration/node-specific/errors.test.ts b/test/integration/node-specific/errors.test.ts index 60cfa12caa..ff00db6564 100644 --- a/test/integration/node-specific/errors.test.ts +++ b/test/integration/node-specific/errors.test.ts @@ -12,29 +12,35 @@ describe('Error (Integration)', function () { }); context('when a server selection error is stringified', function () { - it('the error"s topology description correctly displays the `servers`', async function () { - const client: MongoClient = this.configuration.newClient({ serverSelectionTimeoutMS: 1000 }); - try { - await client.connect(); + it( + 'the error"s topology description correctly displays the `servers`', + { requires: { topology: 'replicaset' } }, + async function () { + const client: MongoClient = this.configuration.newClient({ + serverSelectionTimeoutMS: 1000 + }); + try { + await client.connect(); - const error = await client - .db('foo') - .collection('bar') - .find( - {}, - { - // Use meaningless read preference tags to ensure that the server selection fails - readPreference: new ReadPreference('secondary', [{ ny: 'ny' }]) - } - ) - .toArray() - .catch(e => JSON.parse(JSON.stringify(e))); + const error = await client + .db('foo') + .collection('bar') + .find( + {}, + { + // Use meaningless read preference tags to ensure that the server selection fails + readPreference: new ReadPreference('secondary', [{ ny: 'ny' }]) + } + ) + .toArray() + .catch(e => JSON.parse(JSON.stringify(e))); - const servers = error.reason.servers; - expect(Object.keys(servers).length > 0).to.be.true; - } finally { - await client.close(); + const servers = error.reason.servers; + expect(Object.keys(servers).length > 0).to.be.true; + } finally { + await client.close(); + } } - }); + ); }); }); From c129b245c1c98942574573d92cfb148fa8c5d094 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 8 Apr 2024 13:31:03 -0600 Subject: [PATCH 4/4] add doc comment --- src/sdam/topology_description.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sdam/topology_description.ts b/src/sdam/topology_description.ts index 062fbadd73..436321c7f1 100644 --- a/src/sdam/topology_description.ts +++ b/src/sdam/topology_description.ts @@ -343,6 +343,12 @@ export class TopologyDescription { return this.servers.has(address); } + /** + * Returns a JSON-serializable representation of the TopologyDescription. This is primarily + * intended for use with JSON.stringify(). + * + * This method will not throw. + */ toJSON() { return EJSON.serialize(this); }