Skip to content

Commit ac49df6

Browse files
authored
fix(NODE-2035): exceptions thrown from awaited cursor forEach do not propagate (#2835)
1 parent b3c3721 commit ac49df6

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

src/cursor/abstract_cursor.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,21 +326,27 @@ export abstract class AbstractCursor<
326326
const fetchDocs = () => {
327327
next<T>(this, true, (err, doc) => {
328328
if (err || doc == null) return done(err);
329-
if (doc == null) return done();
330-
329+
let result;
331330
// NOTE: no need to transform because `next` will do this automatically
332-
let result = iterator(doc); // TODO(NODE-3283): Improve transform typing
331+
try {
332+
result = iterator(doc); // TODO(NODE-3283): Improve transform typing
333+
} catch (error) {
334+
return done(error);
335+
}
336+
333337
if (result === false) return done();
334338

335339
// these do need to be transformed since they are copying the rest of the batch
336340
const internalDocs = this[kDocuments].splice(0, this[kDocuments].length);
337-
if (internalDocs) {
338-
for (let i = 0; i < internalDocs.length; ++i) {
341+
for (let i = 0; i < internalDocs.length; ++i) {
342+
try {
339343
result = iterator(
340344
(transform ? transform(internalDocs[i]) : internalDocs[i]) as T // TODO(NODE-3283): Improve transform typing
341345
);
342-
if (result === false) return done();
346+
} catch (error) {
347+
return done(error);
343348
}
349+
if (result === false) return done();
344350
}
345351

346352
fetchDocs();

test/functional/cursor.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,50 @@ describe('Cursor', function () {
39013901
});
39023902
});
39033903

3904+
describe('Cursor forEach Error propagation', function () {
3905+
let configuration;
3906+
let client;
3907+
let cursor;
3908+
let collection;
3909+
3910+
beforeEach(async function () {
3911+
configuration = this.configuration;
3912+
client = configuration.newClient({ w: 1 }, { maxPoolSize: 1 });
3913+
await client.connect().catch(() => {
3914+
expect.fail('Failed to connect to client');
3915+
});
3916+
collection = client.db(configuration.db).collection('cursor_session_tests2');
3917+
});
3918+
3919+
afterEach(async function () {
3920+
await cursor.close();
3921+
await client.close();
3922+
});
3923+
3924+
// NODE-2035
3925+
it('should propagate error when exceptions are thrown from an awaited forEach call', async function () {
3926+
const docs = [{ unique_key_2035: 1 }, { unique_key_2035: 2 }, { unique_key_2035: 3 }];
3927+
await collection.insertMany(docs).catch(() => {
3928+
expect.fail('Failed to insert documents');
3929+
});
3930+
cursor = collection.find({
3931+
unique_key_2035: {
3932+
$exists: true
3933+
}
3934+
});
3935+
await cursor
3936+
.forEach(() => {
3937+
throw new Error('FAILURE IN FOREACH CALL');
3938+
})
3939+
.then(() => {
3940+
expect.fail('Error in forEach call not caught');
3941+
})
3942+
.catch(err => {
3943+
expect(err.message).to.deep.equal('FAILURE IN FOREACH CALL');
3944+
});
3945+
});
3946+
});
3947+
39043948
it('should return a promise when no callback supplied to forEach method', function () {
39053949
const configuration = this.configuration;
39063950
const client = configuration.newClient({ w: 1 }, { maxPoolSize: 1 });

0 commit comments

Comments
 (0)