TEST(CollectionOptions, NExtentsNumberLimits) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{$nExtents: 'a'}"))); ASSERT_EQ(options.initialNumExtents, 0); ASSERT_OK(options.parse(fromjson("{$nExtents: '-1'}"))); ASSERT_EQ(options.initialNumExtents, 0); ASSERT_OK(options.parse(fromjson("{$nExtents: '-9999999999999999999999999999999999'}"))); ASSERT_EQ(options.initialNumExtents, 0); ASSERT_OK(options.parse(fromjson("{$nExtents: 9999999999999999999999999999999}"))); ASSERT_EQ(options.initialNumExtents, LLONG_MAX); }
TEST(CollectionOptions, MaxNumberLimits) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{max: 'a'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); ASSERT_OK(options.parse(fromjson("{max: '-1'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); ASSERT_OK(options.parse(fromjson("{max: '-9999999999999999999999999999999999'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); ASSERT_OK(options.parse(fromjson("{max: 99999999999999999999999999999}"))); ASSERT_EQ(options.cappedMaxDocs, 0); }
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}")); CollectionOptions defaultOptions; ASSERT_BSONOBJ_EQ(defaultOptions.validator, BSONObj()); ASSERT(!defaultOptions.toBSON()["validator"]); }
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, 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, 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, CollationFieldParsesCorrectly) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}"))); ASSERT_EQ(options.collation, fromjson("{locale: 'en'}")); ASSERT_TRUE(options.isValid()); ASSERT_OK(options.validate()); }
TEST(CollectionOptions, ParseEngineField) { CollectionOptions opts; ASSERT_OK(opts.parse(fromjson( "{unknownField: 1, " "storageEngine: {storageEngine1: {x: 1, y: 2}, storageEngine2: {a: 1, b:2}}}"))); checkRoundTrip(opts); // 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")); 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, SizeNumberLimits) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{size: 'a'}"))); ASSERT_EQ(options.cappedSize, 0); ASSERT_OK(options.parse(fromjson("{size: '-1'}"))); ASSERT_EQ(options.cappedSize, 0); ASSERT_OK(options.parse(fromjson("{size: '-9999999999999999999999999999999'}"))); ASSERT_EQ(options.cappedSize, 0); // The test for size is redundant since size returns a status that's not ok if it's larger // than a petabyte, which is smaller than LLONG_MAX anyways. We test that here. ASSERT_NOT_OK(options.parse(fromjson("{size: 9999999999999999}"))); }
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); }
CollectionOptions MMAPV1DatabaseCatalogEntry::getCollectionOptions( OperationContext* txn, const StringData& ns ) const { if ( nsToCollectionSubstring( ns ) == "system.namespaces" ) { return CollectionOptions(); } RecordStoreV1Base* rs = _getNamespaceRecordStore(); invariant( rs ); scoped_ptr<RecordIterator> it( rs->getIterator(txn) ); while ( !it->isEOF() ) { DiskLoc loc = it->getNext(); BSONObj entry = it->dataFor( loc ).toBson(); BSONElement name = entry["name"]; if ( name.type() == String && name.String() == ns ) { CollectionOptions options; if ( entry["options"].isABSONObj() ) { Status status = options.parse( entry["options"].Obj() ); fassert( 18523, status ); } return options; } } return CollectionOptions(); }
TEST(CollectionOptions, ResetStorageEngineField) { CollectionOptions opts; ASSERT_OK(opts.parse(fromjson("{storageEngine: {storageEngine1: {x: 1}}}"))); checkRoundTrip(opts); CollectionOptions defaultOpts; ASSERT_TRUE(defaultOpts.storageEngine.isEmpty()); }
TEST(CollectionOptions, ParseUUID) { CollectionOptions options; CollectionUUID uuid = CollectionUUID::gen(); // Check required parse failures ASSERT_FALSE(options.uuid); ASSERT_NOT_OK(options.parse(uuid.toBSON())); ASSERT_NOT_OK(options.parse(BSON("uuid" << 1))); ASSERT_NOT_OK(options.parse(BSON("uuid" << 1), CollectionOptions::parseForStorage)); ASSERT_FALSE(options.uuid); // Check successful parse and roundtrip. ASSERT_OK(options.parse(uuid.toBSON(), CollectionOptions::parseForStorage)); ASSERT(options.uuid.get() == uuid); // Check that a collection options containing a UUID passes validation. ASSERT_OK(options.validateForStorage()); }
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); }
/** { ..., capped: true, size: ..., max: ... } * @param createDefaultIndexes - if false, defers id (and other) index creation. * @return true if successful */ Status userCreateNS( OperationContext* txn, Database* db, StringData ns, BSONObj options, bool logForReplication, bool createDefaultIndexes ) { invariant( db ); LOG(1) << "create collection " << ns << ' ' << options; if ( !NamespaceString::validCollectionComponent(ns) ) return Status( ErrorCodes::InvalidNamespace, str::stream() << "invalid ns: " << ns ); Collection* collection = db->getCollection( ns ); if ( collection ) return Status( ErrorCodes::NamespaceExists, "collection already exists" ); CollectionOptions collectionOptions; Status status = collectionOptions.parse(options); if ( !status.isOK() ) return status; status = validateStorageOptions(collectionOptions.storageEngine, &StorageEngine::Factory::validateCollectionStorageOptions); if ( !status.isOK() ) return status; invariant( db->createCollection( txn, ns, collectionOptions, true, createDefaultIndexes ) ); if ( logForReplication ) { if ( options.getField( "create" ).eoo() ) { BSONObjBuilder b; b << "create" << nsToCollectionSubstring( ns ); b.appendElements( options ); options = b.obj(); } string logNs = nsToDatabase(ns) + ".$cmd"; repl::logOp(txn, "c", logNs.c_str(), options); } return Status::OK(); }
CollectionOptions MMAPV1DatabaseCatalogEntry::getCollectionOptions(OperationContext* txn, RecordId rid) const { CollectionOptions options; if (rid.isNull()) { return options; } RecordStoreV1Base* rs = _getNamespaceRecordStore(); invariant(rs); RecordData data; invariant(rs->findRecord(txn, rid, &data)); if (data.releaseToBson()["options"].isABSONObj()) { Status status = options.parse(data.releaseToBson()["options"].Obj()); fassert(18523, status); } return options; }
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, CollationFieldParsesCorrectly) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}"))); ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}")); ASSERT_OK(options.validateForStorage()); }
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, CollationFieldLeftEmptyWhenOmitted) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}"))); ASSERT_TRUE(options.collation.isEmpty()); }
TEST(CollectionOptions, WriteConcernWhitelistedOptionIgnored) { CollectionOptions options; auto status = options.parse(fromjson("{writeConcern: 1}")); ASSERT_OK(status); }
TEST(CollectionOptions, MaxTimeMSWhitelistedOptionIgnored) { CollectionOptions options; auto status = options.parse(fromjson("{maxTimeMS: 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, DuplicateCreateOptionIgnoredIfCreateOptionFirst) { CollectionOptions options; auto status = options.parse(BSON("create" << 1 << "create" << 1)); ASSERT_OK(status); }
TEST(CollectionOptions, UnknownOptionRejectedIfCreateOptionNotPresent) { CollectionOptions options; auto status = options.parse(fromjson("{invalidOption: 1}")); ASSERT_NOT_OK(status); ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions); }
TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionPresent) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{invalidOption: 1, create: 1}"))); }
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, CreateOptionIgnoredIfFirst) { CollectionOptions options; auto status = options.parse(fromjson("{create: 1}")); ASSERT_OK(status); }
TEST(CollectionOptions, UnknownTopLevelOptionFailsToParse) { CollectionOptions options; auto status = options.parse(fromjson("{invalidOption: 1}")); ASSERT_NOT_OK(status); ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions); }
TEST(CollectionOptions, PipelineFieldRequiresViewOn) { CollectionOptions options; ASSERT_NOT_OK(options.parse(fromjson("{pipeline: [{$match: {}}]}"))); }