Skip to content

Commit

Permalink
fix(shell-api): Fix invalid regular expression error in db.currentOp()
Browse files Browse the repository at this point in the history
…MONGOSH-1703 (#2187)

Fixes a bug where a regular expression error in db.currentOp() would cause it to error.
  • Loading branch information
gagik authored Oct 9, 2024
1 parent 591aaa5 commit 28f9f52
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 8 deletions.
70 changes: 70 additions & 0 deletions packages/e2e-tests/test/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { createServer as createHTTPServer } from 'http';
import { once } from 'events';
import type { AddressInfo } from 'net';
const { EJSON } = bson;
import { sleep } from './util-helpers';

const jsContextFlagCombinations: `--jsContext=${'plain-vm' | 'repl'}`[][] = [
[],
Expand Down Expand Up @@ -1938,4 +1939,73 @@ describe('e2e', function () {
shell.assertContainsOutput('610');
});
});

describe('currentOp', function () {
context('with 2 shells', function () {
let helperShell: TestShell;
let currentOpShell: TestShell;

const CURRENT_OP_WAIT_TIME = 400;
const OPERATION_TIME = CURRENT_OP_WAIT_TIME * 2;

beforeEach(async function () {
helperShell = this.startTestShell({
args: [await testServer.connectionString()],
});
currentOpShell = this.startTestShell({
args: [await testServer.connectionString()],
});
await helperShell.waitForPrompt();
await currentOpShell.waitForPrompt();

// Insert a dummy object so find commands will actually run with the delay.
await helperShell.executeLine('db.coll.insertOne({})');
});

it('should return the current operation and clear when it is complete', async function () {
const currentCommand = helperShell.executeLine(
`db.coll.find({$where: function() { sleep(${OPERATION_TIME}) }}).projection({testProjection: 1})`
);
helperShell.assertNoErrors();
await sleep(CURRENT_OP_WAIT_TIME);
let currentOpCall = await currentOpShell.executeLine(`db.currentOp()`);

currentOpShell.assertNoErrors();

expect(currentOpCall).to.include('testProjection');

await currentCommand;

currentOpCall = await currentOpShell.executeLine(`db.currentOp()`);

currentOpShell.assertNoErrors();
expect(currentOpCall).not.to.include('testProjection');
});

it('should work when the operation contains regex', async function () {
const regExpString = String.raw`^(?i)\Qchho0842\E`;

// Stringify the reg exp and drop the quotation marks.
// Meant to account for JS escaping behavior and to compare with output later.
const stringifiedRegExpString = `${JSON.stringify(regExpString)}`.slice(
1,
-1
);

void helperShell.executeLine(
`db.coll.find({$where: function() { sleep(${OPERATION_TIME}) }}).projection({re: BSONRegExp('${stringifiedRegExpString}')})`
);
helperShell.assertNoErrors();

await sleep(CURRENT_OP_WAIT_TIME);

const currentOpCall = await currentOpShell.executeLine(
`db.currentOp()`
);
currentOpShell.assertNoErrors();

expect(currentOpCall).to.include(stringifiedRegExpString);
});
});
});
});
3 changes: 3 additions & 0 deletions packages/e2e-tests/test/util-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
15 changes: 8 additions & 7 deletions packages/shell-api/src/database.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1796,10 +1796,11 @@ describe('Database', function () {
},
});

const READ_PREFERENCE = {
const AGGREGATE_OPTIONS = {
$readPreference: {
mode: 'primaryPreferred',
},
bsonRegExp: true,
};

beforeEach(function () {
Expand All @@ -1824,7 +1825,7 @@ describe('Database', function () {
})
);
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
}
});
Expand All @@ -1846,7 +1847,7 @@ describe('Database', function () {
})
);
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
});
});
Expand All @@ -1868,7 +1869,7 @@ describe('Database', function () {
);
expect(matchStage).to.deep.equals({ $match: {} });
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
});
});
Expand All @@ -1890,7 +1891,7 @@ describe('Database', function () {
);
expect(matchStage).to.deep.equals({ $match: {} });
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
});
});
Expand All @@ -1914,7 +1915,7 @@ describe('Database', function () {
);
expect(matchStage).to.deep.equals({ $match: {} });
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
});
}
Expand Down Expand Up @@ -1945,7 +1946,7 @@ describe('Database', function () {
$match: { waitingForLock: true },
});
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
READ_PREFERENCE
AGGREGATE_OPTIONS
);
});

Expand Down
7 changes: 6 additions & 1 deletion packages/shell-api/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,12 @@ export default class Database extends ShellApiWithMongoClass {
}

const adminDb = this.getSiblingDB('admin');
const aggregateOptions = { $readPreference: { mode: 'primaryPreferred' } };
const aggregateOptions = {
$readPreference: { mode: 'primaryPreferred' },
// Regex patterns should be instances of BSONRegExp
// as there can be issues during conversion otherwise.
bsonRegExp: true,
};

try {
const cursor = await adminDb.aggregate(pipeline, aggregateOptions);
Expand Down

0 comments on commit 28f9f52

Please sign in to comment.