mongo/jstests/change_streams/change_streams_namespace_ma...

334 lines
10 KiB
JavaScript

// Tests regular expressions for namespace matching in change streams.
import {assertDropAndRecreateCollection} from "jstests/libs/collection_drop_recreate.js";
import {ChangeStreamTest} from "jstests/libs/query/change_stream_util.js";
// Database name with special characters inside it.
// According to https://www.mongodb.com/docs/manual/reference/limits/#naming-restrictions, many
// special characters are disallowed here.
const kDBName = jsTestName() + "]]][}}{{{{{{{{";
// Collection name with special characters inside it.
const kCollName = "booooo '\"bar baz]]][}}}}\\{{{{{{{{{{{{{{{{{{{{{{{{{";
const kCollNameOther = "other '\"]]]]";
const testDB = db.getSiblingDB(kDBName);
function runTest(db, collection, runOperations, pipelineStages, expectedChanges) {
assertDropAndRecreateCollection(db, kCollName);
assertDropAndRecreateCollection(db, kCollNameOther);
const cst = new ChangeStreamTest(db);
const pipeline = [{$changeStream: {}}].concat(pipelineStages);
jsTestLog("Running test with pipeline: " + tojson(pipeline));
let cursor = cst.startWatchingChanges({pipeline, collection});
assert.eq(0, cursor.firstBatch.length, "Cursor had changes: " + tojson(cursor));
runOperations(db);
cst.assertNextChangesEqual({cursor, expectedChanges});
cst.cleanUp();
}
// Pipeline using a string literal to filter on the entire namespace.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollName].updateOne({_id: "test"}, {$set: {updated: true}}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
},
[{$match: {"ns": {db: kDBName, coll: kCollName}}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "update",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
updateDescription: {
updatedFields: {
updated: true,
},
removedFields: [],
truncatedArrays: [],
},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a string literal to filter on the database name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
},
[{$match: {"ns.db": kDBName}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on the database name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
},
[{$match: {"ns.db": new RegExp(RegExp.escape(kDBName))}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on the database name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
},
[{$match: {"ns.db": new RegExp(RegExp.escape(kDBName) + "[^'\"\\ {}]*")}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a literal string to filter on the collection name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
},
[{$match: {"ns.coll": kCollName}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on the collection name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
},
[{$match: {"ns.coll": new RegExp(RegExp.escape(kCollName))}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on the collection name.
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
},
[{$match: {"ns.coll": new RegExp("^" + RegExp.escape(kCollName) + "[a-zA-Z]*")}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
runTest(
testDB,
kCollName,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
},
[{$match: {"ns.coll": new RegExp(RegExp.escape(kCollName) + "[^'\"\\ {}]*")}}],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on multiple collections.
runTest(
testDB,
1 /* Use entire database */,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
},
[
{
$match: {
"ns.coll": new RegExp("^(" + RegExp.escape(kCollName) + "|" + RegExp.escape(kCollNameOther) + ")$"),
},
},
],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollNameOther},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollNameOther},
documentKey: {_id: "test"},
},
],
);
// Pipeline using a regular expression to filter on multiple collections.
runTest(
testDB,
1 /* Use entire database */,
(db) => {
assert.commandWorked(db[kCollName].insert({_id: "test"}));
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
},
[
{
$match: {
"ns.coll": {
$in: [
new RegExp("^" + RegExp.escape(kCollName) + "$"),
new RegExp("^" + RegExp.escape(kCollNameOther) + "$"),
],
},
},
},
],
[
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "insert",
fullDocument: {_id: "test"},
ns: {db: kDBName, coll: kCollNameOther},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollName},
documentKey: {_id: "test"},
},
{
operationType: "delete",
ns: {db: kDBName, coll: kCollNameOther},
documentKey: {_id: "test"},
},
],
);
assert.commandWorked(testDB.dropDatabase());