Skip to content

Commit

Permalink
fix(NODE-5311): construct error messages for AggregateErrors in Node1…
Browse files Browse the repository at this point in the history
…6+ (#3683)
  • Loading branch information
baileympearson authored and durran committed May 26, 2023
1 parent c0c3d99 commit 98b7bdf
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 3 deletions.
1 change: 1 addition & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare global {
clientSideEncryption?: boolean;
serverless?: 'forbid' | 'allow' | 'require';
auth?: 'enabled' | 'disabled';
nodejs?: string;
};

sessions?: {
Expand Down
23 changes: 20 additions & 3 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ export interface ErrorDescription extends Document {
errInfo?: Document;
}

function isAggregateError(e: Error): e is Error & { errors: Error[] } {
return 'errors' in e && Array.isArray(e.errors);
}

/**
* @public
* @category Error
Expand All @@ -130,15 +134,28 @@ export class MongoError extends Error {
cause?: Error; // depending on the node version, this may or may not exist on the base

constructor(message: string | Error) {
super(MongoError.buildErrorMessage(message));
if (message instanceof Error) {
super(message.message);
this.cause = message;
} else {
super(message);
}

this[kErrorLabels] = new Set();
}

/** @internal */
private static buildErrorMessage(e: Error | string): string {
if (typeof e === 'string') {
return e;
}
if (isAggregateError(e) && e.message.length === 0) {
return e.errors.length === 0
? 'AggregateError has an empty errors array. Please check the `cause` property for more information.'
: e.errors.map(({ message }) => message).join(', ');
}

return e.message;
}

override get name(): string {
return 'MongoError';
}
Expand Down
54 changes: 54 additions & 0 deletions test/integration/node-specific/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { expect } from 'chai';

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

describe('Error (Integration)', function () {
describe('AggregateErrors', function () {
for (const { errors, message } of [
{
errors: [],
message:
'AggregateError has an empty errors array. Please check the `cause` property for more information.'
},
{ errors: [new Error('message 1')], message: 'message 1' },
{
errors: [new Error('message 1'), new Error('message 2')],
message: 'message 1, message 2'
}
]) {
it(
`constructs the message properly with an array of ${errors.length} errors`,
{ requires: { nodejs: '>=16' } },
() => {
const error = new AggregateError(errors);
const mongoError = new MongoError(error);

expect(mongoError.message).to.equal(message);
}
);
}

context('when the message on the AggregateError is non-empty', () => {
it(`uses the AggregateError's message`, { requires: { nodejs: '>=16' } }, () => {
const error = new AggregateError([new Error('non-empty')]);
error.message = 'custom error message';
const mongoError = new MongoError(error);
expect(mongoError.message).to.equal('custom error message');
});
});

it('sets the AggregateError to the cause property', { requires: { nodejs: '>=16' } }, () => {
const error = new AggregateError([new Error('error 1')]);
const mongoError = new MongoError(error);
expect(mongoError.cause).to.equal(error);
});
});

it('NODE-5296: handles aggregate errors from dns lookup', async function () {
const error = await MongoClient.connect('mongodb://localhost:27222', {
serverSelectionTimeoutMS: 1000
}).catch(e => e);
expect(error).to.be.instanceOf(MongoServerSelectionError);
expect(error.message).not.to.be.empty;
});
});
25 changes: 25 additions & 0 deletions test/tools/runner/filters/node_version_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const { satisfies } = require('semver');

/**
* Filter for specific nodejs versions
*
* example:
* metadata: {
* requires: {
* nodejs: '>=14'
* }
* }
*/
class NodeVersionFilter {
filter(test) {
if (!test.metadata) return true;
if (!test.metadata.requires) return true;
if (!test.metadata.requires.nodejs) return true;

return satisfies(process.version, test.metadata.requires.nodejs);
}
}

module.exports = NodeVersionFilter;

0 comments on commit 98b7bdf

Please sign in to comment.