Skip to content

Commit ebd4b44

Browse files
ready for review
lint fix lont fix 2
1 parent 687e66c commit ebd4b44

File tree

4 files changed

+152
-15
lines changed

4 files changed

+152
-15
lines changed

src/cmap/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ export class OpMsgRequest {
465465

466466
// flags
467467
this.checksumPresent = false;
468-
this.moreToCome = options.moreToCome || options.writeConcern?.w === 0 || false;
468+
this.moreToCome = options.moreToCome || command.writeConcern?.w === 0 || false;
469469
this.exhaustAllowed =
470470
typeof options.exhaustAllowed === 'boolean' ? options.exhaustAllowed : false;
471471
}

src/cmap/connection.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
438438
zlibCompressionLevel: this.description.zlibCompressionLevel
439439
});
440440

441-
if (options.noResponse || ('moreToCome' in message && message?.moreToCome)) {
441+
if (options.noResponse) {
442442
yield MongoDBResponse.empty;
443443
return;
444444
}
@@ -500,9 +500,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
500500
let document: MongoDBResponse | undefined = undefined;
501501
/** Cached result of a toObject call */
502502
let object: Document | undefined = undefined;
503+
const wireOptions = options;
504+
if (message instanceof OpMsgRequest) {
505+
wireOptions.noResponse = options.noResponse || message.moreToCome;
506+
}
503507
try {
504508
this.throwIfAborted();
505-
for await (document of this.sendWire(message, options, responseType)) {
509+
for await (document of this.sendWire(message, wireOptions, responseType)) {
506510
object = undefined;
507511
if (options.session != null) {
508512
updateSessionFromResponse(options.session, document);
@@ -526,7 +530,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
526530
new CommandSucceededEvent(
527531
this,
528532
message,
529-
options.noResponse ? undefined : (object ??= document.toObject(bsonOptions)),
533+
wireOptions.noResponse ? { ok: 1 } : (object ??= document.toObject(bsonOptions)),
530534
started,
531535
this.description.serverConnectionId
532536
)
@@ -629,8 +633,8 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
629633
});
630634

631635
const buffer = Buffer.concat(await finalCommand.toBin());
632-
633636
if (this.socket.write(buffer)) return;
637+
634638
return await once(this.socket, 'drain');
635639
}
636640

test/integration/read-write-concern/write_concern.test.ts

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import { expect } from 'chai';
22
import { on, once } from 'events';
3+
import { gte } from 'semver';
4+
import * as sinon from 'sinon';
35

46
import {
57
type Collection,
68
type CommandStartedEvent,
9+
type CommandSucceededEvent,
710
type Db,
811
LEGACY_HELLO_COMMAND,
9-
MongoClient
12+
MongoClient,
13+
OpMsgRequest
1014
} from '../../mongodb';
1115
import * as mock from '../../tools/mongodb-mock/index';
1216
import { filterForCommands } from '../shared';
1317

14-
describe.only('Write Concern', function () {
18+
describe('Write Concern', function () {
1519
context('when the WriteConcern is set in the uri', function () {
1620
let client;
1721
const events: CommandStartedEvent[] = [];
@@ -29,8 +33,7 @@ describe.only('Write Concern', function () {
2933
expect(events[0]).to.containSubset({
3034
commandName: 'insert',
3135
command: {
32-
writeConcern: { w: 0 },
33-
moreToCome: true
36+
writeConcern: { w: 0 }
3437
}
3538
});
3639
});
@@ -169,4 +172,136 @@ describe.only('Write Concern', function () {
169172
});
170173
});
171174
});
175+
176+
describe('fire-and-forget protocol', function () {
177+
context('when writeConcern = 0 and OP_MSG is used', function () {
178+
const writeOperations: { name: string; command: any; expectedReturnVal: any }[] = [
179+
{
180+
name: 'insertOne',
181+
command: client => client.db('test').collection('test').insertOne({ a: 1 }),
182+
expectedReturnVal: { acknowledged: false }
183+
},
184+
{
185+
name: 'insertMany',
186+
command: client =>
187+
client
188+
.db('test')
189+
.collection('test')
190+
.insertMany([{ a: 1 }, { b: 2 }]),
191+
expectedReturnVal: { acknowledged: false }
192+
},
193+
{
194+
name: 'updateOne',
195+
command: client =>
196+
client
197+
.db('test')
198+
.collection('test')
199+
.updateOne({ i: 128 }, { $set: { c: 2 } }),
200+
expectedReturnVal: { acknowledged: false }
201+
},
202+
{
203+
name: 'updateMany',
204+
command: client =>
205+
client
206+
.db('test')
207+
.collection('test')
208+
.updateMany({ name: 'foobar' }, { $set: { name: 'fizzbuzz' } }),
209+
expectedReturnVal: { acknowledged: false }
210+
},
211+
{
212+
name: 'deleteOne',
213+
command: client => client.db('test').collection('test').deleteOne({ a: 1 }),
214+
expectedReturnVal: { acknowledged: false }
215+
},
216+
{
217+
name: 'deleteMany',
218+
command: client => client.db('test').collection('test').deleteMany({ name: 'foobar' }),
219+
expectedReturnVal: { acknowledged: false }
220+
},
221+
{
222+
name: 'replaceOne',
223+
command: client => client.db('test').collection('test').replaceOne({ a: 1 }, { b: 2 }),
224+
expectedReturnVal: { acknowledged: false }
225+
},
226+
{
227+
name: 'removeUser',
228+
command: client => client.db('test').removeUser('albert'),
229+
expectedReturnVal: true
230+
},
231+
{
232+
name: 'findAndModify',
233+
command: client =>
234+
client
235+
.db('test')
236+
.collection('test')
237+
.findOneAndUpdate({}, { $setOnInsert: { a: 1 } }, { upsert: true }),
238+
expectedReturnVal: null
239+
},
240+
{
241+
name: 'dropDatabase',
242+
command: client => client.db('test').dropDatabase(),
243+
expectedReturnVal: true
244+
},
245+
{
246+
name: 'dropCollection',
247+
command: client => client.db('test').dropCollection('test'),
248+
expectedReturnVal: true
249+
},
250+
{
251+
name: 'dropIndexes',
252+
command: client => client.db('test').collection('test').dropIndex('a'),
253+
expectedReturnVal: { ok: 1 }
254+
},
255+
{
256+
name: 'createIndexes',
257+
command: client => client.db('test').collection('test').createIndex({ a: 1 }),
258+
expectedReturnVal: 'a_1'
259+
},
260+
{
261+
name: 'createCollection',
262+
command: client => client.db('test').createCollection('test'),
263+
expectedReturnVal: {}
264+
}
265+
];
266+
267+
for (const op of writeOperations) {
268+
context(`when the write operation ${op.name} is run`, function () {
269+
let client;
270+
let spy;
271+
272+
beforeEach(async function () {
273+
if (gte('3.6.0', this.configuration.version)) {
274+
this.currentTest.skipReason = 'Test requires OP_MSG, needs to be on MongoDB 3.6+';
275+
this.skip();
276+
}
277+
spy = sinon.spy(OpMsgRequest.prototype, 'toBin');
278+
client = this.configuration.newClient({ monitorCommands: true, w: 0 });
279+
await client.connect();
280+
});
281+
282+
afterEach(function () {
283+
sinon.restore();
284+
client.close();
285+
});
286+
287+
it('the request should have moreToCome bit set', async function () {
288+
await op.command(client);
289+
expect(spy.returnValues[spy.returnValues.length - 1][0][16]).to.equal(2);
290+
});
291+
292+
it('the return value of the command should be nullish', async function () {
293+
const result = await op.command(client);
294+
expect(result).to.containSubset(op.expectedReturnVal);
295+
});
296+
297+
it('commandSucceededEvent should have reply with only {ok: 1}', async function () {
298+
const events: CommandSucceededEvent[] = [];
299+
client.on('commandSucceeded', event => events.push(event));
300+
await op.command(client);
301+
expect(events[0]).to.containSubset({ reply: { ok: 1 } });
302+
});
303+
});
304+
}
305+
});
306+
});
172307
});

test/unit/commands.test.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import * as compression from '../../src/cmap/wire_protocol/compression';
66
import {
77
compress,
88
Compressor,
9-
Connection,
10-
MongoClient,
119
OP_MSG,
1210
OP_QUERY,
1311
OpCompressedRequest,
@@ -112,11 +110,11 @@ describe('class OpCompressedRequest', () => {
112110
});
113111
});
114112

115-
describe.only('fire-and-forget', () => {
116-
describe('OpMsgRequest', () => {
117-
context('when writeConcern.w is 0', () => {
113+
describe('OpMsgRequest', () => {
114+
describe('fire-and-forget', () => {
115+
context('when writeConcern = 0', () => {
118116
it('moreToCome is set to true', async () => {
119-
const request = new OpMsgRequest('db', { a: 1 }, { writeConcern: { w: 0 } });
117+
const request = new OpMsgRequest('db', { a: 1, writeConcern: { w: 0 } }, {});
120118
expect(request.moreToCome).to.be.true;
121119
});
122120
});

0 commit comments

Comments
 (0)