Skip to content

Commit

Permalink
fix(NODE-6067): make topology descriptions JSON stringifiable (#4076)
Browse files Browse the repository at this point in the history
Co-authored-by: Aditi Khare <106987683+aditi-khare-mongoDB@users.noreply.github.com>
  • Loading branch information
baileympearson and aditi-khare-mongoDB committed Apr 24, 2024
1 parent 63ba4ac commit 7ae6eac
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 11 deletions.
14 changes: 13 additions & 1 deletion src/sdam/topology_description.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { ObjectId } from '../bson';
import { EJSON } from 'bson';

import { type ObjectId } from '../bson';
import * as WIRE_CONSTANTS from '../cmap/wire_protocol/constants';
import { MongoRuntimeError, type MongoServerError } from '../error';
import { compareObjectId, shuffle } from '../utils';
Expand Down Expand Up @@ -342,6 +344,16 @@ export class TopologyDescription {
hasServer(address: string): boolean {
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);
}
}

function topologyTypeForServerType(serverType: ServerType): TopologyType {
Expand Down
35 changes: 34 additions & 1 deletion test/integration/node-specific/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';

import { MongoClient, MongoError, MongoServerSelectionError } from '../../mongodb';
import { MongoClient, MongoError, MongoServerSelectionError, ReadPreference } from '../../mongodb';

describe('Error (Integration)', function () {
describe('AggregateErrors', function () {
Expand Down Expand Up @@ -51,4 +51,37 @@ 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`',
{ 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 servers = error.reason.servers;
expect(Object.keys(servers).length > 0).to.be.true;
} finally {
await client.close();
}
}
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand All @@ -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<TopologyTypeRequirement, TopologyType>([
['single', TopologyType.Single],
['replicaset', TopologyType.ReplicaSetWithPrimary],
Expand All @@ -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, '`servers` stringified with no servers.').to.be.true;
});
});
});

0 comments on commit 7ae6eac

Please sign in to comment.