import { strict as assert } from "node:assert"; import { test } from "vitest"; import { describeMongoCommandParseFailure, evaluateMongoAggregateSafety, evaluateMongoWriteSafety, mongoAggregateWriteStage, listChainedCalls, MONGO_SHELL_COMMAND_HINT, parseMongoFindExplainCommand, mongoBulkWriteToQueryResult, mongoCollectionStatsToQueryResult, mongoCountToQueryResult, mongoDatabasesToQueryResult, mongoDistinctToQueryResult, mongoDocumentsToQueryResult, mongoDroppedIndexesToQueryResult, mongoFindLogicalTotal, mongoIndexesToQueryResult, normalizeRustMongoCommand, parseMongoAggregateCommand, parseMongoCollectionStatsCommand, parseMongoCommand, parseMongoCreateUserCommand, parseMongoRunCommand, parseMongoCountDocumentsCommand, parseMongoDistinctCommand, parseMongoFindCommand, parseMongoFindOneCommand, parseMongoFindOneAndUpdateCommand, parseMongoFindOneAndReplaceCommand, parseMongoFindOneAndDeleteCommand, parseMongoGetIndexesCommand, parseMongoVersionCommand, planMongoFindPagination, mongoFindPageCoversLogicalLimit, parseMongoWriteCommand, splitMongoCommands, splitMongoCommandRanges, } from "../../apps/desktop/src/lib/mongo/mongoShellCommand.ts"; import type { MongoWriteCommand } from "../../apps/desktop/src/lib/mongo/mongoShellCommand.ts"; import { buildMongoUpdateDocument as buildMongoDocumentUpdate, formatMongoShellLiteral as formatMongoDocumentShellLiteral, mongoDocumentGridDisplayText, mongoDocumentGridEditorText, mongoDocumentGridValue } from "../../apps/desktop/src/lib/mongo/mongoDocumentValues.ts"; import { normalizeJsonArgument } from "../mongo-shell/src/json.ts"; test("parseMongoFindCommand parses db collection find with an empty JSON filter", () => { assert.deepEqual(parseMongoFindCommand("db.users.find({})"), { collection: "users", filter: "{}", skip: 0, limit: 100, sort: undefined, }); }); test("normalizeRustMongoCommand preserves the desktop command contract", () => { assert.deepEqual(normalizeRustMongoCommand({ kind: "countDocuments", collection: "users", filter: "{}", accurate: false }), { kind: "countDocuments", collection: "users", filter: "{}", mode: "legacy" }); assert.deepEqual(normalizeRustMongoCommand({ kind: "dropIndexes", collection: "users", indexes: '"email_1"', single: true }), { kind: "dropIndex", collection: "users", index: '"email_1"' }); assert.deepEqual(normalizeRustMongoCommand({ kind: "findOne", collection: "users", filter: "{}", projection: null, options: null }), { kind: "findOne", collection: "users", filter: "{}" }); }); test("parseMongoCreateUserCommand normalizes user and write concern documents", () => { assert.deepEqual( parseMongoCreateUserCommand(`db.createUser({ user: "test-db", pwd: "test-password", roles: [{ role: "readWrite", db: "db1" }] }, { w: "majority", wtimeout: 5000 })`), { userJson: '{"user":"test-db","pwd":"test-password","roles":[{"role":"readWrite","db":"db1"}]}', writeConcernJson: '{"w":"majority","wtimeout":5000}', }, ); assert.equal(parseMongoCreateUserCommand('db.createUser({pwd: "missing-user", roles: []})'), null); assert.equal(parseMongoCreateUserCommand('db.createUser({user: "test"}, "majority")'), null); }); test("splitMongoCommands executes use before createUser", () => { const commands = splitMongoCommands(`use admin db.createUser({ user: "test-db", pwd: "test-password", roles: [{ role: "readWrite", db: "db1" }] })`); assert.deepEqual( commands.map(({ command }) => command.kind), ["use", "createUser"], ); const createUser = commands[1]?.command; assert.equal(createUser?.kind, "createUser"); if (createUser?.kind === "createUser") { assert.equal(JSON.parse(createUser.userJson).user, "test-db"); assert.match(evaluateMongoWriteSafety(createUser, { allowWrites: true }).reason || "", /high-risk operations/i); assert.equal(evaluateMongoWriteSafety(createUser, { allowWrites: true, allowDangerous: true }).allowed, true); } }); test("parseMongoRunCommand normalizes one non-empty command document", () => { assert.deepEqual( parseMongoRunCommand(`db.runCommand({ find: "orders", filter: {_id: ObjectId("507f1f77bcf86cd799439011")}, createdAt: ISODate("2025-01-01T00:00:00Z") })`), { commandJson: '{"find":"orders","filter":{"_id":{"$oid":"507f1f77bcf86cd799439011"}},"createdAt":{"$date":"2025-01-01T00:00:00Z"}}', }, ); for (const source of ["db.runCommand()", "db.runCommand({})", "db.runCommand('ping')", "db.runCommand({ping: 1}, {readPreference: 'primary'})", "db.runCommand({ping: 1}).valueOf()", "db.runCommand([1, 2, 3])"]) { assert.equal(parseMongoRunCommand(source), null, source); } }); test("parseMongoCommand accepts read-only show-databases aliases exactly", () => { for (const source of ["show dbs", "SHOW DATABASES;", "/* databases */ show dbs -- list"]) { assert.deepEqual(parseMongoCommand(source)?.command, { kind: "showDatabases" }, source); } for (const source of ["show dbs extra", "show database", "show collections", "show"]) { assert.equal(parseMongoCommand(source), null, source); } assert.equal(evaluateMongoWriteSafety(parseMongoCommand("db.runCommand({listDatabases: 1})")!.command as MongoWriteCommand, {}).allowed, false); }); test("splitMongoCommands keeps show databases after a database switch", () => { assert.deepEqual( splitMongoCommands("use app\nshow databases").map(({ command }) => command.kind), ["use", "showDatabases"], ); }); test("mongoDatabasesToQueryResult preserves metadata and bounds rows", () => { assert.deepEqual( mongoDatabasesToQueryResult( [ { databases: [ { name: "admin", sizeOnDisk: 40960, empty: false }, { name: "app", sizeOnDisk: { $numberLong: "8192" }, empty: true }, { name: "logs", sizeOnDisk: 1024, empty: false }, ], totalSize: 50176, ok: 1, }, ], 4.4, 2, ), { columns: ["name", "sizeOnDisk", "empty"], rows: [ ["admin", 40960, false], ["app", "8192", true], ], affected_rows: 3, execution_time_ms: 4, truncated: true, has_more: true, }, ); assert.deepEqual(mongoDatabasesToQueryResult([{ databases: [] }], 0, 10), { columns: ["name", "sizeOnDisk", "empty"], rows: [], affected_rows: 0, execution_time_ms: 0, truncated: false, has_more: false, }); assert.throws(() => mongoDatabasesToQueryResult([{ ok: 1 }], 0, 10), /databases array/); }); test("runCommand support does not accept arbitrary Mongo shell JavaScript", () => { assert.deepEqual( splitMongoCommands(`for (let i = 0; i < 2; i += 1) { db.items.insertOne({ index: i }); }`), [], ); }); test("splitMongoCommands keeps runCommand after a database switch", () => { const commands = splitMongoCommands(`use admin db.runCommand({ ping: 1 })`); assert.deepEqual( commands.map(({ command }) => command.kind), ["use", "runCommand"], ); const runCommand = commands[1]?.command; assert.equal(runCommand?.kind, "runCommand"); if (runCommand?.kind === "runCommand") { assert.deepEqual(JSON.parse(runCommand.commandJson), { ping: 1 }); assert.equal(evaluateMongoWriteSafety(runCommand, { allowWrites: true }).allowed, false); assert.equal(evaluateMongoWriteSafety(runCommand, { allowWrites: true, allowDangerous: true }).allowed, true); } }); test("parseMongoFindCommand parses getCollection find with chained sort skip and limit", () => { assert.deepEqual(parseMongoFindCommand('db.getCollection("audit.logs").find({"level":"warn"}).sort({"createdAt":-1}).skip(20).limit(10)'), { collection: "audit.logs", filter: '{"level":"warn"}', skip: 20, limit: 10, sort: '{"createdAt":-1}', }); }); test("parseMongoFindCommand preserves chained collation for execution and pagination", () => { const command = parseMongoFindCommand(`db.t_user.find({name: 'xxx'}) .collation({ locale: "en", strength: 1 }) .limit(20)`); assert.ok(command); assert.equal(command.collection, "t_user"); assert.deepEqual(JSON.parse(command.filter), { name: "xxx" }); assert.deepEqual(JSON.parse(command.collation ?? "null"), { locale: "en", strength: 1 }); assert.equal(command.skip, 0); assert.equal(command.limit, 20); }); test("planMongoFindPagination pages unbounded find queries", () => { const command = parseMongoFindCommand("db.users.find({})"); assert.ok(command); const plan = planMongoFindPagination("db.users.find({})", command, 100, 100); assert.deepEqual(plan, { pageOffset: 100, pageLimit: 100, requestSkip: 100, requestLimit: 100, logicalSkip: 0, logicalLimit: undefined, }); assert.equal(mongoFindLogicalTotal(824, plan!), 824); }); test("planMongoFindPagination preserves explicit skip and limit bounds", () => { const source = "db.users.find({ active: true }).skip(20).limit(150)"; const command = parseMongoFindCommand(source); assert.ok(command); const plan = planMongoFindPagination(source, command, 100, 100); assert.deepEqual(plan, { pageOffset: 100, pageLimit: 100, requestSkip: 120, requestLimit: 50, logicalSkip: 20, logicalLimit: 150, }); assert.equal(mongoFindLogicalTotal(824, plan!), 150); }); test("mongoFindPageCoversLogicalLimit only skips counting when the first page holds every limited row", () => { const plan = (source: string, pageOffset: number, pageLimit = 100) => { const command = parseMongoFindCommand(source); assert.ok(command); const result = planMongoFindPagination(source, command, pageOffset, pageLimit); assert.ok(result); return result; }; assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({ type: 0 }).sort({ eventTime: -1 }).limit(50)", 0)), true); assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({}).skip(20).limit(100)", 0)), true); assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({}).limit(150)", 0)), false); assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({}).limit(150)", 100)), false); assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({})", 0)), false); assert.equal(mongoFindPageCoversLogicalLimit(plan("db.events.find({}).limit(0)", 0)), false); }); test("parseMongoFindCommand accepts line breaks before find and chained calls", () => { const command = parseMongoFindCommand(`db.getCollection("accounting_reconciliations") .find({ "_id": ObjectId("68ad51ca84c8127bc7d44cb3") }) .sort({ lineNo: -1 }) .skip(5) .limit(20)`); assert.ok(command); assert.equal(command.collection, "accounting_reconciliations"); assert.deepEqual(JSON.parse(command.filter), { _id: { $oid: "68ad51ca84c8127bc7d44cb3" } }); assert.deepEqual(JSON.parse(command.sort || "{}"), { lineNo: -1 }); assert.equal(command.skip, 5); assert.equal(command.limit, 20); }); test("parseMongoFindCommand accepts Compass-style unquoted keys and ObjectId", () => { const command = parseMongoFindCommand("db.products.find({_id: ObjectId('6a045a92d2971e44243771a1')}).limit(1)"); assert.ok(command); assert.equal(command.collection, "products"); assert.equal(command.limit, 1); assert.deepEqual(JSON.parse(command.filter), { _id: { $oid: "6a045a92d2971e44243771a1" } }); }); test("parseMongoFindCommand does not rewrite NumberLong text inside strings", () => { const command = parseMongoFindCommand('db.orders.find({label: "NumberLong(123)"})'); assert.deepEqual(command, { collection: "orders", filter: '{"label": "NumberLong(123)"}', skip: 0, limit: 100, sort: undefined, }); }); test("parseMongoFindCommand rewrites ISODate into extended JSON $date", () => { const command = parseMongoFindCommand(`db.trainingdocuments.find({ createdAt: { $gte: ISODate("2025-02-25T04:57:39.965Z") } })`); assert.ok(command); assert.equal(command.collection, "trainingdocuments"); assert.deepEqual(JSON.parse(command.filter), { createdAt: { $gte: { $date: "2025-02-25T04:57:39.965Z" } } }); }); test("parseMongoFindCommand rewrites new Date and single-quoted ISODate", () => { const command = parseMongoFindCommand("db.events.find({ at: { $lt: new Date('2025-01-01T00:00:00Z'), $gte: ISODate('2024-01-01T00:00:00Z') } })"); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { at: { $lt: { $date: "2025-01-01T00:00:00Z" }, $gte: { $date: "2024-01-01T00:00:00Z" } }, }); }); test("parseMongoFindCommand rewrites NumberLong into extended JSON", () => { const quoted = parseMongoFindCommand('db.orders.find({_id: NumberLong("2048938405781032962")})'); const unquoted = parseMongoFindCommand("db.orders.find({snowflake: NumberLong(9007199254740993)})"); assert.ok(quoted); assert.deepEqual(JSON.parse(quoted.filter), { _id: { $numberLong: "2048938405781032962" } }); assert.ok(unquoted); assert.deepEqual(JSON.parse(unquoted.filter), { snowflake: { $numberLong: "9007199254740993" } }); }); test("parseMongoWriteCommand fills in zero-argument new Date, ISODate and ObjectId", () => { const before = Date.now(); const command = parseMongoWriteCommand(`db.reports.updateOne( { phone_number: "+84905421172", code: "VN" }, { $set: { type: ObjectId("5bbc28701f3fd80f00e0211c"), created_at: new Date(), updated_at: ISODate() } }, { upsert: true } )`); assert.ok(command); assert.equal(command.kind, "update"); const update = JSON.parse(command.update) as { $set: Record }; assert.deepEqual(update.$set.type, { $oid: "5bbc28701f3fd80f00e0211c" }); for (const field of ["created_at", "updated_at"] as const) { const filled = Date.parse(update.$set[field]!.$date!); assert.ok(filled >= before && filled <= Date.now(), `${field} is not the current time`); } assert.match(JSON.parse(parseMongoFindCommand("db.reports.find({_id: ObjectId()})")!.filter)._id.$oid, /^[0-9a-f]{24}$/); }); test("parseMongoFindCommand rewrites epoch-millisecond and numeric BSON constructors", () => { const command = parseMongoFindCommand('db.orders.find({at: new Date(1735689600000), qty: NumberInt(3), total: NumberDecimal("12.34")})'); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { at: { $date: { $numberLong: "1735689600000" } }, qty: { $numberInt: "3" }, total: { $numberDecimal: "12.34" }, }); }); test("parseMongoCommand accepts the mongosh Int32, Long, Double and Decimal128 constructors", () => { const insert = parseMongoCommand('db.getCollection("issue_demo").insert({\n name: "demo",\n coins: Int32("15000"),\n amount: Decimal128("100")\n});'); assert.equal(insert?.command.kind, "insert"); assert.deepEqual(JSON.parse((insert!.command as { docsJson: string }).docsJson), { name: "demo", coins: { $numberInt: "15000" }, amount: { $numberDecimal: "100" }, }); const command = parseMongoFindCommand('db.orders.find({a: Int32(3), b: new Int32(-4), c: Long("9223372036854775807"), d: Long(5), e: Double(1.5), f: Double("2"), g: Decimal128("12.34")})'); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { a: { $numberInt: "3" }, b: { $numberInt: "-4" }, c: { $numberLong: "9223372036854775807" }, d: { $numberLong: "5" }, e: { $numberDouble: "1.5" }, f: { $numberDouble: "2" }, g: { $numberDecimal: "12.34" }, }); for (const source of ["db.orders.find({a: Int32(2147483648)})", 'db.orders.find({a: Long("x")})', 'db.orders.find({a: Double("abc")})', "db.orders.find({a: Decimal128()})", "db.orders.find({a: myLong(1)})"]) { assert.equal(parseMongoFindCommand(source), null, source); } assert.match(describeMongoCommandParseFailure("db.orders.find({a: myLong(1)})"), /Unsupported value myLong\(\.\.\.\)/); assert.match(describeMongoCommandParseFailure("db.orders.find({a: Foo(1)})"), /Int32, Long, Double, Decimal128/); }); test("parseMongoFindCommand rejects out-of-range integer constructor arguments", () => { assert.equal(parseMongoFindCommand("db.orders.find({a: NumberLong(9223372036854775808)})"), null); assert.equal(parseMongoFindCommand("db.orders.find({a: NumberLong(-9223372036854775809)})"), null); assert.equal(parseMongoFindCommand("db.orders.find({a: NumberInt(2147483648)})"), null); assert.equal(parseMongoFindCommand("db.orders.find({a: new Date(99999999999999999999)})"), null); const atBounds = parseMongoFindCommand("db.orders.find({a: NumberLong(9223372036854775807), b: NumberInt(-2147483648)})"); assert.ok(atBounds); assert.deepEqual(JSON.parse(atBounds.filter), { a: { $numberLong: "9223372036854775807" }, b: { $numberInt: "-2147483648" } }); }); test("parseMongoFindCommand does not rewrite constructor text inside strings", () => { const command = parseMongoFindCommand(`db.orders.find({label: "new Date()", note: 'ObjectId()'})`); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { label: "new Date()", note: "ObjectId()" }); }); test("parseMongoCommand wraps db.getSiblingDB() commands with the target database", () => { // The exact command from #3936. const parsed = parseMongoCommand(`db.getSiblingDB("iam_account").getCollection("user") .find({_id: NumberLong('144115205316939462')}) .sort({phone: 1}) .limit(21);`); assert.ok(parsed && parsed.command.kind === "inDatabase"); assert.equal(parsed.command.database, "iam_account"); assert.equal(parsed.command.command.kind, "find"); assert.equal((parsed.command.command as { limit: number }).limit, 21); assert.equal((parsed.command.command as { collection: string }).collection, "user"); assert.equal(parseMongoCommand("db.getSiblingDB('other').orders.updateOne({a: 1}, {$set: {b: 2}})")?.command.kind, "inDatabase"); assert.equal(parseMongoCommand('db . getSiblingDB( "x" ) . stats()')?.command.kind, "inDatabase"); // A sibling command is one statement in a batch, and a plain one after it is unaffected. assert.deepEqual( splitMongoCommandRanges('db.getSiblingDB("a").c.find({});\ndb.d.find({})').map((range) => range.command.kind), ["inDatabase", "find"], ); // A command that parses is not a failure, even when asked directly. assert.equal(describeMongoCommandParseFailure('db.getSiblingDB("other").c.find({})'), MONGO_SHELL_COMMAND_HINT); }); test("parseMongoCommand rejects malformed db.getSiblingDB() with a specific reason", () => { for (const [source, expected] of [ ['db.getSiblingDB("x")', /requires a database name string followed by a command/], ['db.getSiblingDB("").c.find({})', /requires a database name string/], ["db.getSiblingDB(1).c.find({})", /requires a database name string/], ['db.getSiblingDB("x", "y").c.find({})', /requires a database name string/], ['db.getSiblingDB("x").getSiblingDB("y").c.find({})', /cannot be chained/], // Errors inside the wrapped command are reported as they would be without the prefix. ['db.getSiblingDB("x").c.replaceOne({a: 1}, {$set: {b: 2}})', /must not contain update operators such as \$set/], ['db.getSiblingDB("x").c.bogus()', /Collection method bogus\(\) is not supported/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test('parseMongoCommand accepts db["name"] bracket collection accessors', () => { const find = parseMongoFindCommand('db["orders-2024"].find({a: 1})'); assert.ok(find); assert.equal(find.collection, "orders-2024"); assert.deepEqual(JSON.parse(find.filter), { a: 1 }); // Single quotes and a dotted name behave like db.. assert.equal(parseMongoFindCommand("db['audit.logs'].find({})")?.collection, "audit.logs"); assert.deepEqual(parseMongoWriteCommand('db["orders-2024"].updateOne({a: 1}, {$set: {b: 2}}, {upsert: true})'), { kind: "update", collection: "orders-2024", filter: '{"a": 1}', update: '{"$set": {"b": 2}}', options: '{"upsert": true}', many: false, }); for (const source of ["db[].find({})", 'db["x"]find({})', 'db["x"]']) { assert.equal(parseMongoCommand(source), null, source); } }); test("parseMongoFindCommand rewrites UUID, BinData, Timestamp and MinKey/MaxKey", () => { const command = parseMongoFindCommand(`db.c.find({ u: UUID("3b241101-e2bb-4255-8caf-4136c566a962"), b: BinData(128, "AQID"), t: Timestamp(1735689600, 7), lo: MinKey, hi: MaxKey(), range: {$gt: MinKey(), $lt: MaxKey}, list: [MinKey, MaxKey] })`); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { u: { $uuid: "3b241101-e2bb-4255-8caf-4136c566a962" }, b: { $binary: { base64: "AQID", subType: "80" } }, t: { $timestamp: { t: 1735689600, i: 7 } }, lo: { $minKey: 1 }, hi: { $maxKey: 1 }, range: { $gt: { $minKey: 1 }, $lt: { $maxKey: 1 } }, list: [{ $minKey: 1 }, { $maxKey: 1 }], }); const generated = JSON.parse(parseMongoFindCommand("db.c.find({u: UUID()})")!.filter).u.$uuid; assert.match(generated, /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); }); test("parseMongoFindCommand leaves MinKey/MaxKey alone outside value positions", () => { const command = parseMongoFindCommand('db.c.find({MinKey: 1, MaxKey: 2, label: "MinKey", nested: {MaxKey: true}})'); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { MinKey: 1, MaxKey: 2, label: "MinKey", nested: { MaxKey: true } }); }); test("parseMongoFindCommand rejects malformed UUID, BinData, Timestamp and MinKey/MaxKey", () => { for (const source of [ 'db.c.find({b: BinData(256, "x")})', "db.c.find({b: BinData(0)})", 'db.c.find({b: BinData("00", "x")})', "db.c.find({t: Timestamp(1)})", "db.c.find({t: Timestamp(-1, 0)})", "db.c.find({t: Timestamp(4294967296, 0)})", "db.c.find({u: UUID(1)})", "db.c.find({a: MinKey(1)})", 'db.c.find({a: MaxKey("x")})', ]) { assert.equal(parseMongoFindCommand(source), null, source); } }); test("parseMongoFindCommand validates UUID strings at parse time", () => { // mongosh requires the canonical 8-4-4-4-12 hex form: dashes at fixed // positions, hex digits elsewhere. for (const source of [ 'db.c.find({u: UUID("3b241101e2bb42558caf4136c566a962")})', 'db.c.find({u: UUID("3b241101-e2bb-4255-8caf-4136c566a96")})', 'db.c.find({u: UUID("3b241101-e2bb-4255-8caf-4136c566a9620")})', 'db.c.find({u: UUID("zb241101-e2bb-4255-8caf-4136c566a962")})', 'db.c.find({u: UUID("3b241101_e2bb_4255_8caf_4136c566a962")})', ]) { assert.equal(parseMongoFindCommand(source), null, source); } // Upper-case hex is accepted and preserved verbatim. const command = parseMongoFindCommand('db.c.find({u: UUID("3B241101-E2BB-4255-8CAF-4136C566A962")})'); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { u: { $uuid: "3B241101-E2BB-4255-8CAF-4136C566A962" } }); }); test("parseMongoFindExplainCommand reads a final explain() with the find it wraps", () => { assert.deepEqual(parseMongoFindExplainCommand('db.c.find({a: 1}, {b: 1}).sort({b: -1}).skip(2).limit(5).explain("allPlansExecution")'), { collection: "c", filter: '{"a": 1}', projection: '{"b": 1}', skip: 2, limit: 5, sort: '{"b": -1}', verbosity: "allPlansExecution", }); assert.equal(parseMongoFindExplainCommand("db.c.find({a: 1}).explain()")?.verbosity, "queryPlanner"); assert.equal(parseMongoFindExplainCommand("db.c.find({a: 1})\n .sort({b: 1})\n .explain('executionStats');")?.verbosity, "executionStats"); assert.equal(parseMongoFindExplainCommand('db["x-y"].find({}).explain()')?.collection, "x-y"); assert.equal(parseMongoCommand("db.c.find({}).explain()")?.command.kind, "findExplain"); // A plain find is not an explain, and vice versa. assert.equal(parseMongoFindExplainCommand("db.c.find({a: 1}).limit(1)"), null); assert.equal(parseMongoFindCommand("db.c.find({a: 1}).explain()"), null); }); test("parseMongoFindExplainCommand rejects a misplaced explain or an unknown verbosity", () => { for (const [source, expected] of [ ["db.c.find({}).explain('verbose')", /verbosity must be "queryPlanner", "executionStats" or "allPlansExecution", got 'verbose'/], ["db.c.find({}).explain(1)", /verbosity must be .*, got 1/], ["db.c.find({}).explain().limit(5)", /explain\(\) must be the final chain method/], ["db.c.find({}).explain", /Unexpected text after find\(\.\.\.\): "\.explain"/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test("parseMongoFindCommand rejects chained methods it would otherwise silently drop", () => { // Each of these used to parse as a plain find and run a different query than written. for (const [source, expected] of [ ["db.c.find({}).hint({a: 1})", /find\(\)\.hint\(\) is not supported yet\. Supported after find\(\): sort, skip, limit, collation, count, explain, toArray, pretty\./], ["db.c.find({}).batchSize(10)", /batchSize\(\) is not supported yet/], ["db.c.find({}).sort({a: 1}).maxTimeMS(100)", /maxTimeMS\(\) is not supported yet/], ["db.c.find({}).itcount()", /itcount\(\) is not supported; use find\(\)\.count\(\) or countDocuments\(\)/], ["db.c.find({}).size()", /size\(\) is not supported; use find\(\)\.count\(\)/], ["db.c.find({}).forEach(d => print(d))", /forEach\(\) runs JavaScript, which the editor does not execute/], ["db.c.find({}).map(d => d.a)", /map\(\) runs JavaScript/], ["db.c.find({}).toArray(1)", /toArray\(\) takes no arguments/], ["db.c.find({}).count", /Unexpected text after find\(\.\.\.\): "\.count"/], ] as const) { assert.equal(parseMongoFindCommand(source), null, source); assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } // Non-cursor methods explain why nothing can follow them. assert.match(describeMongoCommandParseFailure("db.c.findOne({}).limit(1)"), /findOne\(\) returns a result, not a cursor/); assert.match(describeMongoCommandParseFailure("db.c.countDocuments({}).limit(1)"), /countDocuments\(\) returns a result, not a cursor/); }); test("parseMongoFindCommand still accepts the chain methods it executes", () => { assert.equal(parseMongoFindCommand("db.c.find({a: 1}).sort({b: -1}).skip(2).limit(5)")?.limit, 5); assert.ok(parseMongoFindCommand("db.c.find({}).collation({locale: 'en'}).limit(1)")); assert.ok(parseMongoFindCommand("db.c.find({a: 1}).sort({b: 1}).toArray()")); assert.ok(parseMongoFindCommand("db.c.find({a: 1}).pretty()")); assert.ok(parseMongoFindCommand("db.c.find({a: 1})\n .sort({b: 1})\n .limit(10)")); assert.equal(parseMongoCommand("db.c.find({a: 1}).count()")?.command.kind, "countDocuments"); assert.deepEqual( listChainedCalls(".sort({b: 1}) . limit( 10 ).toArray()")?.map((call) => [call.name, call.args.trim()]), [ ["sort", "{b: 1}"], ["limit", "10"], ["toArray", ""], ], ); assert.equal(listChainedCalls(".count"), null); }); test("parseMongoFindCommand accepts single-quoted string values and unquoted sort keys", () => { const command = parseMongoFindCommand("db.products.find({category: 'Electronics'}).sort({price: -1}).limit(2)"); assert.ok(command); assert.equal(command.collection, "products"); assert.equal(command.limit, 2); assert.deepEqual(JSON.parse(command.filter), { category: "Electronics" }); assert.deepEqual(JSON.parse(command.sort || "{}"), { price: -1 }); }); test("parseMongoFindCommand parses projection arguments", () => { const command = parseMongoFindCommand(`db.jobs.find({ status: "open" }, { title: 1, _id: 0 }).sort({ title: 1 })`); assert.ok(command); assert.equal(command.collection, "jobs"); assert.deepEqual(JSON.parse(command.filter), { status: "open" }); assert.deepEqual(JSON.parse(command.projection || "{}"), { title: 1, _id: 0 }); assert.deepEqual(JSON.parse(command.sort || "{}"), { title: 1 }); }); test("parseMongoFindCommand rejects unsupported mongo shell commands", () => { assert.equal(parseMongoFindCommand("db.users.drop()"), null); assert.equal(parseMongoFindCommand("db.users.find({}, {}, { hint: { name: 1 } })"), null); }); test("parseMongoFindOneCommand parses a dedicated findOne command", () => { assert.deepEqual(parseMongoFindOneCommand("db.users.findOne({email:'a@b.com'})"), { collection: "users", filter: '{"email":"a@b.com"}', }); }); test("parseMongoFindOneCommand defaults an empty filter and parses projection", () => { assert.deepEqual(parseMongoFindOneCommand("db.users.findOne()"), { collection: "users", filter: "{}", }); const withProjection = parseMongoFindOneCommand('db.getCollection("audit.logs").findOne({ level: "warn" }, { message: 1, _id: 0 })'); assert.ok(withProjection); assert.equal(withProjection.collection, "audit.logs"); assert.deepEqual(JSON.parse(withProjection.projection || "{}"), { message: 1, _id: 0 }); }); test("parseMongoFindOneCommand accepts documented projection and options arguments", () => { const command = parseMongoFindOneCommand("db.users.findOne({ active: true }, { name: 1 }, { sort: { createdAt: -1 } })"); assert.ok(command); assert.deepEqual(JSON.parse(command.filter), { active: true }); assert.deepEqual(JSON.parse(command.projection || "{}"), { name: 1 }); assert.deepEqual(JSON.parse(command.options || "{}"), { sort: { createdAt: -1 } }); }); test("parseMongoFindOneCommand rejects cursor chaining and does not collide with find", () => { assert.equal(parseMongoFindOneCommand("db.users.findOne({}).limit(5)"), null); assert.equal(parseMongoFindOneCommand("db.users.find({})"), null); }); test("parseMongoCommand tags findOne with its own kind", () => { const parsed = parseMongoCommand("db.users.findOne({active:true})"); assert.deepEqual(parsed?.command, { kind: "findOne", collection: "users", filter: '{"active":true}', }); }); test("parseMongoFindOneAndUpdateCommand parses filter, update and options", () => { assert.deepEqual(parseMongoFindOneAndUpdateCommand("db.users.findOneAndUpdate({_id:1},{$set:{active:true}},{returnDocument:'after'})"), { collection: "users", filter: '{"_id":1}', update: '{"$set":{"active":true}}', options: '{"returnDocument":"after"}', }); // requires both filter and update assert.equal(parseMongoFindOneAndUpdateCommand("db.users.findOneAndUpdate({_id:1})"), null); }); test("parseMongoFindOneAndReplaceCommand parses filter and replacement", () => { assert.deepEqual(parseMongoFindOneAndReplaceCommand("db.users.findOneAndReplace({_id:1},{name:'a'})"), { collection: "users", filter: '{"_id":1}', replacement: '{"name":"a"}', }); }); test("parseMongoFindOneAndDeleteCommand parses filter and defaults empty", () => { assert.deepEqual(parseMongoFindOneAndDeleteCommand("db.users.findOneAndDelete({_id:1})"), { collection: "users", filter: '{"_id":1}', }); assert.deepEqual(parseMongoFindOneAndDeleteCommand("db.users.findOneAndDelete()"), { collection: "users", filter: "{}", }); }); test("parseMongoCommand tags find-and-modify commands with their kind", () => { assert.equal(parseMongoCommand("db.users.findOneAndUpdate({_id:1},{$set:{a:1}})")?.command.kind, "findOneAndUpdate"); assert.equal(parseMongoCommand("db.users.findOneAndReplace({_id:1},{a:1})")?.command.kind, "findOneAndReplace"); assert.equal(parseMongoCommand("db.users.findOneAndDelete({_id:1})")?.command.kind, "findOneAndDelete"); // findOne must not be swallowed by the find-and-modify parsers assert.equal(parseMongoCommand("db.users.findOne({_id:1})")?.command.kind, "findOne"); }); test("evaluateMongoWriteSafety blocks empty-filter find-and-modify unless dangerous", () => { const command = parseMongoCommand("db.users.findOneAndDelete({})")?.command as MongoWriteCommand; assert.ok(command); assert.equal(evaluateMongoWriteSafety(command, { allowWrites: true, allowDangerous: false }).allowed, false); assert.equal(evaluateMongoWriteSafety(command, { allowWrites: true, allowDangerous: true }).allowed, true); }); test("evaluateMongoWriteSafety fails closed for opaque or effectively unbounded filters", () => { for (const source of [ 'db.users.deleteMany({"$nor":[{"$expr":false}]})', 'db.users.deleteMany({"$or":[{"id":{"$exists":true}},{"id":{"$exists":false}}]})', 'db.users.deleteMany({"$or":[{"id":{"$exists":true}},{"id":{"$not":{"$exists":true}}}]})', 'db.users.deleteMany({"$or":[{"id":{"$eq":1}},{"id":{"$ne":1}}]})', 'db.users.deleteMany({"$or":[{"$and":[{"id":{"$eq":1}}]},{"id":{"$ne":1}}]})', 'db.users.deleteMany({"$or":[{"$and":[{"id":{"$eq":1}},{}]},{"id":{"$ne":1}}]})', 'db.users.deleteMany({"$or":[{"$and":[{"id":{"$eq":1}},{"x":{"$exists":true}}]},{"id":{"$ne":1}},{"x":{"$exists":false}}]})', 'db.users.deleteMany({"$or":[{"id":1},{"id":{"$ne":1}}]})', 'db.users.deleteMany({"$or":[{"id":{"$gt":1}},{"id":{"$lte":1}}]})', 'db.users.deleteMany({"$or":[{"id":{"$gte":1}},{"id":{"$lt":1}}]})', 'db.users.deleteMany({"$or":[{"id":{"$in":[1,2]}},{"id":{"$nin":[2,1]}}]})', 'db.users.deleteMany({"_id":{"$exists":true}})', 'db.users.deleteMany({"id":{"$nin":[]}})', 'db.users.deleteMany({"id":{"$elemMatch":{"value":1}}})', 'db.users.deleteMany({"_id":{"$oid":"not-an-object-id"}})', 'db.users.deleteMany({"sequence":{"$numberLong":"9223372036854775808"}})', 'db.users.deleteMany({"created_at":{"$date":"2026-02-30T00:00:00Z"}})', 'db.users.deleteMany({"name":{"$regex":".*"}})', 'db.users.deleteMany({"$or":[{"_id":{"$oid":"507f1f77bcf86cd799439011"}},{"_id":{"$ne":{"$oid":"507f1f77bcf86cd799439011"}}}]})', 'db.users.updateMany({"$where":"true"},{"$set":{"active":false}})', 'db.users.deleteMany({"$or":[]})', 'db.users.deleteMany({"$opaque":[{"id":1}]})', ]) { const command = parseMongoWriteCommand(source); assert.ok(command, source); assert.equal(evaluateMongoWriteSafety(command, { allowWrites: true, allowDangerous: false }).allowed, false, source); assert.equal(evaluateMongoWriteSafety(command, { allowWrites: true, allowDangerous: true }).allowed, true, source); } for (const source of [ 'db.users.deleteMany({"tenant_id":1})', 'db.users.updateMany({"created_at":{"$gte":"2026-01-01"}},{"$set":{"active":false}})', 'db.users.deleteMany({"$or":[{"tenant_id":1},{"tenant_id":2}]})', 'db.users.deleteMany({"id":{"$ne":1}})', 'db.users.deleteMany({"id":{"$in":[1,2]}})', 'db.users.deleteMany({"id":{"$exists":true}})', 'db.users.updateOne({_id:ObjectId("507f1f77bcf86cd799439011")},{"$set":{"active":true}})', 'db.users.deleteMany({"sequence":NumberLong("9223372036854775807")})', 'db.users.deleteMany({"created_at":ISODate("2026-01-01T00:00:00.000Z")})', 'db.users.deleteMany({"tenant_id":1,"id":{"$nin":[]}})', ]) { const command = parseMongoWriteCommand(source); assert.ok(command, source); assert.equal(evaluateMongoWriteSafety(command, { allowWrites: true, allowDangerous: false }).allowed, true, source); } }); test("parseMongoVersionCommand parses db.version", () => { assert.deepEqual(parseMongoVersionCommand("db.version();"), { kind: "version" }); assert.equal(parseMongoVersionCommand("db.jobs.version()"), null); }); test("parseMongoCommand normalizes outer comments around a command", () => { const parsed = parseMongoCommand(` // current database use accounting; // keep working here `); assert.deepEqual(parsed, { text: "use accounting;", command: { kind: "use", database: "accounting", }, }); }); test("parseMongoCommand strips SQL-style -- comments the editor inserts", () => { // The editor runs Mongo through its SQL language mode, whose line comment is `--`. assert.equal(parseMongoCommand("-- current database\ndb.users.find({})")?.command.kind, "find"); assert.equal(parseMongoCommand("db.users.find({}) -- run this one")?.command.kind, "find"); assert.equal(parseMongoCommand("/* header */ db.users.find({}) -- trailer")?.command.kind, "find"); // Native // comments still work alongside --. assert.equal(parseMongoCommand("// keep\ndb.users.find({})")?.command.kind, "find"); }); test("parseMongoCommand keeps comment markers that live inside string values", () => { // A `--` or `//` inside a string must not be trimmed as a trailing comment. const dash = parseMongoFindCommand('db.users.find({ note: "a--b" })'); assert.ok(dash); assert.deepEqual(JSON.parse(dash.filter), { note: "a--b" }); const slash = parseMongoFindCommand('db.users.find({ url: "http://x" })'); assert.ok(slash); assert.deepEqual(JSON.parse(slash.filter), { url: "http://x" }); }); test("splitMongoCommands splits statements separated by -- comment lines", () => { const commands = splitMongoCommands(` -- seed two rows db.users.insertOne({ name: "A" }); db.users.insertOne({ name: "B" }) -- second `); assert.deepEqual( commands.map(({ command }) => command.kind), ["insert", "insert"], ); }); test("parseMongoWriteCommand accepts unquoted insert and update commands", () => { assert.deepEqual(parseMongoWriteCommand("db.products.insertOne({name: 'demo', price: 1})"), { kind: "insert", collection: "products", docsJson: '{"name": "demo", "price": 1}', }); assert.deepEqual(parseMongoWriteCommand("db.products.updateOne({_id: ObjectId('507f1f77bcf86cd799439011')}, {$set: {stock: 3}})"), { kind: "update", collection: "products", filter: '{"_id": {"$oid":"507f1f77bcf86cd799439011"}}', update: '{"$set": {"stock": 3}}', many: false, }); }); test("parseMongoWriteCommand reads renameCollection", () => { assert.deepEqual(parseMongoWriteCommand('db.orders.renameCollection("orders_2024");'), { kind: "renameCollection", collection: "orders", newName: "orders_2024" }); assert.equal(parseMongoWriteCommand("db[\"a-b\"].renameCollection('c')")?.kind, "renameCollection"); assert.equal(parseMongoWriteCommand('db.getCollection("a").renameCollection("b")')?.kind, "renameCollection"); for (const [source, expected] of [ ["db.orders.renameCollection()", /expects the new collection name/], ["db.orders.renameCollection(1)", /expects the new collection name/], ['db.orders.renameCollection("")', /requires a valid collection name/], ['db.orders.renameCollection("a$b")', /requires a valid collection name/], ['db.orders.renameCollection("orders")', /must differ from the current name/], ['db.orders.renameCollection("x", true)', /dropTarget is not supported; drop the target collection first/], ['db.orders.renameCollection("x").y()', /Unexpected text after renameCollection/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test("parseMongoWriteCommand reads bulkWrite with every operation kind", () => { const command = parseMongoWriteCommand(`db.orders.bulkWrite([ { insertOne: { document: { sku: "A1", stock: 1 } } }, { updateOne: { filter: { sku: "A1" }, update: { $inc: { stock: 1 } }, upsert: true } }, { updateMany: { filter: { archived: true }, update: [{ $set: { stock: 0 } }] } }, { replaceOne: { filter: { sku: "B2" }, replacement: { sku: "B2", stock: 9 } } }, { deleteOne: { filter: { sku: "C3" } } }, { deleteMany: { filter: { stock: { $lt: 0 } } } } ], { ordered: false })`); assert.ok(command && command.kind === "bulkWrite"); assert.equal(command.collection, "orders"); assert.equal(command.options, '{ "ordered": false }'); assert.deepEqual( (JSON.parse(command.operations) as Array>).map((entry) => Object.keys(entry)[0]), ["insertOne", "updateOne", "updateMany", "replaceOne", "deleteOne", "deleteMany"], ); assert.equal(parseMongoWriteCommand('db["my-coll"].bulkWrite([{ deleteOne: { filter: { a: 1 } } }]);')?.kind, "bulkWrite"); }); test("bulkWrite operations are validated and the problem is named", () => { for (const [source, expected] of [ ["db.orders.bulkWrite()", /array of operations and optional options/], ["db.orders.bulkWrite({})", /requires an array of operations/], ["db.orders.bulkWrite([])", /at least one operation/], ["db.orders.bulkWrite([1])", /operation 1 must be a document/], ["db.orders.bulkWrite([{ insertOne: {}, deleteOne: {} }])", /exactly one operation key/], ["db.orders.bulkWrite([{ upsertOne: { document: {} } }])", /unsupported operation upsertOne/], ["db.orders.bulkWrite([{ insertOne: { doc: {} } }])", /unsupported field doc/], ["db.orders.bulkWrite([{ insertOne: {} }])", /requires a document document/], ["db.orders.bulkWrite([{ updateOne: { filter: {}, update: { a: 1 } } }])", /must use operators such as \$set/], ["db.orders.bulkWrite([{ updateOne: { filter: {}, update: { $set: { a: 1 } }, upsert: 1 } }])", /upsert must be a boolean/], ["db.orders.bulkWrite([{ updateOne: { filter: {}, update: { $set: { a: 1 } }, collation: {} } }])", /unsupported field collation/], ["db.orders.bulkWrite([{ replaceOne: { filter: {}, replacement: { $set: { a: 1 } } } }])", /must not contain update operators such as \$set/], ["db.orders.bulkWrite([{ deleteOne: { filter: [] } }])", /field filter must be a document/], ["db.orders.bulkWrite([{ deleteOne: { filter: {} } }], { ordered: 1 })", /ordered option must be a boolean/], ["db.orders.bulkWrite([{ deleteOne: { filter: {} } }], { writeConcern: {} })", /Unsupported bulkWrite\(\) option: writeConcern/], ] as const) { assert.equal(parseMongoWriteCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test("evaluateMongoWriteSafety treats a bulkWrite as risky as its least-bounded operation", () => { const policy = { allowWrites: true, allowDangerous: false } as Parameters[1]; const bounded = parseMongoWriteCommand("db.orders.bulkWrite([{ insertOne: { document: { a: 1 } } }, { deleteOne: { filter: { sku: 'X' } } }])")!; const unbounded = parseMongoWriteCommand("db.orders.bulkWrite([{ updateOne: { filter: { sku: 'X' }, update: { $set: { a: 1 } } } }, { deleteMany: { filter: {} } }])")!; assert.equal(evaluateMongoWriteSafety(bounded, policy).allowed, true); assert.equal(evaluateMongoWriteSafety(unbounded, policy).allowed, false); assert.equal(evaluateMongoWriteSafety(unbounded, { ...policy, allowDangerous: true }).allowed, true); }); test("mongoBulkWriteToQueryResult renders one row of counts", () => { const result = mongoBulkWriteToQueryResult({ inserted_count: 2, matched_count: 3, modified_count: 1, deleted_count: 4, upserted_count: 1 }, 12.6); assert.deepEqual(result.columns, ["insertedCount", "matchedCount", "modifiedCount", "deletedCount", "upsertedCount"]); assert.deepEqual(result.rows, [[2, 3, 1, 4, 1]]); assert.equal(result.affected_rows, 8); assert.equal(result.execution_time_ms, 13); }); test("parseMongoWriteCommand reads replaceOne as a filtered replace", () => { assert.deepEqual(parseMongoWriteCommand('db.orders.replaceOne({_id: ObjectId("507f1f77bcf86cd799439011")}, {name: "new", tags: []}, {upsert: true})'), { kind: "replace", collection: "orders", filter: '{"_id": {"$oid":"507f1f77bcf86cd799439011"}}', replacement: '{"name": "new", "tags": []}', options: '{"upsert": true}', }); assert.deepEqual(parseMongoWriteCommand('db["my-coll"].replaceOne({a: 1}, {b: 2});'), { kind: "replace", collection: "my-coll", filter: '{"a": 1}', replacement: '{"b": 2}', }); for (const source of ["db.orders.replaceOne({a: 1})", "db.orders.replaceOne({a: 1}, {b: 2}, {upsert: true}, 4)", "db.orders.replaceOne({a: 1}, [{b: 2}])", "db.orders.replaceOne({a: 1}, {$set: {b: 2}})"]) { assert.equal(parseMongoWriteCommand(source), null, source); } assert.match(describeMongoCommandParseFailure("db.orders.replaceOne({a: 1}, {$set: {b: 2}})"), /must not contain update operators such as \$set; use updateOne\(\)/); }); test("evaluateMongoWriteSafety guards an unbounded replaceOne like an update", () => { const bounded = parseMongoWriteCommand("db.orders.replaceOne({a: 1}, {b: 2})")!; const unbounded = parseMongoWriteCommand("db.orders.replaceOne({}, {b: 2})")!; const policy = { allowWrites: true, allowDangerous: false } as Parameters[1]; assert.equal(evaluateMongoWriteSafety(bounded, policy).allowed, true); assert.equal(evaluateMongoWriteSafety(unbounded, policy).allowed, false); assert.equal(evaluateMongoWriteSafety(bounded, { ...policy, allowWrites: false }).allowed, false); }); test("normalizeRustMongoCommand passes a replace command through unchanged", () => { assert.deepEqual(normalizeRustMongoCommand({ kind: "replace", collection: "orders", filter: '{"a":1}', replacement: '{"b":2}', options: null }), { kind: "replace", collection: "orders", filter: '{"a":1}', replacement: '{"b":2}', }); }); test("parseMongoWriteCommand unwraps EJSON.deserialize values", () => { assert.deepEqual(parseMongoWriteCommand('db.products.updateOne({_id: ObjectId("507f1f77bcf86cd799439011")}, {$set: {price: EJSON.deserialize({"$numberDecimal":"12.34"}), payload: EJSON.deserialize({"$binary":{"base64":"AQI=","subType":"00"}})}})'), { kind: "update", collection: "products", filter: '{"_id": {"$oid":"507f1f77bcf86cd799439011"}}', update: '{"$set": {"price": {"$numberDecimal":"12.34"}, "payload": {"$binary":{"base64":"AQI=","subType":"00"}}}}', many: false, }); }); test("parseMongoWriteCommand accepts the copied insert form that wraps BSON values in EJSON.deserialize", () => { // "Copy -> SQL Inserts" formats values extended JSON cannot spell directly // through `EJSON.deserialize({...})`. The statement is re-parsed by the Rust // parser before execution, so both parsers must accept this exact shape // (#11666). assert.deepEqual( parseMongoWriteCommand(`db.getCollection("test").insert({ "type": EJSON.deserialize({ "$numberInt": "999999" }), "refid": "27507" })`), { kind: "insert", collection: "test", docsJson: `{ "type": { "$numberInt": "999999" }, "refid": "27507" }`, }, ); }); test("parseMongoWriteCommand accepts legacy insert commands", () => { assert.deepEqual( parseMongoWriteCommand(`db.getCollection("accounting_reconciliations").insert({ "accountId": 999, "status": "done" });`), { kind: "insert", collection: "accounting_reconciliations", docsJson: '{\n "accountId": 999,\n "status": "done"\n }', }, ); assert.deepEqual(parseMongoWriteCommand("db.products.insert([{name: 'first'}, {name: 'second'}])"), { kind: "insert", collection: "products", docsJson: '[{"name": "first"}, {"name": "second"}]', }); assert.deepEqual(parseMongoWriteCommand("db.products.insertMany([{name: 'first'}, {name: 'second'}])"), { kind: "insert", collection: "products", docsJson: '[{"name": "first"}, {"name": "second"}]', }); assert.equal(parseMongoWriteCommand("db.products.insert({name: 'demo'}, {writeConcern: {w: 1}})"), null); assert.equal(parseMongoWriteCommand("db.products.insert()"), null); assert.equal(parseMongoWriteCommand("db.products.insert('demo')"), null); }); test("parseMongoWriteCommand accepts updateMany arrayFilters options", () => { assert.deepEqual( parseMongoWriteCommand(`db.issue_3231.updateMany( { msgType: 3, "order.orderId": { $in: [12345] } }, { $set: { "order.$[orderElem].bcorderproducts.$[prodElem].pankouType": "双双2" } }, { arrayFilters: [ { "orderElem.orderId": { $in: [12345] } }, { "prodElem.id": 322678 } ] } )`), { kind: "update", collection: "issue_3231", filter: '{ "msgType": 3, "order.orderId": { "$in": [12345] } }', update: '{ "$set": { "order.$[orderElem].bcorderproducts.$[prodElem].pankouType": "双双2" } }', options: '{ "arrayFilters": [\n { "orderElem.orderId": { "$in": [12345] } },\n { "prodElem.id": 322678 }\n ] }', many: true, }, ); }); test("parseMongoWriteCommand accepts Mongo shell regex literals", () => { const command = parseMongoWriteCommand(`db.code_info.updateMany( { level: "SECOND_LAYER", position: "B", packagingRatio: /^[^::]*盒/, type: { $ne: "PACK_IN" } }, { $set: { type: "PACK_IN" }, $currentDate: { lastUpdateTime: true } } )`); assert.equal(command?.kind, "update"); if (command?.kind !== "update") return; assert.equal(command.collection, "code_info"); assert.equal(command.many, true); assert.deepEqual(JSON.parse(command.filter), { level: "SECOND_LAYER", position: "B", packagingRatio: { $regularExpression: { pattern: "^[^::]*盒", options: "" } }, type: { $ne: "PACK_IN" }, }); }); test("parseMongoWriteCommand ignores delimiters inside Mongo shell regex literals", () => { const command = parseMongoWriteCommand(String.raw`db.items.updateMany({ value: /a\),b/ }, { $set: { matched: true } })`); assert.equal(command?.kind, "update"); if (command?.kind !== "update") return; assert.deepEqual(JSON.parse(command.filter), { value: { $regularExpression: { pattern: "a\\),b", options: "" } }, }); }); test("normalizeJsonArgument handles regex escapes, character classes, flags, strings, and comments", () => { const normalized = normalizeJsonArgument(`{ pattern: /a\\/b[/::]/mi, url: "https://example.com/a/b", note: "// remains text", // slash comments remain comments active: true }`); assert.ok(normalized); assert.deepEqual(JSON.parse(normalized), { pattern: { $regularExpression: { pattern: "a\\/b[/::]", options: "im" } }, url: "https://example.com/a/b", note: "// remains text", active: true, }); }); test("normalizeJsonArgument accepts a regex literal after comments", () => { const normalized = normalizeJsonArgument(`{ block: /* explain the pattern */ /block/i, line: // explain the next pattern /line/m }`); assert.ok(normalized); assert.deepEqual(JSON.parse(normalized), { block: { $regularExpression: { pattern: "block", options: "i" } }, line: { $regularExpression: { pattern: "line", options: "m" } }, }); }); test("normalizeJsonArgument drops JS-only regex literal flags", () => { const normalized = normalizeJsonArgument(`{ global: /value/g, sticky: /value/yd, verbose: /value/givs }`); assert.ok(normalized); assert.deepEqual(JSON.parse(normalized), { global: { $regularExpression: { pattern: "value", options: "" } }, sticky: { $regularExpression: { pattern: "value", options: "" } }, verbose: { $regularExpression: { pattern: "value", options: "is" } }, }); }); test("normalizeJsonArgument rejects malformed Mongo shell regex literals", () => { for (const source of ["{pattern: /unterminated}", "{pattern: /value/ii}", "{pattern: /value/q}"]) { assert.equal(normalizeJsonArgument(source), null, source); } }); test("parseMongoWriteCommand parses createIndex with optional options", () => { assert.deepEqual(parseMongoWriteCommand("db.users.createIndex({email: 1}, {unique: true, name: 'users_email_unique'})"), { kind: "createIndex", collection: "users", keys: '{"email": 1}', options: '{"unique": true, "name": "users_email_unique"}', }); }); test("parseMongoWriteCommand parses dropIndex and dropIndexes variants", () => { assert.deepEqual(parseMongoWriteCommand('db.users.dropIndex("users_email_unique")'), { kind: "dropIndex", collection: "users", index: '"users_email_unique"', }); assert.deepEqual(parseMongoWriteCommand("db.users.dropIndex({email: 1})"), { kind: "dropIndex", collection: "users", index: '{"email": 1}', }); assert.deepEqual(parseMongoWriteCommand("db.users.dropIndexes()"), { kind: "dropIndexes", collection: "users", }); assert.deepEqual(parseMongoWriteCommand("db.users.dropIndexes({email: 1})"), { kind: "dropIndexes", collection: "users", indexes: '{"email": 1}', }); assert.deepEqual(parseMongoWriteCommand('db.users.dropIndexes("*")'), { kind: "dropIndexes", collection: "users", indexes: '"*"', }); assert.deepEqual(parseMongoWriteCommand('db.users.dropIndexes(["a_1", "b_1"])'), { kind: "dropIndexes", collection: "users", indexes: '["a_1", "b_1"]', }); }); test("parseMongoWriteCommand parses collection drop commands", () => { assert.deepEqual(parseMongoWriteCommand("db.users.drop()"), { kind: "dropCollection", collection: "users", }); assert.deepEqual(parseMongoWriteCommand('db.getCollection("audit.logs").drop();'), { kind: "dropCollection", collection: "audit.logs", }); assert.deepEqual(parseMongoCommand("db.users.drop()")?.command, { kind: "dropCollection", collection: "users", }); }); test("parseMongoWriteCommand rejects collection drop arguments", () => { assert.equal(parseMongoWriteCommand("db.users.drop({ writeConcern: 1 })"), null); }); test("parseMongoWriteCommand rejects invalid dropIndex/dropIndexes variants", () => { assert.equal(parseMongoWriteCommand("db.users.dropIndex()"), null); assert.equal(parseMongoWriteCommand('db.users.dropIndex("*")'), null); assert.equal(parseMongoWriteCommand('db.users.dropIndex(["a_1"])'), null); assert.equal(parseMongoWriteCommand('db.users.dropIndexes([{"a":1}])'), null); }); test("evaluateMongoWriteSafety blocks collection drop unless dangerous writes are enabled", () => { const dropCollection = parseMongoWriteCommand("db.users.drop()"); assert.ok(dropCollection); assert.match(evaluateMongoWriteSafety(dropCollection, { allowWrites: true }).reason || "", /high-risk operations.*DBX MCP settings/i); assert.equal(evaluateMongoWriteSafety(dropCollection, { allowWrites: true, allowDangerous: true }).allowed, true); }); test("evaluateMongoWriteSafety requires high-risk permission for schema changes", () => { const dropAll = parseMongoWriteCommand("db.users.dropIndexes()"); assert.ok(dropAll); assert.match(evaluateMongoWriteSafety(dropAll, { allowWrites: true }).reason || "", /high-risk operations.*DBX MCP settings/i); const dropOne = parseMongoWriteCommand('db.users.dropIndexes("users_email_unique")'); assert.ok(dropOne); assert.equal(evaluateMongoWriteSafety(dropOne, { allowWrites: true }).allowed, false); assert.equal(evaluateMongoWriteSafety(dropOne, { allowWrites: true, allowDangerous: true }).allowed, true); }); test("parseMongoCountDocumentsCommand parses db collection countDocuments", () => { assert.deepEqual(parseMongoCountDocumentsCommand("db.products.countDocuments({})"), { collection: "products", filter: "{}", mode: "accurate", }); }); test("parseMongoCountDocumentsCommand parses legacy count helpers", () => { assert.deepEqual(parseMongoCountDocumentsCommand("db.products.count({ active: true })"), { collection: "products", filter: '{ "active": true }', mode: "legacy", }); assert.deepEqual(parseMongoCountDocumentsCommand('db.getCollection("audit.logs").count()'), { collection: "audit.logs", filter: "{}", mode: "legacy", }); assert.deepEqual(parseMongoCountDocumentsCommand("db.products.find({ active: true }).count()"), { collection: "products", filter: '{ "active": true }', mode: "legacy", }); assert.equal(parseMongoFindCommand("db.products.find({ active: true }).count()"), null); assert.deepEqual(parseMongoCommand("db.products.find({ active: true }).count()")?.command, { kind: "countDocuments", collection: "products", filter: '{ "active": true }', mode: "legacy", }); }); test("parseMongoAggregateCommand parses db collection aggregate", () => { assert.deepEqual(parseMongoAggregateCommand('db.products.aggregate([{"$match":{"active":true}},{"$count":"total"}])'), { collection: "products", pipeline: '[{"$match":{"active":true}},{"$count":"total"}]', }); }); test("parseMongoAggregateCommand drops the no-op cursor methods mongosh and Compass append", () => { const expected = { collection: "orders", pipeline: '[{"$match": {"a": 1}}]' }; for (const source of [ "db.orders.aggregate([{$match: {a: 1}}]).toArray()", "db.orders.aggregate([{$match: {a: 1}}]).pretty()", "db.orders.aggregate([{$match: {a: 1}}]).toArray().pretty()", "db.orders.aggregate([{$match: {a: 1}}])\n .toArray()", "db.orders.aggregate([{$match: {a: 1}}]).toArray();", ]) { assert.deepEqual(parseMongoAggregateCommand(source), expected, source); } assert.deepEqual(parseMongoAggregateCommand("db.orders.aggregate([], {allowDiskUse: true}).toArray()"), { collection: "orders", pipeline: "[]", options: '{"allowDiskUse": true}', }); }); test("parseMongoAggregateCommand still rejects cursor methods that would change the result", () => { for (const source of ["db.orders.aggregate([]).limit(5)", "db.orders.aggregate([]).sort({a: 1})", "db.orders.aggregate([]).toArray().limit(5)", "db.orders.aggregate([]).toArray(1)"]) { assert.equal(parseMongoAggregateCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), /Chaining .* is not supported/, source); } }); test("parseMongoAggregateCommand accepts an empty pipeline", () => { assert.deepEqual(parseMongoAggregateCommand("db.products.aggregate([])"), { collection: "products", pipeline: "[]", }); }); test("parseMongoAggregateCommand accepts Mongo Shell trailing commas", () => { const command = parseMongoAggregateCommand(`db.AdressInfo.aggregate([ { $match: { IsDelete: 0, DataSource: { $ne: 'XC' }, TypeName: 1, }, }, { $project: { MainId: '$_id', labels: ['primary', 'backup',], }, }, { $out: 'IBMBiititle' }, ], { allowDiskUse: true, })`); assert.ok(command); assert.deepEqual(JSON.parse(command.pipeline), [ { $match: { IsDelete: 0, DataSource: { $ne: "XC" }, TypeName: 1, }, }, { $project: { MainId: "$_id", labels: ["primary", "backup"], }, }, { $out: "IBMBiititle" }, ]); assert.deepEqual(JSON.parse(command.options ?? "null"), { allowDiskUse: true }); }); test("parseMongoAggregateCommand preserves trailing-comma text inside strings", () => { const command = parseMongoAggregateCommand(`db.logs.aggregate([ { $project: { objectText: "literal,}", arrayText: "literal,]", escapedQuote: "literal\\",]", }, }, ])`); assert.ok(command); assert.deepEqual(JSON.parse(command.pipeline), [ { $project: { objectText: "literal,}", arrayText: "literal,]", escapedQuote: 'literal",]', }, }, ]); }); test("parseMongoAggregateCommand ignores comments inside aggregate pipelines", () => { const command = parseMongoAggregateCommand(` db.cash.aggregate([ { $match: { portfolio_id: "b6f6ec62-8571-11f1-bbfb-000c29caf77f", date: { $gte: "20260604" } } }, { $unwind: "$cashs" }, { $project: { _id: 0, date: 1, trans_currency_cd: "$cashs.trans_currency_cd", cash_local: { $multiply: ["$cashs.cash", "$cashs.fx_rate"] } } }, { $group: { _id: { date: "$date", currency: "$trans_currency_cd" }, cash_local: { $sum: "$cash_local" } } }, // 第二步:按 date 分组,将不同币种转为字段 { $group: { _id: "$_id.date", cny: { $sum: { $cond: [{ $eq: ["$_id.currency", "CNY"] }, "$cash_local", 0] } }, hkd: { $sum: { $cond: [{ $eq: ["$_id.currency", "HKD"] }, "$cash_local", 0] } }, total_cash: { $sum: "$cash_local" } } }, { $sort: { _id: 1 } }, /* 可选:将 _id 重命名为 date */ { $project: { _id: 0, date: "$_id", cny: 1, hkd: 1, total_cash: 1 } } ]) `); assert.ok(command); assert.equal(command.collection, "cash"); const pipeline = JSON.parse(command.pipeline); assert.equal(pipeline.length, 7); assert.deepEqual(pipeline[4].$group.total_cash, { $sum: "$cash_local" }); }); test("parseMongoAggregateCommand keeps comment markers inside string values", () => { const command = parseMongoAggregateCommand(`db.logs.aggregate([ { $match: { url: "https://example.com/a//b", note: "literal /* text */" } }, // comment with closing delimiters )]} { $project: { url: 1, note: 1 } } ])`); assert.ok(command); assert.deepEqual(JSON.parse(command.pipeline)[0].$match, { url: "https://example.com/a//b", note: "literal /* text */", }); }); test("parseMongoAggregateCommand accepts every JavaScript line terminator after comments", () => { for (const lineTerminator of ["\n", "\r", "\r\n", "\u2028", "\u2029"]) { const command = parseMongoAggregateCommand(`db.logs.aggregate([// pipeline${lineTerminator}{ $match: { active: true } }])`); assert.ok(command, `expected parser result for ${JSON.stringify(lineTerminator)}`); assert.deepEqual(JSON.parse(command.pipeline), [{ $match: { active: true } }]); } }); test("parseMongoAggregateCommand accepts official aggregate options document", () => { assert.deepEqual(parseMongoAggregateCommand("db.products.aggregate([], {})"), { collection: "products", pipeline: "[]", options: "{}", }); const withExplain = parseMongoAggregateCommand("db.uc_user.aggregate([], {explain: true})"); assert.equal(withExplain?.collection, "uc_user"); assert.equal(withExplain?.pipeline, "[]"); assert.deepEqual(JSON.parse(withExplain?.options ?? "null"), { explain: true }); // Official aggregate options are parsed as a free-form object and forwarded to the server. const fullOptions = parseMongoAggregateCommand(`db.products.aggregate( [{"$match":{"active":true}}], { allowDiskUse: true, cursor: { batchSize: 50 }, maxTimeMS: 1000, collation: { locale: "en" }, hint: "status_1", comment: "agg-test", let: { year: 2024 } } )`); assert.equal(fullOptions?.collection, "products"); assert.deepEqual(JSON.parse(fullOptions?.pipeline ?? "null"), [{ $match: { active: true } }]); assert.deepEqual(JSON.parse(fullOptions?.options ?? "null"), { allowDiskUse: true, cursor: { batchSize: 50 }, maxTimeMS: 1000, collation: { locale: "en" }, hint: "status_1", comment: "agg-test", let: { year: 2024 }, }); }); test("parseMongoAggregateCommand rejects non-array pipelines and invalid options", () => { assert.equal(parseMongoAggregateCommand('db.products.aggregate({"$match":{}})'), null); assert.equal(parseMongoAggregateCommand("db.products.aggregate([], [])"), null); assert.equal(parseMongoAggregateCommand("db.products.aggregate([], {explain: true"), null); assert.equal(parseMongoAggregateCommand("db.products.aggregate([]).limit(10)"), null); assert.equal(parseMongoAggregateCommand("db.products.aggregate([], {}, true)"), null); }); test("parseMongoCountDocumentsCommand reads estimatedDocumentCount as a metadata-backed count", () => { // The driver already takes the metadata fast path for a filterless legacy count(). const expected = { collection: "orders", filter: "{}", mode: "legacy" }; for (const source of ["db.orders.estimatedDocumentCount()", 'db["orders"].estimatedDocumentCount()', "db.getCollection('orders').estimatedDocumentCount();"]) { assert.deepEqual(parseMongoCountDocumentsCommand(source), expected, source); } assert.equal(parseMongoCountDocumentsCommand("db.orders.estimatedDocumentCount({a: 1})"), null); assert.equal(parseMongoCountDocumentsCommand("db.orders.estimatedDocumentCount().limit(5)"), null); // count() and countDocuments() keep their own modes. assert.equal(parseMongoCountDocumentsCommand("db.orders.countDocuments({})")?.mode, "accurate"); assert.equal(parseMongoCountDocumentsCommand("db.orders.count()")?.mode, "legacy"); }); test("parseMongoRunCommand reads db.dropDatabase() and db.createCollection() as run commands", () => { assert.deepEqual(parseMongoRunCommand("db . dropDatabase ( ) ;"), { commandJson: '{"dropDatabase":1}' }); assert.deepEqual(parseMongoRunCommand('db.createCollection("events")'), { commandJson: '{"create":"events"}' }); assert.deepEqual(JSON.parse(parseMongoRunCommand("db.createCollection('logs', {capped: true, size: 1048576, max: 1000})")!.commandJson), { create: "logs", capped: true, size: 1048576, max: 1000, }); assert.equal(parseMongoCommand("db.dropDatabase()")?.command.kind, "runCommand"); for (const [source, expected] of [ ["db.dropDatabase(1)", /dropDatabase\(\) expects no arguments/], ["db.createCollection()", /createCollection\(\) expects a collection name and optional options/], ["db.createCollection(1)", /expects a collection name/], ['db.createCollection("a$b")', /expects a collection name/], ['db.createCollection("a", {create: "b"})', /expects a collection name/], ['db.createCollection("a").x()', /Unexpected text after createCollection/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test("parseMongoRunCommand reads db.createView() as a create command with viewOn and pipeline", () => { assert.deepEqual(JSON.parse(parseMongoRunCommand('db.createView("active_users", "users", [{ $match: { active: true } }])')!.commandJson), { create: "active_users", viewOn: "users", pipeline: [{ $match: { active: true } }], }); assert.deepEqual(JSON.parse(parseMongoRunCommand("db.createView('v', 'users', [], { collation: { locale: 'fr' } });")!.commandJson), { create: "v", viewOn: "users", pipeline: [], collation: { locale: "fr" }, }); assert.equal(parseMongoCommand('db.createView("v", "users", [])')?.command.kind, "runCommand"); for (const [source, expected] of [ ['db.createView("v", "users")', /createView\(\) expects a view name, a source collection name, a pipeline/], ['db.createView("v", "users", {})', /createView\(\) expects/], ['db.createView("v", 1, [])', /createView\(\) expects/], ['db.createView("v", "users", [], { viewOn: "other" })', /createView\(\) expects/], ['db.createView("v", "users", [{ $match: { a: Foo() } }])', /Unsupported value Foo\(\.\.\.\) in the pipeline argument of createView\(\)/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } }); test("parseMongoRunCommand reads user and role helpers as usersInfo and rolesInfo", () => { for (const [source, expected] of [ ["show users", { usersInfo: 1 }], ["SHOW ROLES;", { rolesInfo: 1 }], ["db.getUsers()", { usersInfo: 1 }], ["db.getUsers({ showCredentials: false, filter: { roles: { $size: 0 } } })", { usersInfo: 1, showCredentials: false, filter: { roles: { $size: 0 } } }], ['db.getUser("app")', { usersInfo: "app" }], ["db.getUser('app', { showPrivileges: true })", { usersInfo: "app", showPrivileges: true }], ["db.getRoles({ showBuiltinRoles: true })", { rolesInfo: 1, showBuiltinRoles: true }], ['db.getRole("reader")', { rolesInfo: "reader" }], ] as const) { assert.deepEqual(JSON.parse(parseMongoRunCommand(source)?.commandJson ?? "null"), expected, source); assert.equal(parseMongoCommand(source)?.command.kind, "runCommand", source); } for (const [source, expected] of [ ["db.getUser()", /getUser\(\) expects a user name/], ["db.getUser(1)", /getUser\(\) expects a user name/], ["db.getUsers({ usersInfo: 2 })", /getUsers\(\) expects optional options/], ["db.getRoles({}, {})", /getRoles\(\) expects optional options/], ['db.getRole("a").x()', /Unexpected text after getRole/], ] as const) { assert.equal(parseMongoCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), expected, source); } assert.equal(parseMongoCommand("show users extra"), null); assert.deepEqual( splitMongoCommands("use admin\nshow users\nshow roles").map(({ command }) => command.kind), ["use", "runCommand", "runCommand"], ); }); test("parseMongoRunCommand reads db.stats() and db.serverStatus() as run commands", () => { assert.deepEqual(parseMongoRunCommand("db.stats()"), { commandJson: '{"dbStats":1}' }); assert.deepEqual(parseMongoRunCommand("db . serverStatus ( ) ;"), { commandJson: '{"serverStatus":1}' }); assert.deepEqual(parseMongoRunCommand("DB.STATS()"), { commandJson: '{"dbStats":1}' }); for (const source of ["db.stats(1)", "db.serverStatus({})"]) { assert.equal(parseMongoRunCommand(source), null, source); assert.match(describeMongoCommandParseFailure(source), /takes no arguments|expects no arguments/, source); } // db.collection.stats() is still collection stats, not a run command. assert.equal(parseMongoRunCommand("db.orders.stats()"), null); assert.equal(parseMongoCommand("db.orders.stats()")?.command.kind, "collectionStats"); }); test("describeMongoCommandParseFailure names an unsupported value constructor and where it is", () => { const message = describeMongoCommandParseFailure('db.reports.updateOne({a: 1}, {$set: {t: Foo("x")}}, {upsert: true})'); assert.match(message, /Unsupported value Foo\(\.\.\.\) in the update argument of updateOne\(\)/); assert.match(message, /ObjectId, ISODate, new Date, NumberLong/); assert.match(describeMongoCommandParseFailure("db.c.find({a: new Bar()})"), /Unsupported value new Bar\(\.\.\.\) in the filter argument of find\(\)/); // Text inside a string is not a constructor call. assert.match(describeMongoCommandParseFailure('db.c.find({a: "Foo(1)", b: })'), /filter argument of find\(\) is not a valid document/); }); test("describeMongoCommandParseFailure explains the argument shape a known method expects", () => { assert.equal(describeMongoCommandParseFailure("db.c.insertOne({a: 1}, {writeConcern: {w: 1}})"), "insertOne() expects one document."); assert.equal(describeMongoCommandParseFailure("db.c.deleteOne({a: 1}, {collation: {locale: 'en'}})"), "deleteOne() expects a filter."); assert.equal(describeMongoCommandParseFailure("db.c.updateOne({a: 1})"), "updateOne() expects a filter, an update, and optional options."); assert.equal(describeMongoCommandParseFailure("db.runCommand()"), "runCommand() expects one command document."); assert.equal(describeMongoCommandParseFailure("db.version(1)"), "version() expects no arguments."); assert.equal(describeMongoCommandParseFailure("db.c.find({a: 1}).count"), 'Unexpected text after find(...): ".count".'); }); test("describeMongoCommandParseFailure names unsupported methods and points at alternatives", () => { const mapReduce = describeMongoCommandParseFailure("db.c.mapReduce()"); assert.match(mapReduce, /^Collection method mapReduce\(\) is not supported\. Supported collection methods: find, findOne/); // Bracket and getCollection targets are recognised too. assert.match(describeMongoCommandParseFailure('db["my-coll"].validate()'), /validate\(\) is not supported/); assert.match(describeMongoCommandParseFailure('db.getCollection("my-coll").watch()'), /watch\(\) is not supported/); assert.match(describeMongoCommandParseFailure("db.getCollectionNames()"), /db\.getCollectionNames\(\) is not supported; collections are listed in the sidebar/); assert.match(describeMongoCommandParseFailure("db.currentOp()"), /db\.currentOp\(\) is not supported/); assert.match(describeMongoCommandParseFailure("db.adminCommand({ping: 1})"), /use db\.runCommand/); assert.match(describeMongoCommandParseFailure("show collections"), /listed in the sidebar/); // Leading comments do not hide the command shape. assert.match(describeMongoCommandParseFailure("// note\ndb.c.mapReduce()"), /mapReduce\(\) is not supported/); }); test("describeMongoCommandParseFailure reports unclosed delimiters and shell hints", () => { const unclosed = describeMongoCommandParseFailure("db.uc_user.aggregate([], {explain: true"); assert.match(unclosed, /unclosed/i); const chained = describeMongoCommandParseFailure("db.products.aggregate([]).limit(10)"); assert.match(chained, /chaining|not supported/i); const nonArrayPipeline = describeMongoCommandParseFailure('db.products.aggregate({"$match":{}})'); assert.match(nonArrayPipeline, /pipeline must be a JSON array/i); const badOptions = describeMongoCommandParseFailure("db.products.aggregate([], [])"); assert.match(badOptions, /options must be a JSON object/i); const generic = describeMongoCommandParseFailure("SELECT 1"); assert.match(generic, /MongoDB shell-style commands/i); }); test("parseMongoAggregateCommand normalises ObjectId arguments with either quote style", () => { const oid = "507f1f77bcf86cd799439011"; for (const quote of ['"', "'"]) { const command = parseMongoAggregateCommand(`db.orders.aggregate([{"$match":{"_id":ObjectId(${quote}${oid}${quote})}}])`); assert.ok(command, `quote=${quote} should parse`); assert.equal(command.collection, "orders"); assert.deepEqual(JSON.parse(command.pipeline), [{ $match: { _id: { $oid: oid } } }]); } }); test("parseMongoDistinctCommand parses a field with an optional filter", () => { assert.deepEqual(parseMongoDistinctCommand('db.products.distinct("category")'), { collection: "products", field: "category", }); assert.deepEqual(parseMongoDistinctCommand("db.products.distinct('category', { active: true })"), { collection: "products", field: "category", filter: '{ "active": true }', }); assert.deepEqual(parseMongoDistinctCommand('db.getCollection("audit.logs").distinct("level");'), { collection: "audit.logs", field: "level", }); }); test("parseMongoDistinctCommand normalises shell constructors in its filter", () => { const command = parseMongoDistinctCommand("db.orders.distinct(\"status\", { _id: ObjectId('507f1f77bcf86cd799439011') })"); assert.ok(command); assert.deepEqual(JSON.parse(command.filter || "{}"), { _id: { $oid: "507f1f77bcf86cd799439011" } }); }); test("parseMongoDistinctCommand requires a non-empty string field", () => { assert.equal(parseMongoDistinctCommand("db.products.distinct()"), null); assert.equal(parseMongoDistinctCommand('db.products.distinct("")'), null); assert.equal(parseMongoDistinctCommand("db.products.distinct({ category: 1 })"), null); assert.equal(parseMongoDistinctCommand("db.products.distinct(category)"), null); // Cursor chaining and extra arguments are rejected rather than silently ignored. assert.equal(parseMongoDistinctCommand('db.products.distinct("category").limit(5)'), null); assert.equal(parseMongoDistinctCommand('db.products.distinct("category", {}, {})'), null); }); test("parseMongoCommand tags distinct with its own kind", () => { assert.deepEqual(parseMongoCommand('db.products.distinct("category")')?.command, { kind: "distinct", collection: "products", field: "category", }); }); test("mongoDistinctToQueryResult names the column after the field", () => { assert.deepEqual(mongoDistinctToQueryResult("category", ["books", "toys", null], 4), { columns: ["category"], rows: [["books"], ["toys"], [null]], affected_rows: 3, execution_time_ms: 4, }); // ObjectId values are displayed, not dumped as raw extended JSON. assert.deepEqual(mongoDistinctToQueryResult("owner", [{ $oid: "507f1f77bcf86cd799439011" }], 0).rows, [["507f1f77bcf86cd799439011"]]); }); test("parseMongoGetIndexesCommand parses collection index commands", () => { assert.deepEqual(parseMongoGetIndexesCommand("db.web_log.getIndexes();"), { collection: "web_log", }); assert.deepEqual(parseMongoGetIndexesCommand('db.getCollection("audit.logs").getIndexes()'), { collection: "audit.logs", }); assert.equal(parseMongoGetIndexesCommand("db.web_log.getIndexes({})"), null); }); test("parseMongoCollectionStatsCommand parses collection stats commands", () => { assert.deepEqual(parseMongoCollectionStatsCommand("db.users.stats()"), { collection: "users", metric: "stats", }); assert.deepEqual(parseMongoCollectionStatsCommand("db.users.dataSize();"), { collection: "users", metric: "dataSize", }); assert.deepEqual(parseMongoCollectionStatsCommand("db.users.storageSize(1024)"), { collection: "users", metric: "storageSize", scale: 1024, }); assert.deepEqual(parseMongoCollectionStatsCommand("db.users.totalIndexSize()"), { collection: "users", metric: "totalIndexSize", }); assert.deepEqual(parseMongoCollectionStatsCommand('db.getCollection("audit.logs").stats()'), { collection: "audit.logs", metric: "stats", }); // A non-numeric argument is rejected rather than silently ignored. assert.equal(parseMongoCollectionStatsCommand('db.users.storageSize("big")'), null); }); test("parseMongoCommand tags collection stats commands with the collectionStats kind", () => { const parsed = parseMongoCommand("db.users.stats()"); assert.ok(parsed); assert.deepEqual(parsed.command, { kind: "collectionStats", collection: "users", metric: "stats" }); }); test("mongoCollectionStatsToQueryResult formats stats and single-metric results", () => { const stats = { count: 12, size: 4096, avgObjSize: 341, storageSize: 8192, totalIndexSize: 2048, nindexes: 3, }; assert.deepEqual(mongoCollectionStatsToQueryResult("stats", stats, 5), { columns: ["count", "size", "avgObjSize", "storageSize", "totalIndexSize", "nindexes"], rows: [[12, 4096, 341, 8192, 2048, 3]], affected_rows: 1, execution_time_ms: 5, }); assert.deepEqual(mongoCollectionStatsToQueryResult("dataSize", stats, 0), { columns: ["dataSize"], rows: [[4096]], affected_rows: 1, execution_time_ms: 0, }); assert.deepEqual(mongoCollectionStatsToQueryResult("totalIndexSize", {}, 0), { columns: ["totalIndexSize"], rows: [[null]], affected_rows: 1, execution_time_ms: 0, }); }); test("splitMongoCommands keeps semicolon-separated mongo commands in order", () => { const commands = splitMongoCommands(` db.users.insertOne({ name: "A" }); db.users.insertOne({ name: "B" }); `); assert.deepEqual( commands.map(({ text, command }) => ({ kind: command.kind, text })), [ { kind: "insert", text: 'db.users.insertOne({ name: "A" })' }, { kind: "insert", text: 'db.users.insertOne({ name: "B" })' }, ], ); }); test("splitMongoCommands splits top-level line starts without semicolons", () => { const commands = splitMongoCommands(` use accounting db.getCollection("entries") .find({ status: "open" }) .limit(5) `); assert.deepEqual( commands.map(({ text, command }) => ({ kind: command.kind, text })), [ { kind: "use", text: "use accounting" }, { kind: "find", text: 'db.getCollection("entries")\n .find({ status: "open" })\n .limit(5)' }, ], ); }); test("splitMongoCommandRanges preserve document offsets for newline-separated commands", () => { const source = ` use accounting db.getCollection("entries") .find({ status: "open" }) .limit(5) `; const commands = splitMongoCommandRanges(source); assert.deepEqual( commands.map(({ from, to, text, command }) => ({ from, to, text, kind: command.kind, })), [ { from: source.indexOf("use accounting"), to: source.indexOf("use accounting") + "use accounting".length, text: "use accounting", kind: "use", }, { from: source.indexOf('db.getCollection("entries")'), to: source.indexOf(" .limit(5)") + " .limit(5)".length, text: 'db.getCollection("entries")\n .find({ status: "open" })\n .limit(5)', kind: "find", }, ], ); }); test("evaluateMongoAggregateSafety follows the DBX-managed MCP permission level", () => { const out = parseMongoAggregateCommand('db.products.aggregate([{"$out":"products_copy"}])'); assert.ok(out); assert.equal(mongoAggregateWriteStage(out.pipeline), "$out"); assert.match(evaluateMongoAggregateSafety(out, {}).reason || "", /DBX MCP read-only policy/i); const merge = parseMongoAggregateCommand('db.products.aggregate([{"$merge":{"into":"products_copy"}}])'); assert.ok(merge); assert.equal(mongoAggregateWriteStage(merge.pipeline), "$merge"); assert.match(evaluateMongoAggregateSafety(merge, { allowWrites: true }).reason || "", /high-risk operations.*DBX MCP settings/i); assert.equal(evaluateMongoAggregateSafety(merge, { allowWrites: true, allowDangerous: true }).allowed, true); }); test("mongoIndexesToQueryResult formats index metadata", () => { assert.deepEqual( mongoIndexesToQueryResult( [ { name: "_id_", columns: ["_id"], is_unique: false, is_primary: true, index_type: "_id: 1", filter: null, }, ], 7, ), { columns: ["name", "columns", "unique", "primary", "type", "filter"], rows: [["_id_", "_id", false, true, "_id: 1", null]], affected_rows: 1, execution_time_ms: 7, }, ); }); test("mongoCountToQueryResult returns a single count row", () => { assert.deepEqual(mongoCountToQueryResult(42, 5), { columns: ["count"], rows: [[42]], affected_rows: 42, execution_time_ms: 5, }); }); test("mongoDocumentsToQueryResult turns mongo documents into grid rows", () => { const result = mongoDocumentsToQueryResult( [ { _id: "1", name: "Ada", profile: { role: "admin" } }, { _id: "2", active: true, name: "Lin" }, ], 5, 12, ); assert.deepEqual(result.columns, ["_id", "name", "profile", "active"]); assert.deepEqual(result.rows, [ ["1", "Ada", mongoDocumentGridValue({ role: "admin" }), null], ["2", "Lin", null, true], ]); assert.deepEqual(result.mongo_documents, [ { _id: "1", name: "Ada", profile: { role: "admin" } }, { _id: "2", active: true, name: "Lin" }, ]); assert.equal(result.affected_rows, 12); assert.equal(result.execution_time_ms, 5); assert.equal(result.truncated, true); }); test("mongoDocumentsToQueryResult displays array structures with shell-style BSON literals", () => { const document = { _id: "1", history: [{ at: 'ISODate("2026-06-10T13:59:31.287Z")', by: { $oid: "507f1f77bcf86cd799439011" } }], }; const result = mongoDocumentsToQueryResult([document], 5, 1); const historyCell = result.rows[0]?.[1]; assert.equal(typeof historyCell, "string"); assert.equal(mongoDocumentGridDisplayText(historyCell), '[{"at": ISODate("2026-06-10T13:59:31.287Z"), "by": ObjectId("507f1f77bcf86cd799439011")}]'); assert.equal(mongoDocumentGridEditorText(historyCell), JSON.stringify(document.history)); }); test("mongoDocumentsToQueryResult keeps aligned extended documents for copying", () => { const documents = [{ _id: { $oid: "6743e4bfa3f6f84bc3fff6c8" }, createdAt: 'ISODate("2026-07-24T00:00:00Z")' }]; const copyDocuments = [{ _id: { $oid: "6743e4bfa3f6f84bc3fff6c8" }, createdAt: { $date: "2026-07-24T00:00:00Z" } }]; const result = mongoDocumentsToQueryResult(documents, 5, 1, copyDocuments); assert.deepEqual(result.mongo_documents, documents); assert.deepEqual(result.mongo_copy_documents, copyDocuments); assert.equal(mongoDocumentsToQueryResult(documents, 5, 1, []).mongo_copy_documents, undefined); }); test("mongoDroppedIndexesToQueryResult exposes partial failures", () => { const result = mongoDroppedIndexesToQueryResult(["email_1"], 5, [{ name: "missing_1", message: "index not found" }]); assert.deepEqual(result.columns, ["name", "status", "message"]); assert.deepEqual(result.rows, [ ["email_1", "dropped", null], ["missing_1", "failed", "index not found"], ]); assert.equal(result.affected_rows, 1); }); test("mongoDocumentsToQueryResult preserves an inexact total marker", () => { const result = mongoDocumentsToQueryResult([{ _id: "1" }], 5, 10_000_000, undefined, false); assert.equal(result.total_is_exact, false); assert.equal(result.affected_rows, 10_000_000); assert.equal(result.truncated, true); assert.equal(mongoDocumentsToQueryResult([{ _id: "1" }], 5, 1).total_is_exact, undefined); }); test("mongoDocumentsToQueryResult displays ids without losing raw type metadata", () => { const documents = [ { _id: { $oid: "6743e4bfa3f6f84bc3fff6c8" }, name: "object id" }, { _id: { $numberLong: "2048938405781032962" }, name: "int64" }, { _id: 42, name: "int" }, { _id: 42.5, name: "double" }, { _id: "customer-42", name: "string" }, ]; const result = mongoDocumentsToQueryResult(documents, documents.length, documents.length); assert.deepEqual(result.rows, [ ["6743e4bfa3f6f84bc3fff6c8", "object id"], ["2048938405781032962", "int64"], [42, "int"], [42.5, "double"], ["customer-42", "string"], ]); assert.deepEqual(result.mongo_documents, documents); }); test("buildMongoUpdateDocument ignores _id and preserves typed values", () => { const changes = new Map([ [0, "other-id"], [1, "42"], [2, '{"role":"admin"}'], [3, null], ]); const update = buildMongoDocumentUpdate(changes, ["_id", "age", "profile", "nickname"]); assert.deepEqual(update, { $set: { age: 42, profile: { role: "admin" }, }, $unset: { nickname: "", }, }); assert.equal(formatMongoDocumentShellLiteral(update), '{"$set":{"age":42,"profile":{"role":"admin"}},"$unset":{"nickname":""}}'); });