diff --git a/test/functional/apm.test.js b/test/functional/apm.test.js index 957ba615f3f..6902f94c9b1 100644 --- a/test/functional/apm.test.js +++ b/test/functional/apm.test.js @@ -9,6 +9,7 @@ const { const { loadSpecTests } = require('../spec'); const { expect } = require('chai'); const { ReadPreference } = require('../../src/read_preference'); +const { runUnifiedTest } = require('./unified-spec-runner/runner'); describe('APM', function () { before(function () { @@ -695,7 +696,7 @@ describe('APM', function () { }); }); - describe('spec tests', function () { + describe('command monitoring spec tests', function () { before(function () { return setupDatabase(this.configuration); }); @@ -926,7 +927,7 @@ describe('APM', function () { }); } - loadSpecTests('apm').forEach(scenario => { + loadSpecTests('command-monitoring/legacy').forEach(scenario => { if (scenario.name === 'command') return; // FIXME(NODE-3074): remove when `count` spec tests have been fixed describe(scenario.name, function () { scenario.tests.forEach(test => { @@ -964,4 +965,30 @@ describe('APM', function () { }); }); }); + + describe('command monitoring unified spec tests', () => { + for (const loadedSpec of loadSpecTests('command-monitoring/unified')) { + expect(loadedSpec).to.include.all.keys(['description', 'tests']); + // TODO: NODE-3356 unskip redaction tests + const testsToSkip = + loadedSpec.description === 'redacted-commands' + ? loadedSpec.tests + .map(test => test.description) + .filter( + description => + description !== 'hello without speculative authenticate is not redacted' + ) + : []; + context(String(loadedSpec.description), function () { + for (const test of loadedSpec.tests) { + it(String(test.description), { + metadata: { sessions: { skipLeakTests: true } }, + test: async function () { + await runUnifiedTest(this, loadedSpec, test, testsToSkip); + } + }); + } + }); + } + }); }); diff --git a/test/spec/apm/README.rst b/test/spec/command-monitoring/README.rst similarity index 95% rename from test/spec/apm/README.rst rename to test/spec/command-monitoring/README.rst index 9f0562364ad..5700409cf32 100644 --- a/test/spec/apm/README.rst +++ b/test/spec/command-monitoring/README.rst @@ -12,6 +12,9 @@ Command Monitoring Testing ======= +Tests in ``unified`` are implemented in the `Unified Test Format <../../unified-test-format/unified-test-format.rst>`__ and require +schema version 1.0. Tests in ``legacy`` should be run as described below. + Tests are provided in YML and JSON format to assert proper upconversion of commands. Database and Collection Names diff --git a/test/spec/apm/bulkWrite.json b/test/spec/command-monitoring/legacy/bulkWrite.json similarity index 100% rename from test/spec/apm/bulkWrite.json rename to test/spec/command-monitoring/legacy/bulkWrite.json diff --git a/test/spec/apm/bulkWrite.yml b/test/spec/command-monitoring/legacy/bulkWrite.yml similarity index 100% rename from test/spec/apm/bulkWrite.yml rename to test/spec/command-monitoring/legacy/bulkWrite.yml diff --git a/test/spec/apm/command.json b/test/spec/command-monitoring/legacy/command.json similarity index 100% rename from test/spec/apm/command.json rename to test/spec/command-monitoring/legacy/command.json diff --git a/test/spec/apm/command.yml b/test/spec/command-monitoring/legacy/command.yml similarity index 100% rename from test/spec/apm/command.yml rename to test/spec/command-monitoring/legacy/command.yml diff --git a/test/spec/apm/deleteMany.json b/test/spec/command-monitoring/legacy/deleteMany.json similarity index 100% rename from test/spec/apm/deleteMany.json rename to test/spec/command-monitoring/legacy/deleteMany.json diff --git a/test/spec/apm/deleteMany.yml b/test/spec/command-monitoring/legacy/deleteMany.yml similarity index 100% rename from test/spec/apm/deleteMany.yml rename to test/spec/command-monitoring/legacy/deleteMany.yml diff --git a/test/spec/apm/deleteOne.json b/test/spec/command-monitoring/legacy/deleteOne.json similarity index 100% rename from test/spec/apm/deleteOne.json rename to test/spec/command-monitoring/legacy/deleteOne.json diff --git a/test/spec/apm/deleteOne.yml b/test/spec/command-monitoring/legacy/deleteOne.yml similarity index 100% rename from test/spec/apm/deleteOne.yml rename to test/spec/command-monitoring/legacy/deleteOne.yml diff --git a/test/spec/apm/find.json b/test/spec/command-monitoring/legacy/find.json similarity index 100% rename from test/spec/apm/find.json rename to test/spec/command-monitoring/legacy/find.json diff --git a/test/spec/apm/find.yml b/test/spec/command-monitoring/legacy/find.yml similarity index 100% rename from test/spec/apm/find.yml rename to test/spec/command-monitoring/legacy/find.yml diff --git a/test/spec/apm/insertMany.json b/test/spec/command-monitoring/legacy/insertMany.json similarity index 100% rename from test/spec/apm/insertMany.json rename to test/spec/command-monitoring/legacy/insertMany.json diff --git a/test/spec/apm/insertMany.yml b/test/spec/command-monitoring/legacy/insertMany.yml similarity index 100% rename from test/spec/apm/insertMany.yml rename to test/spec/command-monitoring/legacy/insertMany.yml diff --git a/test/spec/apm/insertOne.json b/test/spec/command-monitoring/legacy/insertOne.json similarity index 100% rename from test/spec/apm/insertOne.json rename to test/spec/command-monitoring/legacy/insertOne.json diff --git a/test/spec/apm/insertOne.yml b/test/spec/command-monitoring/legacy/insertOne.yml similarity index 100% rename from test/spec/apm/insertOne.yml rename to test/spec/command-monitoring/legacy/insertOne.yml diff --git a/test/spec/apm/unacknowledgedBulkWrite.json b/test/spec/command-monitoring/legacy/unacknowledgedBulkWrite.json similarity index 100% rename from test/spec/apm/unacknowledgedBulkWrite.json rename to test/spec/command-monitoring/legacy/unacknowledgedBulkWrite.json diff --git a/test/spec/apm/unacknowledgedBulkWrite.yml b/test/spec/command-monitoring/legacy/unacknowledgedBulkWrite.yml similarity index 100% rename from test/spec/apm/unacknowledgedBulkWrite.yml rename to test/spec/command-monitoring/legacy/unacknowledgedBulkWrite.yml diff --git a/test/spec/apm/updateMany.json b/test/spec/command-monitoring/legacy/updateMany.json similarity index 100% rename from test/spec/apm/updateMany.json rename to test/spec/command-monitoring/legacy/updateMany.json diff --git a/test/spec/apm/updateMany.yml b/test/spec/command-monitoring/legacy/updateMany.yml similarity index 100% rename from test/spec/apm/updateMany.yml rename to test/spec/command-monitoring/legacy/updateMany.yml diff --git a/test/spec/apm/updateOne.json b/test/spec/command-monitoring/legacy/updateOne.json similarity index 100% rename from test/spec/apm/updateOne.json rename to test/spec/command-monitoring/legacy/updateOne.json diff --git a/test/spec/apm/updateOne.yml b/test/spec/command-monitoring/legacy/updateOne.yml similarity index 100% rename from test/spec/apm/updateOne.yml rename to test/spec/command-monitoring/legacy/updateOne.yml diff --git a/test/spec/command-monitoring/unified/redacted-commands.json b/test/spec/command-monitoring/unified/redacted-commands.json new file mode 100644 index 00000000000..3a1d1895d9d --- /dev/null +++ b/test/spec/command-monitoring/unified/redacted-commands.json @@ -0,0 +1,490 @@ +{ + "description": "redacted-commands", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "5.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "command-monitoring-tests" + } + } + ], + "tests": [ + { + "description": "authenticate", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "authenticate", + "command": { + "authenticate": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "authenticate", + "command": { + "authenticate": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "saslStart", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "saslStart", + "command": { + "saslStart": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "saslContinue", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "saslContinue", + "command": { + "saslContinue": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "getnonce", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": "private" + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "getnonce", + "command": { + "getnonce": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "createUser", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createUser", + "command": { + "createUser": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateUser", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "updateUser", + "command": { + "updateUser": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydbgetnonce", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydbsaslstart", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "copydb", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "copydb", + "command": { + "copydb": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "hello with speculative authenticate", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": "private", + "speculativeAuthenticate": "foo" + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": "private", + "speculativeAuthenticate": "foo" + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": "private", + "speculativeAuthenticate": "foo" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "hello without speculative authenticate is not redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": "public" + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": "public" + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": "public" + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "hello", + "command": { + "hello": "public" + } + } + }, + { + "commandStartedEvent": { + "commandName": "ismaster", + "command": { + "ismaster": "public" + } + } + }, + { + "commandStartedEvent": { + "commandName": "isMaster", + "command": { + "isMaster": "public" + } + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/command-monitoring/unified/redacted-commands.yml b/test/spec/command-monitoring/unified/redacted-commands.yml new file mode 100644 index 00000000000..ef2d4cf41ad --- /dev/null +++ b/test/spec/command-monitoring/unified/redacted-commands.yml @@ -0,0 +1,250 @@ +description: "redacted-commands" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + +tests: + - description: "authenticate" + operations: + - name: runCommand + object: *database + arguments: + commandName: authenticate + command: + authenticate: "private" + # Malformed authentication commands will fail against the server, but we + # just want to assert that security-related commands are redacted + # in command monitoring. + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: authenticate + # This only asserts that the command name has been redacted from the published command; + # however, it's unlikely that a driver would redact this but leave other, sensitive fields. + # We cannot simply assert that command is an empty document because it's at root-level, so + # additional fields in the actual document will be permitted. + command: { authenticate: { $$exists: false } } + + - description: "saslStart" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslStart + command: + saslStart: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: saslStart + command: { saslStart: { $$exists: false } } + + - description: "saslContinue" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslContinue + command: + saslContinue: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: saslContinue + command: { saslContinue: { $$exists: false } } + + - description: "getnonce" + operations: + - name: runCommand + object: *database + arguments: + commandName: getnonce + command: + getnonce: "private" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: getnonce + command: { getnonce: { $$exists: false } } + + - description: "createUser" + operations: + - name: runCommand + object: *database + arguments: + commandName: createUser + command: + createUser: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createUser + command: { createUser: { $$exists: false } } + + - description: "updateUser" + operations: + - name: runCommand + object: *database + arguments: + commandName: updateUser + command: + updateUser: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: updateUser + command: { updateUser: { $$exists: false } } + + - description: "copydbgetnonce" + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbgetnonce + command: + copydbgetnonce: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: copydbgetnonce + command: { copydbgetnonce: { $$exists: false } } + + - description: "copydbsaslstart" + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbsaslstart + command: + copydbsaslstart: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: copydbsaslstart + command: { copydbsaslstart: { $$exists: false } } + + - description: "copydb" + operations: + - name: runCommand + object: *database + arguments: + commandName: copydb + command: + copydb: "private" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: copydb + command: { copydb: { $$exists: false } } + + - description: "hello with speculative authenticate" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: "private" + speculativeAuthenticate: "foo" + expectError: + isError: true + - name: runCommand + object: *database + arguments: + commandName: ismaster + command: + ismaster: "private" + speculativeAuthenticate: "foo" + expectError: + isError: true + - name: runCommand + object: *database + arguments: + commandName: isMaster + command: + isMaster: "private" + speculativeAuthenticate: "foo" + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: hello + command: { hello: { $$exists: false } } + - commandStartedEvent: + commandName: ismaster + command: { ismaster: { $$exists: false } } + - commandStartedEvent: + commandName: isMaster + command: { isMaster: { $$exists: false } } + + - description: "hello without speculative authenticate is not redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: "public" + - name: runCommand + object: *database + arguments: + commandName: ismaster + command: + ismaster: "public" + - name: runCommand + object: *database + arguments: + commandName: isMaster + command: + isMaster: "public" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: hello + command: { hello: "public" } + - commandStartedEvent: + commandName: ismaster + command: { ismaster: "public" } + - commandStartedEvent: + commandName: isMaster + command: { isMaster: "public" }