Skip to content
9 changes: 4 additions & 5 deletions src/cmap/wire_protocol/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
parseToElementsToArray,
parseUtf8ValidationOption,
pluckBSONSerializeOptions,
serialize,
type Timestamp
} from '../../bson';
import { MONGODB_ERROR_CODES, MongoUnexpectedServerResponseError } from '../../error';
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 29 additions & 1 deletion test/integration/crud/find.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
);
});