summaryrefslogtreecommitdiff
path: root/jstests/core/validate_db_metadata_command.js
blob: d1def3666ff38288e158d77c70f799646507dcd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
 * Tests the validateDBMetaData commands with various input parameters.
 * @tags: [
 *   requires_fcv_49
 * ]
 */
(function() {
"use strict";

load("jstests/libs/fixture_helpers.js");             // For FixtureHelpers.
load("jstests/core/timeseries/libs/timeseries.js");  // For TimeseriesTest.

const dbName = jsTestName();

const testDB = db.getSiblingDB(dbName);
assert.commandWorked(testDB.dropDatabase());
const coll1 = testDB.coll1;

// Drop all the unstable data that the other tests might have created. This will ensure that the
// validateDBMetadata command is validating only the data generated by this test.
(function dropAllUnstableData() {
    const listDBRes = assert.commandWorked(db.adminCommand({listDatabases: 1, nameOnly: true}));
    for (let listDBOutput of listDBRes.databases) {
        // Skip non-user databases.
        if (Array.contains(["admin", "config", "local", "$external"], listDBOutput.name)) {
            continue;
        }
        const currentDB = db.getSiblingDB(listDBOutput.name);
        for (let collInfo of currentDB.getCollectionInfos()) {
            if (collInfo.type == "collection" && !collInfo.name.startsWith("system")) {
                assert.commandWorked(
                    currentDB.runCommand({dropIndexes: collInfo.name, index: "*"}));
            }
        }
    }
})();

// Verify that the 'apiParameters' field is required.
const res = assert.commandFailedWithCode(testDB.runCommand({validateDBMetadata: 1}), 40414);

function validate({dbName, coll, apiStrict, error}) {
    dbName = dbName ? dbName : null;
    coll = coll ? coll : null;
    const res = assert.commandWorked(testDB.runCommand({
        validateDBMetadata: 1,
        db: dbName,
        collection: coll,
        apiParameters: {version: "1", strict: apiStrict}
    }));

    assert(res.apiVersionErrors);
    const foundError = res.apiVersionErrors.length > 0;

    // Verify that 'apiVersionErrors' is not empty when 'error' is true, and vice versa.
    assert((!error && !foundError) || (error && foundError), res);

    if (error) {
        for (let apiError of res.apiVersionErrors) {
            assert(apiError.ns);
            if (error.code) {
                assert.eq(apiError.code, error.code);
            }

            if (FixtureHelpers.isMongos(testDB)) {
                // Check that every error has an additional 'shard' field on sharded clusters.
                assert(apiError.shard);
            }
        }
    }
}

//
// Tests for indexes.
//
assert.commandWorked(coll1.createIndex({p: "text"}));

validate({apiStrict: false});

// All dbs but different collection name.
validate({coll: "coll2", apiStrict: true});

// Different db, and collection which has unstable index should not error.
validate({dbName: "new", coll: "coll1", apiStrict: true});
validate({
    dbName: "new",
    apiStrict: true,
});

// Cases where the command returns an error.
validate({apiStrict: true, error: true});
validate({coll: "coll1", apiStrict: true, error: true});
validate({
    dbName: testDB.getName(),
    coll: "coll1",
    apiStrict: true,
    error: {code: ErrorCodes.APIStrictError}
});
validate({dbName: testDB.getName(), apiStrict: true, error: true});

//
// Tests for views.
//
assert.commandWorked(coll1.dropIndexes());
validate({apiStrict: true});

// Create a view which uses unstable expression and verify that validateDBMetadata commands throws
// an assertion.
const view =
    testDB.createView("view1", "coll2", [{$project: {v: {$_testApiVersion: {unstable: true}}}}]);

validate({apiStrict: true, error: true});
validate({dbName: dbName, apiStrict: true, error: true});

validate({dbName: "otherDB", apiStrict: true});
validate({dbName: dbName, coll: "coll", apiStrict: true});

// With view name in the input.
validate({coll: "view1", apiStrict: true, error: {code: ErrorCodes.APIStrictError}});
validate(
    {dbName: dbName, coll: "view1", apiStrict: true, error: {code: ErrorCodes.APIStrictError}});

validate({dbName: "new", coll: "view1", apiStrict: true});

// Collection named same as the view name in another db.
const testDB2 = db.getSiblingDB("testDB2");
const collWithViewName = testDB2.view1;
validate({coll: "view1", apiStrict: true, error: {code: ErrorCodes.APIStrictError}});

//
// Tests for validator.
//
assert.commandWorked(testDB.dropDatabase());

assert.commandWorked(testDB.createCollection(
    "validatorColl", {validator: {$expr: {$_testApiVersion: {unstable: true}}}}));

validate({apiStrict: true, error: true});

// Drop the collection with validation rules. By not using the 'coll.drop()' shell helper, we can
// avoid implicit collection creation in certain passthrough suites. This should increase the
// coverage of this test on sharded clusters.
assert.commandWorked(testDB.runCommand({drop: "validatorColl"}));

//
// Validates the metadata across all the databases and collections after creating a time-series
// collection if time-series collection feature flag is enabled.
//
(function maybeValidateWithTimeseriesCollection() {
    if (!TimeseriesTest.timeseriesCollectionsEnabled(db.getMongo())) {
        jsTestLog("Cannot validate metadata with timeseries collection, feature flag is disabled");
        return;
    }

    const coll = "timeseriesCollectionMetaDataValidation";
    assert.commandWorked(
        testDB.createCollection(coll, {timeseries: {timeField: "time", metaField: "meta"}}));
    validate({apiStrict: true});
}());
}());