Skip to content

Commit 8a129bd

Browse files
committed
fix(NODE-6638): throw if all atomic updates are undefined
1 parent 3139a92 commit 8a129bd

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

src/utils.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,10 @@ export function calculateDurationInMs(started: number | undefined): number {
476476
}
477477

478478
/** @internal */
479-
export function hasAtomicOperators(doc: Document | Document[]): boolean {
479+
export function hasAtomicOperators(
480+
doc: Document | Document[],
481+
options?: CommandOperationOptions
482+
): boolean {
480483
if (Array.isArray(doc)) {
481484
for (const document of doc) {
482485
if (hasAtomicOperators(document)) {
@@ -487,6 +490,21 @@ export function hasAtomicOperators(doc: Document | Document[]): boolean {
487490
}
488491

489492
const keys = Object.keys(doc);
493+
// In this case we need to throw if all the atomic operators are undefined.
494+
if (options?.ignoreUndefined) {
495+
let allUndefined = true;
496+
for (const key of keys) {
497+
// eslint-disable-next-line no-restricted-syntax
498+
if (doc[key] !== undefined) {
499+
allUndefined = false;
500+
break;
501+
}
502+
}
503+
if (allUndefined) {
504+
throw new MongoInvalidArgumentError('All atomic operators provided have undefined values.');
505+
}
506+
}
507+
490508
return keys.length > 0 && keys[0][0] === '$';
491509
}
492510

test/unit/utils.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
compareObjectId,
1212
decorateWithExplain,
1313
Explain,
14+
hasAtomicOperators,
1415
HostAddress,
1516
hostMatchesWildcards,
1617
isHello,
@@ -19,13 +20,74 @@ import {
1920
List,
2021
MongoDBCollectionNamespace,
2122
MongoDBNamespace,
23+
MongoInvalidArgumentError,
2224
MongoRuntimeError,
2325
ObjectId,
2426
shuffle
2527
} from '../mongodb';
2628
import { sleep } from '../tools/utils';
2729

2830
describe('driver utils', function () {
31+
describe.only('.hasAtomicOperators', function () {
32+
context('when ignoreUndefined is true', function () {
33+
const options = { ignoreUndefined: true };
34+
35+
context('when no operator is undefined', function () {
36+
const document = { $set: { n: 1 }, $unset: '' };
37+
38+
it('returns true', function () {
39+
expect(hasAtomicOperators(document, options)).to.be.true;
40+
});
41+
});
42+
43+
context('when some operators are undefined', function () {
44+
const document = { $set: { n: 1 }, $unset: undefined };
45+
46+
it('returns true', function () {
47+
expect(hasAtomicOperators(document, options)).to.be.true;
48+
});
49+
});
50+
51+
context('when all operators are undefined', function () {
52+
const document = { $set: undefined, $unset: undefined };
53+
54+
it('throws an error', function () {
55+
expect(() => {
56+
hasAtomicOperators(document, options);
57+
}).to.throw(MongoInvalidArgumentError);
58+
});
59+
});
60+
});
61+
62+
context('when ignoreUndefined is false', function () {
63+
const options = { ignoreUndefined: false };
64+
65+
context('when no operator is undefined', function () {
66+
const document = { $set: { n: 1 }, $unset: '' };
67+
68+
it('returns true', function () {
69+
expect(hasAtomicOperators(document, options)).to.be.true;
70+
});
71+
});
72+
73+
context('when some operators are undefined', function () {
74+
const document = { $set: { n: 1 }, $unset: undefined };
75+
76+
it('returns true', function () {
77+
expect(hasAtomicOperators(document, options)).to.be.true;
78+
});
79+
});
80+
81+
context('when all operators are undefined', function () {
82+
const document = { $set: undefined, $unset: undefined };
83+
84+
it('returns true', function () {
85+
expect(hasAtomicOperators(document, options)).to.be.true;
86+
});
87+
});
88+
});
89+
});
90+
2991
describe('.hostMatchesWildcards', function () {
3092
context('when using domains', function () {
3193
context('when using exact match', function () {

0 commit comments

Comments
 (0)