516 lines
22 KiB
JavaScript
516 lines
22 KiB
JavaScript
/*
|
|
* Declaratively-defined tests for views for all database commands. This file contains a map of test
|
|
* definitions as well as code to run them.
|
|
*
|
|
* The example test
|
|
*
|
|
* {
|
|
* command: {insert: "view", documents: [{x: 1}]},
|
|
* expectFailure: true
|
|
* }
|
|
*
|
|
* invokes runCommand with `command` and expects it to fail with an error code specific to views.
|
|
* A test can be an array of subtests as well.
|
|
*
|
|
* Each test or subtest takes the following options:
|
|
*
|
|
* command
|
|
* The command object to pass to db.runCommand(). Each command can assume that there exists a
|
|
* view named "view", built on top of a collection named "collection". A command can also be a
|
|
* function that takes a db handle as argument and handles pass/failure checking internally.
|
|
*
|
|
* isAdminCommand
|
|
* If true, will execute 'command' against the admin db.
|
|
*
|
|
* skip
|
|
* A string that, if present, causes the test runner to skip running this command altogether.
|
|
* The value should be the reason why the test is being skipped. (There is a predefined
|
|
* selection of commonly-used reasons below.)
|
|
*
|
|
* expectFailure
|
|
* If true, assert that the command fails with a views-specific error code. Otherwise, all
|
|
* commands are expected to succeed.
|
|
*
|
|
* setup
|
|
* A function that will be run before the command is executed. It takes a handle to the 'test'
|
|
* database as its single argument.
|
|
*
|
|
* teardown
|
|
* A function that will be run after the command is executed. It takes a handle to the 'test'
|
|
* database as its single argument.
|
|
*
|
|
* skipSharded
|
|
* If true, do not run this command on a mongos.
|
|
*
|
|
* skipStandalone
|
|
* If true, do not run this command on a standalone mongod.
|
|
*/
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
// Pre-written reasons for skipping a test.
|
|
const isAnInternalCommand = "internal command";
|
|
const isUnrelated = "is unrelated";
|
|
|
|
let viewsCommandTests = {
|
|
_configsvrAddShard: {skip: isAnInternalCommand},
|
|
_configsvrAddShardToZone: {skip: isAnInternalCommand},
|
|
_configsvrBalancerStart: {skip: isAnInternalCommand},
|
|
_configsvrBalancerStatus: {skip: isAnInternalCommand},
|
|
_configsvrBalancerStop: {skip: isAnInternalCommand},
|
|
_configsvrCommitChunkMigration: {skip: isAnInternalCommand},
|
|
_configsvrMoveChunk: {skip: isAnInternalCommand},
|
|
_configsvrRemoveShardFromZone: {skip: isAnInternalCommand},
|
|
_configsvrUpdateZoneKeyRange: {skip: isAnInternalCommand},
|
|
_getUserCacheGeneration: {skip: isAnInternalCommand},
|
|
_hashBSONElement: {skip: isAnInternalCommand},
|
|
_isSelf: {skip: isAnInternalCommand},
|
|
_mergeAuthzCollections: {skip: isAnInternalCommand},
|
|
_migrateClone: {skip: isAnInternalCommand},
|
|
_recvChunkAbort: {skip: isAnInternalCommand},
|
|
_recvChunkCommit: {skip: isAnInternalCommand},
|
|
_recvChunkStart: {skip: isAnInternalCommand},
|
|
_recvChunkStatus: {skip: isAnInternalCommand},
|
|
_transferMods: {skip: isAnInternalCommand},
|
|
addShard: {skip: isUnrelated},
|
|
addShardToZone: {skip: isUnrelated},
|
|
aggregate: {command: {aggregate: "view", pipeline: [{$match: {}}]}},
|
|
appendOplogNote: {skip: isUnrelated},
|
|
applyOps: {
|
|
command: {applyOps: [{op: "i", o: {_id: 1}, ns: "test.view"}]},
|
|
expectFailure: true,
|
|
skipSharded: true,
|
|
},
|
|
authSchemaUpgrade: {skip: isUnrelated},
|
|
authenticate: {skip: isUnrelated},
|
|
availableQueryOptions: {skip: isAnInternalCommand},
|
|
balancerStart: {skip: isUnrelated},
|
|
balancerStatus: {skip: isUnrelated},
|
|
balancerStop: {skip: isUnrelated},
|
|
buildInfo: {skip: isUnrelated},
|
|
captrunc: {
|
|
command: {captrunc: "view", n: 2, inc: false},
|
|
expectFailure: true,
|
|
},
|
|
checkShardingIndex: {skip: isUnrelated},
|
|
cleanupOrphaned: {command: {cleanupOrphaned: 1}, skip: "TODO(SERVER-24764)"},
|
|
clone: {skip: "TODO(SERVER-24506)"},
|
|
cloneCollection: {skip: "TODO(SERVER-24506)"},
|
|
cloneCollectionAsCapped: {
|
|
command: {cloneCollectionAsCapped: "view", toCollection: "testcapped", size: 10240},
|
|
expectFailure: true,
|
|
},
|
|
collMod: {command: {collMod: "view", viewOn: "other", pipeline: []}},
|
|
collStats: {command: {collStats: "view"}, skip: "TODO(SERVER-24568)"},
|
|
compact: {command: {compact: "view", force: true}, expectFailure: true, skipSharded: true},
|
|
configureFailPoint: {skip: isUnrelated},
|
|
connPoolStats: {skip: isUnrelated},
|
|
connPoolSync: {skip: isUnrelated},
|
|
connectionStatus: {skip: isUnrelated},
|
|
convertToCapped: {command: {convertToCapped: "view", size: 12345}, expectFailure: true},
|
|
copydb: {skip: "TODO(SERVER-24506)"},
|
|
copydbgetnonce: {skip: isUnrelated},
|
|
copydbsaslstart: {skip: isUnrelated},
|
|
count: {command: {count: "view"}},
|
|
create: {skip: "tested in views/views_creation.js"},
|
|
createIndexes: {
|
|
command: {createIndexes: "view", indexes: [{key: {x: 1}, name: "x_1"}]},
|
|
expectFailure: true,
|
|
},
|
|
createRole: {
|
|
command: {createRole: "testrole", privileges: [], roles: []},
|
|
setup: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
createUser: {
|
|
command: {createUser: "testuser", pwd: "testpass", roles: []},
|
|
setup: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllUsersFromDatabase: 1}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllUsersFromDatabase: 1}));
|
|
}
|
|
},
|
|
currentOp: {skip: isUnrelated},
|
|
currentOpCtx: {skip: isUnrelated},
|
|
dataSize: {
|
|
command: {dataSize: "test.view"},
|
|
expectFailure: true,
|
|
},
|
|
dbHash: {
|
|
command: function(conn) {
|
|
let getHash = function() {
|
|
let cmd = {dbHash: 1};
|
|
let res = conn.runCommand(cmd);
|
|
assert.commandWorked(res, tojson(cmd));
|
|
return res.collections["system.views"];
|
|
};
|
|
// The checksum below should change if we change the views, but not otherwise.
|
|
let hash1 = getHash();
|
|
assert.commandWorked(conn.runCommand({create: "view2", viewOn: "view"}),
|
|
"could not create view 'view2' on 'view'");
|
|
let hash2 = getHash();
|
|
assert.neq(hash1, hash2, "expected hash to change after creating new view");
|
|
assert.commandWorked(conn.runCommand({drop: "view2"}, "problem dropping view2"));
|
|
let hash3 = getHash();
|
|
assert.eq(hash1, hash3, "hash should be the same again after removing 'view2'");
|
|
}
|
|
},
|
|
dbStats: {command: {dbStats: 1}, skip: "TODO(SERVER-24568)"},
|
|
delete: {command: {delete: "view", deletes: [{q: {x: 1}, limit: 1}]}, expectFailure: true},
|
|
diagLogging: {skip: isUnrelated},
|
|
distinct: {command: {distinct: "view", key: "_id"}},
|
|
driverOIDTest: {skip: isUnrelated},
|
|
drop: {command: {drop: "view"}},
|
|
dropAllRolesFromDatabase: {skip: isUnrelated},
|
|
dropAllUsersFromDatabase: {skip: isUnrelated},
|
|
dropDatabase: {command: {dropDatabase: 1}},
|
|
dropIndexes: {command: {dropIndexes: "view"}, expectFailure: true},
|
|
dropRole: {
|
|
command: {dropRole: "testrole"},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createRole: "testrole", privileges: [], roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
dropUser: {skip: isUnrelated},
|
|
emptycapped: {
|
|
command: {emptycapped: "view"},
|
|
expectFailure: true,
|
|
},
|
|
enableSharding: {skip: "Tested as part of shardCollection"},
|
|
eval: {skip: isUnrelated},
|
|
explain: {command: {explain: {count: "view"}}},
|
|
features: {skip: isUnrelated},
|
|
filemd5: {skip: isUnrelated},
|
|
find: {skip: "tested in views/views_find.js & views/views_sharded.js"},
|
|
findAndModify: {
|
|
command: {findAndModify: "view", query: {a: 1}, update: {$set: {a: 2}}},
|
|
expectFailure: true
|
|
},
|
|
flushRouterConfig: {skip: isUnrelated},
|
|
forceerror: {skip: isUnrelated},
|
|
fsync: {skip: isUnrelated},
|
|
fsyncUnlock: {skip: isUnrelated},
|
|
geoNear: {
|
|
command:
|
|
{geoNear: "view", near: {type: "Point", coordinates: [-50, 37]}, spherical: true},
|
|
expectFailure: true
|
|
},
|
|
geoSearch: {
|
|
command: {
|
|
geoSearch: "view",
|
|
search: {},
|
|
near: [-50, 37],
|
|
},
|
|
expectFailure: true
|
|
},
|
|
getCmdLineOpts: {skip: isUnrelated},
|
|
getDiagnosticData: {skip: isUnrelated},
|
|
getLastError: {skip: isUnrelated},
|
|
getLog: {skip: isUnrelated},
|
|
getMore: {skip: "TODO(SERVER-24724)"},
|
|
getParameter: {skip: isUnrelated},
|
|
getPrevError: {skip: isUnrelated},
|
|
getShardMap: {skip: isUnrelated},
|
|
getShardVersion: {
|
|
command: {getShardVersion: "test.view"},
|
|
isAdminCommand: true,
|
|
expectFailure: true,
|
|
skip: "TODO(SERVER-24764)"
|
|
},
|
|
getnonce: {skip: isUnrelated},
|
|
godinsert: {skip: isAnInternalCommand},
|
|
grantPrivilegesToRole: {
|
|
command: {
|
|
grantPrivilegesToRole: "testrole",
|
|
privileges: [{resource: {db: "test", collection: "view"}, actions: ["find"]}]
|
|
},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createRole: "testrole", privileges: [], roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
grantRolesToRole: {
|
|
command: {grantRolesToRole: "testrole", roles: ["read"]},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createRole: "testrole", privileges: [], roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
grantRolesToUser: {
|
|
command: {grantRolesToUser: "testuser", roles: ["read"]},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createUser: "testuser", pwd: "testpass", roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllUsersFromDatabase: 1}));
|
|
},
|
|
skip: "TODO(SERVER-24724)"
|
|
},
|
|
group: {
|
|
command: {group: {ns: "test.view", key: "x", $reduce: function() {}, initial: {}}},
|
|
},
|
|
handshake: {skip: isUnrelated},
|
|
hostInfo: {skip: isUnrelated},
|
|
insert: {command: {insert: "view", documents: [{x: 1}]}, expectFailure: true},
|
|
invalidateUserCache: {skip: isUnrelated},
|
|
isdbgrid: {skip: isUnrelated},
|
|
isMaster: {skip: isUnrelated},
|
|
journalLatencyTest: {skip: isUnrelated},
|
|
killCursors: {skip: "TODO(SERVER-24771)"},
|
|
killOp: {skip: isUnrelated},
|
|
listCollections: {skip: "tested in views/views_creation.js"},
|
|
listCommands: {skip: isUnrelated},
|
|
listDatabases: {skip: isUnrelated},
|
|
listIndexes: {command: {listIndexes: "view"}, expectFailure: true},
|
|
listShards: {skip: isUnrelated},
|
|
lockInfo: {skip: isUnrelated},
|
|
logApplicationMessage: {skip: isUnrelated},
|
|
logRotate: {skip: isUnrelated},
|
|
logout: {skip: isUnrelated},
|
|
makeSnapshot: {skip: isAnInternalCommand},
|
|
mapReduce: {
|
|
command:
|
|
{mapReduce: "view", map: function() {}, reduce: function(key, vals) {}, out: "out"},
|
|
expectFailure: true
|
|
},
|
|
"mapreduce.shardedfinish": {skip: isAnInternalCommand},
|
|
mergeChunks: {
|
|
command: {mergeChunks: "test.view", bounds: [{x: 0}, {x: 10}]},
|
|
isAdminCommand: true,
|
|
skip: "TODO(SERVER-24764) Confirm/add correct mongoS error handling.",
|
|
},
|
|
moveChunk: {
|
|
command: {moveChunk: "test.view"},
|
|
isAdminCommand: true,
|
|
skip: "TODO(SERVER-24764) Confirm/add correct mongoS error handling.",
|
|
},
|
|
movePrimary: {skip: "TODO(SERVER-24764) Add test for views"},
|
|
netstat: {skip: isAnInternalCommand},
|
|
parallelCollectionScan: {command: {parallelCollectionScan: "view"}, expectFailure: true},
|
|
ping: {command: {ping: 1}},
|
|
planCacheClear: {command: {planCacheClear: "view"}, expectFailure: true},
|
|
planCacheClearFilters: {command: {planCacheClearFilters: "view"}, expectFailure: true},
|
|
planCacheListFilters: {command: {planCacheListFilters: "view"}, expectFailure: true},
|
|
planCacheListPlans: {command: {planCacheListPlans: "view"}, expectFailure: true},
|
|
planCacheListQueryShapes:
|
|
{command: {planCacheListQueryShapes: "view"}, expectFailure: true},
|
|
planCacheSetFilter: {command: {planCacheSetFilter: "view"}, expectFailure: true},
|
|
profile: {skip: isUnrelated},
|
|
reIndex: {command: {reIndex: "view"}, expectFailure: true},
|
|
removeShard: {skip: isUnrelated},
|
|
removeShardFromZone: {skip: isUnrelated},
|
|
renameCollection: [
|
|
{
|
|
isAdminCommand: true,
|
|
command: {renameCollection: "test.view", to: "test.otherview"},
|
|
expectFailure: true,
|
|
},
|
|
{
|
|
isAdminCommand: true,
|
|
command: {renameCollection: "test.collection", to: "test.view"},
|
|
expectFailure: ErrorCodes.NamespaceExists,
|
|
}
|
|
],
|
|
repairCursor: {command: {repairCursor: "view"}, expectFailure: true},
|
|
repairDatabase: {command: {repairDatabase: 1}},
|
|
replSetElect: {skip: isUnrelated},
|
|
replSetFreeze: {skip: isUnrelated},
|
|
replSetFresh: {skip: isUnrelated},
|
|
replSetGetConfig: {skip: isUnrelated},
|
|
replSetGetRBID: {skip: isUnrelated},
|
|
replSetGetStatus: {skip: isUnrelated},
|
|
replSetHeartbeat: {skip: isUnrelated},
|
|
replSetInitiate: {skip: isUnrelated},
|
|
replSetMaintenance: {skip: isUnrelated},
|
|
replSetReconfig: {skip: isUnrelated},
|
|
replSetRequestVotes: {skip: isUnrelated},
|
|
replSetStepDown: {skip: isUnrelated},
|
|
replSetStepUp: {skip: isUnrelated},
|
|
replSetSyncFrom: {skip: isUnrelated},
|
|
replSetTest: {skip: isUnrelated},
|
|
replSetUpdatePosition: {skip: isUnrelated},
|
|
resetError: {skip: isUnrelated},
|
|
resync: {skip: isUnrelated},
|
|
revokePrivilegesFromRole: {
|
|
command: {
|
|
revokePrivilegesFromRole: "testrole",
|
|
privileges: [{resource: {db: "test", collection: "view"}, actions: ["find"]}]
|
|
},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createRole: "testrole", privileges: [], roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
revokeRolesFromRole: {skip: isUnrelated},
|
|
revokeRolesFromUser: {skip: isUnrelated},
|
|
rolesInfo: {skip: isUnrelated},
|
|
saslContinue: {skip: isUnrelated},
|
|
saslStart: {skip: isUnrelated},
|
|
serverStatus: {command: {serverStatus: 1}, skip: isUnrelated},
|
|
setCommittedSnapshot: {skip: isAnInternalCommand},
|
|
setParameter: {skip: isUnrelated},
|
|
setShardVersion: {skip: isUnrelated},
|
|
shardCollection: {
|
|
command: {shardCollection: "test.view", key: {_id: 1}},
|
|
setup: function(conn) {
|
|
assert.commandWorked(conn.adminCommand({enableSharding: "test"}));
|
|
},
|
|
skipStandalone: true,
|
|
expectFailure: true,
|
|
isAdminCommand: true,
|
|
skip: "TODO(SERVER-24764) Add view check to mongoS"
|
|
},
|
|
shardConnPoolStats: {skip: isUnrelated},
|
|
shardingState: {skip: isUnrelated},
|
|
shutdown: {skip: isUnrelated},
|
|
sleep: {skip: isUnrelated},
|
|
split: {skip: "TODO(SERVER-24764) Test that split on view fails"},
|
|
splitChunk: {skip: "TODO(SERVER-24764) Test that split on view fails"},
|
|
splitVector: {skip: "TODO(SERVER-24764) Test that split on view fails"},
|
|
stageDebug: {skip: isAnInternalCommand},
|
|
top: {command: {top: "view"}, isAdminCommand: true, skip: "TODO(SERVER-24568)"},
|
|
touch: {
|
|
command: {touch: "view", data: true},
|
|
expectFailure: true,
|
|
},
|
|
unsetSharding: {skip: isAnInternalCommand},
|
|
update: {command: {update: "view", updates: [{q: {x: 1}, u: {x: 2}}]}, expectFailure: true},
|
|
updateRole: {
|
|
command: {
|
|
updateRole: "testrole",
|
|
privileges: [{resource: {db: "test", collection: "view"}, actions: ["find"]}]
|
|
},
|
|
setup: function(conn) {
|
|
assert.commandWorked(
|
|
conn.runCommand({createRole: "testrole", privileges: [], roles: []}));
|
|
},
|
|
teardown: function(conn) {
|
|
assert.commandWorked(conn.runCommand({dropAllRolesFromDatabase: 1}));
|
|
}
|
|
},
|
|
updateUser: {skip: isUnrelated},
|
|
updateZoneKeyRange: {skip: isUnrelated},
|
|
usersInfo: {skip: isUnrelated},
|
|
validate: {command: {validate: "view"}, skip: "TODO(SERVER-24768)"},
|
|
whatsmyuri: {skip: isUnrelated}
|
|
};
|
|
|
|
/**
|
|
* Helper function for failing commands or writes that checks the result 'res' of either.
|
|
* If 'code' is undefined or true, the expected error defaults to CommandNotSupportedOnView.
|
|
* Otherwise it is the numerical value of code. On no error, or wrong error code, the resulting
|
|
* assert includes the message 'msg'.
|
|
*/
|
|
let assertCommandOrWriteFailed = function(res, code, msg) {
|
|
if (code == undefined || code === true)
|
|
code = ErrorCodes.CommandNotSupportedOnView;
|
|
|
|
if (res.writeErrors !== undefined)
|
|
assert.neq(0, res.writeErrors.length, msg);
|
|
else
|
|
assert.commandFailedWithCode(res, code, msg);
|
|
};
|
|
|
|
function runTests(db) {
|
|
// Are we on a mongos?
|
|
var isMaster = db.runCommand("ismaster");
|
|
assert.commandWorked(isMaster);
|
|
var isMongos = (isMaster.msg === "isdbgrid");
|
|
|
|
// Obtain a list of all commands.
|
|
let res = db.runCommand({listCommands: 1});
|
|
assert.commandWorked(res);
|
|
|
|
let commands = Object.keys(res.commands);
|
|
for (let command of commands) {
|
|
let test = viewsCommandTests[command];
|
|
assert(test !== undefined,
|
|
"Coverage failure: must explicitly define a views test for " + command);
|
|
|
|
if (!(test instanceof Array))
|
|
test = [test];
|
|
let subtest_nr = 0;
|
|
for (let subtest of test) {
|
|
// Tests can be explicitly skipped. Print the name of the skipped test, as well as
|
|
// the reason why.
|
|
if (subtest.skip !== undefined) {
|
|
print("Skipping " + command + ": " + subtest.skip);
|
|
continue;
|
|
}
|
|
|
|
let dbHandle = db.getSiblingDB("test");
|
|
let commandHandle = dbHandle;
|
|
|
|
// Skip tests depending on sharding configuration.
|
|
if (subtest.skipSharded && isMongos) {
|
|
print("Skipping " + command + ": not applicable to mongoS");
|
|
continue;
|
|
}
|
|
|
|
if (subtest.skipStandalone && !isMongos) {
|
|
print("Skipping " + command + ": not applicable to mongoD");
|
|
continue;
|
|
}
|
|
|
|
// Perform test setup, and call any additional setup callbacks provided by the test.
|
|
// All tests assume that there exists a view named 'view' that is backed by
|
|
// 'collection'.
|
|
assert.commandWorked(dbHandle.dropDatabase());
|
|
assert.commandWorked(dbHandle.runCommand({create: "view", viewOn: "collection"}));
|
|
assert.writeOK(dbHandle.collection.insert({x: 1}));
|
|
if (subtest.setup !== undefined)
|
|
subtest.setup(dbHandle);
|
|
|
|
// Execute the command. Print the command name for the first subtest, as otherwise
|
|
// it may be hard to figure out what command caused a failure.
|
|
if (!subtest_nr++)
|
|
print("Testing " + command);
|
|
|
|
if (subtest.isAdminCommand)
|
|
commandHandle = db.getSiblingDB("admin");
|
|
|
|
if (subtest.expectFailure)
|
|
assertCommandOrWriteFailed(commandHandle.runCommand(subtest.command),
|
|
subtest.expectFailure,
|
|
tojson(subtest.command));
|
|
else if (subtest.command instanceof Function)
|
|
subtest.command(commandHandle);
|
|
else
|
|
assert.commandWorked(commandHandle.runCommand(subtest.command),
|
|
tojson(subtest.command));
|
|
|
|
if (subtest.teardown !== undefined)
|
|
subtest.teardown(dbHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run tests against mongoD.
|
|
runTests(db.getSiblingDB("test"));
|
|
|
|
// Run tests against mongoS.
|
|
var st = new ShardingTest({shards: 2});
|
|
runTests(st.s.getDB("test"));
|
|
st.stop();
|
|
}());
|