diff --git a/src/cmap/wire_protocol/responses.ts b/src/cmap/wire_protocol/responses.ts index 1d20566e2d5..bacf6d648be 100644 --- a/src/cmap/wire_protocol/responses.ts +++ b/src/cmap/wire_protocol/responses.ts @@ -8,6 +8,7 @@ import { parseToElementsToArray, parseUtf8ValidationOption, pluckBSONSerializeOptions, + serialize, type Timestamp } from '../../bson'; import { MONGODB_ERROR_CODES, MongoUnexpectedServerResponseError } from '../../error'; @@ -230,11 +231,9 @@ export class CursorResponse extends MongoDBResponse { * This supports a feature of the FindCursor. * It is an optimization to avoid an extra getMore when the limit has been reached */ - static emptyGetMore: CursorResponse = { - id: new Long(0), - length: 0, - shift: () => null - } as unknown as CursorResponse; + static get emptyGetMore(): CursorResponse { + return new CursorResponse(serialize({ ok: 1, cursor: { id: 0n, nextBatch: [] } })); + } static override is(value: unknown): value is CursorResponse { return value instanceof CursorResponse || value === CursorResponse.emptyGetMore; diff --git a/test/integration/crud/find.test.js b/test/integration/crud/find.test.js index ed098d31c0a..c6d5b2fdf6e 100644 --- a/test/integration/crud/find.test.js +++ b/test/integration/crud/find.test.js @@ -3,7 +3,7 @@ const { assert: test } = require('../shared'); const { expect } = require('chai'); const sinon = require('sinon'); const { setTimeout } = require('timers'); -const { Code, ObjectId, Long, Binary, ReturnDocument } = require('../../mongodb'); +const { Code, ObjectId, Long, Binary, ReturnDocument, CursorResponse } = require('../../mongodb'); describe('Find', function () { let client; @@ -2388,4 +2388,32 @@ describe('Find', function () { }); }); }); + + it( + 'regression test (NODE-6878): CursorResponse.emptyGetMore contains all CursorResponse fields', + { requires: { topology: 'sharded' } }, + async function () { + const collection = client.db('rewind-regression').collection('bar'); + + await collection.deleteMany({}); + await collection.insertMany(Array.from({ length: 4 }, (_, i) => ({ x: i }))); + + const getMoreSpy = sinon.spy(CursorResponse, 'emptyGetMore', ['get']); + + const cursor = collection.find({}, { batchSize: 1, limit: 3 }); + // emptyGetMore is used internally after limit + 1 documents have been iterated + await cursor.next(); + await cursor.next(); + await cursor.next(); + await cursor.next(); + + // assert that `emptyGetMore` is called. if it is not, this test + // always passes, even without the fix in NODE-6878. + expect(getMoreSpy.get).to.have.been.called; + + cursor.rewind(); + + await cursor.toArray(); + } + ); });