/**
* Copyright (C) 2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/db/catalog/collection_options.h"
#include
#include "mongo/db/json.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
void checkRoundTrip(const CollectionOptions& options1) {
CollectionOptions options2;
options2.parse(options1.toBSON());
ASSERT_BSONOBJ_EQ(options1.toBSON(), options2.toBSON());
}
TEST(CollectionOptions, SimpleRoundTrip) {
CollectionOptions options;
checkRoundTrip(options);
options.capped = true;
options.cappedSize = 10240;
options.cappedMaxDocs = 1111;
checkRoundTrip(options);
options.setNoIdIndex();
options.flags = 5;
checkRoundTrip(options);
}
TEST(CollectionOptions, IsValid) {
CollectionOptions options;
ASSERT_TRUE(options.isValid());
options.storageEngine = fromjson("{storageEngine1: 1}");
ASSERT_FALSE(options.isValid());
}
TEST(CollectionOptions, Validate) {
CollectionOptions options;
ASSERT_OK(options.validate());
options.storageEngine = fromjson("{storageEngine1: 1}");
ASSERT_NOT_OK(options.validate());
}
TEST(CollectionOptions, Validator) {
CollectionOptions options;
ASSERT_NOT_OK(options.parse(fromjson("{validator: 'notAnObject'}")));
ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}")));
ASSERT_BSONOBJ_EQ(options.validator, fromjson("{a: 1}"));
options.validator = fromjson("{b: 1}");
ASSERT_BSONOBJ_EQ(options.toBSON()["validator"].Obj(), fromjson("{b: 1}"));
options.reset();
ASSERT_BSONOBJ_EQ(options.validator, BSONObj());
ASSERT(!options.toBSON()["validator"]);
}
TEST(CollectionOptions, ErrorBadSize) {
ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{capped: true, size: -1}")));
ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{capped: false, size: -1}")));
ASSERT_NOT_OK(CollectionOptions().parse(
BSON("capped" << true << "size" << std::numeric_limits::min())));
ASSERT_NOT_OK(CollectionOptions().parse(BSON("capped" << true << "size" << (1LL << 62))));
ASSERT_NOT_OK(CollectionOptions().parse(
BSON("capped" << true << "size" << std::numeric_limits::max())));
}
TEST(CollectionOptions, ErrorBadMax) {
ASSERT_NOT_OK(CollectionOptions().parse(BSON("capped" << true << "max" << (1LL << 31))));
}
TEST(CollectionOptions, CappedSizeRoundsUpForAlignment) {
const long long kUnalignedCappedSize = 1000;
const long long kAlignedCappedSize = 1024;
CollectionOptions options;
// Check size rounds up to multiple of alignment.
ASSERT_OK(options.parse(BSON("capped" << true << "size" << kUnalignedCappedSize)));
ASSERT_EQUALS(options.capped, true);
ASSERT_EQUALS(options.cappedSize, kAlignedCappedSize);
ASSERT_EQUALS(options.cappedMaxDocs, 0);
}
TEST(CollectionOptions, IgnoreSizeWrongType) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{size: undefined, capped: undefined}")));
ASSERT_EQUALS(options.capped, false);
ASSERT_EQUALS(options.cappedSize, 0);
}
TEST(CollectionOptions, IgnoreMaxWrongType) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{capped: true, size: 1024, max: ''}")));
ASSERT_EQUALS(options.capped, true);
ASSERT_EQUALS(options.cappedSize, 1024);
ASSERT_EQUALS(options.cappedMaxDocs, 0);
}
TEST(CollectionOptions, InvalidStorageEngineField) {
// "storageEngine" field has to be an object if present.
ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{storageEngine: 1}")));
// Every field under "storageEngine" has to be an object.
ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{storageEngine: {storageEngine1: 1}}")));
// Empty "storageEngine" not allowed
ASSERT_OK(CollectionOptions().parse(fromjson("{storageEngine: {}}")));
}
TEST(CollectionOptions, ParseEngineField) {
CollectionOptions opts;
ASSERT_OK(opts.parse(
fromjson("{storageEngine: {storageEngine1: {x: 1, y: 2}, storageEngine2: {a: 1, b:2}}}")));
checkRoundTrip(opts);
BSONObj obj = opts.toBSON();
// Check "storageEngine" field.
ASSERT_TRUE(obj.hasField("storageEngine"));
ASSERT_TRUE(obj.getField("storageEngine").isABSONObj());
BSONObj storageEngine = obj.getObjectField("storageEngine");
// Check individual storage storageEngine fields.
ASSERT_TRUE(storageEngine.getField("storageEngine1").isABSONObj());
BSONObj storageEngine1 = storageEngine.getObjectField("storageEngine1");
ASSERT_EQUALS(1, storageEngine1.getIntField("x"));
ASSERT_EQUALS(2, storageEngine1.getIntField("y"));
ASSERT_TRUE(storageEngine.getField("storageEngine2").isABSONObj());
BSONObj storageEngine2 = storageEngine.getObjectField("storageEngine2");
ASSERT_EQUALS(1, storageEngine2.getIntField("a"));
ASSERT_EQUALS(2, storageEngine2.getIntField("b"));
}
TEST(CollectionOptions, ResetStorageEngineField) {
CollectionOptions opts;
ASSERT_OK(opts.parse(fromjson("{storageEngine: {storageEngine1: {x: 1}}}")));
checkRoundTrip(opts);
opts.reset();
ASSERT_TRUE(opts.storageEngine.isEmpty());
}
TEST(CollectionOptions, ModifyStorageEngineField) {
CollectionOptions opts;
// Directly modify storageEngine field in collection options.
opts.storageEngine = fromjson("{storageEngine1: {x: 1}}");
// Unrecognized field should not be present in BSON representation.
BSONObj obj = opts.toBSON();
ASSERT_FALSE(obj.hasField("unknownField"));
// Check "storageEngine" field.
ASSERT_TRUE(obj.hasField("storageEngine"));
ASSERT_TRUE(obj.getField("storageEngine").isABSONObj());
BSONObj storageEngine = obj.getObjectField("storageEngine");
// Check individual storage storageEngine fields.
ASSERT_TRUE(storageEngine.getField("storageEngine1").isABSONObj());
BSONObj storageEngine1 = storageEngine.getObjectField("storageEngine1");
ASSERT_EQUALS(1, storageEngine1.getIntField("x"));
}
TEST(CollectionOptions, FailToParseCollationThatIsNotAnObject) {
CollectionOptions options;
ASSERT_NOT_OK(options.parse(fromjson("{collation: 'notAnObject'}")));
}
TEST(CollectionOptions, FailToParseCollationThatIsAnEmptyObject) {
CollectionOptions options;
ASSERT_NOT_OK(options.parse(fromjson("{collation: {}}")));
}
TEST(CollectionOptions, CollationFieldParsesCorrectly) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}"));
ASSERT_TRUE(options.isValid());
ASSERT_OK(options.validate());
}
TEST(CollectionOptions, ParsedCollationObjShouldBeOwned) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}"));
ASSERT_TRUE(options.collation.isOwned());
}
TEST(CollectionOptions, ResetClearsCollationField) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
ASSERT_FALSE(options.collation.isEmpty());
options.reset();
ASSERT_TRUE(options.collation.isEmpty());
}
TEST(CollectionOptions, CollationFieldLeftEmptyWhenOmitted) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}")));
ASSERT_TRUE(options.collation.isEmpty());
}
TEST(CollectionOptions, CollationFieldNotDumpedToBSONWhenOmitted) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}")));
ASSERT_TRUE(options.collation.isEmpty());
BSONObj asBSON = options.toBSON();
ASSERT_FALSE(asBSON["collation"]);
}
TEST(CollectionOptions, ViewParsesCorrectly) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{viewOn: 'c', pipeline: [{$match: {}}]}")));
ASSERT_EQ(options.viewOn, "c");
ASSERT_BSONOBJ_EQ(options.pipeline, fromjson("[{$match: {}}]"));
}
TEST(CollectionOptions, ViewParsesCorrectlyWithoutPipeline) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{viewOn: 'c'}")));
ASSERT_EQ(options.viewOn, "c");
ASSERT_BSONOBJ_EQ(options.pipeline, BSONObj());
}
TEST(CollectionOptions, PipelineFieldRequiresViewOn) {
CollectionOptions options;
ASSERT_NOT_OK(options.parse(fromjson("{pipeline: [{$match: {}}]}")));
}
TEST(CollectionOptions, UnknownTopLevelOptionFailsToParse) {
CollectionOptions options;
auto status = options.parse(fromjson("{invalidOption: 1}"));
ASSERT_NOT_OK(status);
ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions);
}
TEST(CollectionOptions, CreateOptionIgnoredIfFirst) {
CollectionOptions options;
auto status = options.parse(fromjson("{create: 1}"));
ASSERT_OK(status);
}
TEST(CollectionOptions, CreateOptionIgnoredIfNotFirst) {
CollectionOptions options;
auto status = options.parse(fromjson("{capped: true, create: 1, size: 1024}"));
ASSERT_OK(status);
ASSERT_EQ(options.capped, true);
ASSERT_EQ(options.cappedSize, 1024L);
}
TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionFirst) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{create: 1, invalidOption: 1}")));
}
TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionPresent) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{invalidOption: 1, create: 1}")));
}
TEST(CollectionOptions, UnknownOptionRejectedIfCreateOptionNotPresent) {
CollectionOptions options;
auto status = options.parse(fromjson("{invalidOption: 1}"));
ASSERT_NOT_OK(status);
ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions);
}
TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionFirst) {
CollectionOptions options;
auto status = options.parse(BSON("create" << 1 << "create" << 1));
ASSERT_OK(status);
}
TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionNotFirst) {
CollectionOptions options;
auto status =
options.parse(BSON("capped" << true << "create" << 1 << "create" << 1 << "size" << 1024));
ASSERT_OK(status);
}
TEST(CollectionOptions, MaxTimeMSWhitelistedOptionIgnored) {
CollectionOptions options;
auto status = options.parse(fromjson("{maxTimeMS: 1}"));
ASSERT_OK(status);
}
TEST(CollectionOptions, WriteConcernWhitelistedOptionIgnored) {
CollectionOptions options;
auto status = options.parse(fromjson("{writeConcern: 1}"));
ASSERT_OK(status);
}
} // namespace mongo