// updatetests.cpp : unit tests relating to update requests // /** * Copyright (C) 2008 10gen 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 #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/client/dbclientcursor.h" #include "mongo/db/db.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/json.h" #include "mongo/db/lasterror.h" #include "mongo/db/ops/update.h" #include "mongo/db/operation_context_impl.h" #include "mongo/dbtests/dbtests.h" namespace UpdateTests { using std::unique_ptr; using std::numeric_limits; using std::string; using std::stringstream; using std::vector; class ClientBase { public: ClientBase() : _client(&_txn) { mongo::LastError::get(_txn.getClient()).reset(); } virtual ~ClientBase() { mongo::LastError::get(_txn.getClient()).reset(); } protected: void insert( const char *ns, BSONObj o ) { _client.insert( ns, o ); } void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) { _client.update( ns, Query( q ), o, upsert ); } bool error() { return !_client.getPrevError().getField( "err" ).isNull(); } OperationContextImpl _txn; DBDirectClient _client; }; class Fail : public ClientBase { public: virtual ~Fail() {} void run() { prep(); ASSERT( !error() ); doIt(); ASSERT( error() ); } protected: const char *ns() { return "unittests.UpdateTests_Fail"; } virtual void prep() { insert( ns(), fromjson( "{a:1}" ) ); } virtual void doIt() = 0; }; class ModId : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{$set:{'_id':4}}" ) ); } }; class ModNonmodMix : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{$set:{a:4},z:3}" ) ); } }; class InvalidMod : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{$awk:{a:4}}" ) ); } }; class ModNotFirst : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{z:3,$set:{a:4}}" ) ); } }; class ModDuplicateFieldSpec : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{$set:{a:4},$inc:{a:1}}" ) ); } }; class IncNonNumber : public Fail { void doIt() { update( ns(), BSONObj(), fromjson( "{$inc:{a:'d'}}" ) ); } }; class PushAllNonArray : public Fail { void doIt() { insert( ns(), fromjson( "{a:[1]}" ) ); update( ns(), BSONObj(), fromjson( "{$pushAll:{a:'d'}}" ) ); } }; class PullAllNonArray : public Fail { void doIt() { insert( ns(), fromjson( "{a:[1]}" ) ); update( ns(), BSONObj(), fromjson( "{$pullAll:{a:'d'}}" ) ); } }; class IncTargetNonNumber : public Fail { void doIt() { insert( ns(), BSON( "a" << "a" ) ); update( ns(), BSON( "a" << "a" ), fromjson( "{$inc:{a:1}}" ) ); } }; class SetBase : public ClientBase { public: ~SetBase() { _client.dropCollection( ns() ); } protected: const char *ns() { return "unittests.updatetests.SetBase"; } }; class SetNum : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << 1 ) ); _client.update( ns(), BSON( "a" << 1 ), BSON( "$set" << BSON( "a" << 4 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << 4 ) ).isEmpty() ); } }; class SetString : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << "b" ) ); _client.update( ns(), BSON( "a" << "b" ), BSON( "$set" << BSON( "a" << "c" ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << "c" ) ).isEmpty() ); } }; class SetStringDifferentLength : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << "b" ) ); _client.update( ns(), BSON( "a" << "b" ), BSON( "$set" << BSON( "a" << "cd" ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << "cd" ) ).isEmpty() ); } }; class SetStringToNum : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << "b" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 5 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << 5 ) ).isEmpty() ); } }; class SetStringToNumInPlace : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << "bcd" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 5.0 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << 5.0 ) ).isEmpty() ); } }; class SetOnInsertFromEmpty : public SetBase { public: void run() { // Try with upsert false first. _client.insert( ns(), BSONObj() /* empty document */); _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), false ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); // Then with upsert true. _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), true ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); } }; class SetOnInsertFromNonExistent : public SetBase { public: void run() { // Try with upsert false first. _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), false ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); // Then with upsert true. _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 1 ) ), true ); ASSERT( !_client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); } }; class SetOnInsertFromNonExistentWithQuery : public SetBase { public: void run() { Query q("{a:1}"); // Try with upsert false first. _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "b" << 1 ) ), false ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); // Then with upsert true. _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "b" << 1 ) ), true ); ASSERT( !_client.findOne( ns(), BSON( "a" << 1 << "b" << 1) ).isEmpty() ); } }; class SetOnInsertFromNonExistentWithQueryOverField : public SetBase { public: void run() { Query q("{a:1}"); // same field that we'll setOnInsert on // Try with upsert false first. _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "a" << 2 ) ), false ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); // Then with upsert true. _client.update( ns(), q, BSON( "$setOnInsert" << BSON( "a" << 2 ) ), true ); ASSERT( !_client.findOne( ns(), BSON( "a" << 2 ) ).isEmpty() ); } }; class SetOnInsertMissingField : public SetBase { public: void run() { BSONObj res = fromjson("{'_id':0, a:1}"); _client.insert( ns(), res ); _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "b" << 1 ) ) ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 ) ).woCompare( res ) == 0 ); } }; class SetOnInsertExisting : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$setOnInsert" << BSON( "a" << 2 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a" << 1 ) ).isEmpty() ); } }; class SetOnInsertMixed : public SetBase { public: void run() { // Try with upsert false first. _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) << "$setOnInsert" << BSON( "b" << 2 ) ), false ); ASSERT( _client.findOne( ns(), BSON( "a" << 1 << "b" << 2 ) ).isEmpty() ); // Then with upsert true. _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) << "$setOnInsert" << BSON( "b" << 2 ) ), true ); ASSERT( !_client.findOne( ns(), BSON( "a" << 1 << "b" << 2 ) ).isEmpty() ); } }; class SetOnInsertMissingParent : public SetBase { public: void run() { // In a mod that uses dontApply, we should be careful not to create a // parent unneccesarily. BSONObj initial = fromjson( "{'_id':0}" ); BSONObj final = fromjson( "{'_id':0, d:1}" ); _client.insert( ns(), initial ); _client.update( ns(), initial, BSON( "$setOnInsert" << BSON( "a.b" << 1 ) << "$set" << BSON( "d" << 1 ) ) ); ASSERT_EQUALS( _client.findOne( ns(), initial ), final ); } }; class ModDotted : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{a:{b:4}}" ) ); _client.update( ns(), Query(), BSON( "$inc" << BSON( "a.b" << 10 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a.b" << 14 ) ).isEmpty() ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 55 ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a.b" << 55 ) ).isEmpty() ); } }; class SetInPlaceDotted : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{a:{b:'cdef'}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "llll" ) ) ); ASSERT( !_client.findOne( ns(), BSON( "a.b" << "llll" ) ).isEmpty() ); } }; class SetRecreateDotted : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:'cdef'}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) ); ASSERT( _client.findOne( ns(), BSON( "a.b" << "lllll" ) ).woCompare( fromjson( "{'_id':0,a:{b:'lllll'}}" ) ) == 0 ); } }; class SetMissingDotted : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) ); ASSERT( _client.findOne( ns(), BSON( "a.b" << "lllll" ) ).woCompare( fromjson( "{'_id':0,a:{b:'lllll'}}" ) ) == 0 ); } }; class SetAdjacentDotted : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{c:4}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << "lllll" ) ) ); ASSERT_EQUALS( mutablebson::unordered( _client.findOne( ns(), BSON( "a.b" << "lllll" ) ) ), mutablebson::unordered( fromjson( "{'_id':0,a:{b:'lllll',c:4}}" ) ) ); } }; class IncMissing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$inc" << BSON( "f" << 3.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,f:3}" ) ) == 0 ); } }; class MultiInc : public SetBase { public: string s() { stringstream ss; unique_ptr cc = _client.query( ns() , Query().sort( BSON( "_id" << 1 ) ) ); bool first = true; while ( cc->more() ) { if ( first ) first = false; else ss << ","; BSONObj o = cc->next(); ss << o["x"].numberInt(); } return ss.str(); } void run() { _client.insert( ns(), BSON( "_id" << 1 << "x" << 1 ) ); _client.insert( ns(), BSON( "_id" << 2 << "x" << 5 ) ); ASSERT_EQUALS( "1,5" , s() ); _client.update( ns() , BSON( "_id" << 1 ) , BSON( "$inc" << BSON( "x" << 1 ) ) ); ASSERT_EQUALS( "2,5" , s() ); _client.update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) ); ASSERT_EQUALS( "3,5" , s() ); _client.update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , false , true ); ASSERT_EQUALS( "4,6" , s() ); } }; class UnorderedNewSet : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "f.g.h" << 3.0 << "f.g.a" << 2.0 ) ) ); ASSERT_EQUALS( mutablebson::unordered( _client.findOne( ns(), Query() ) ), mutablebson::unordered( fromjson( "{'_id':0,f:{g:{a:2,h:3}}}" ) ) ); } }; class UnorderedNewSetAdjacent : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "f.g.h.b" << 3.0 << "f.g.a.b" << 2.0 ) ) ); ASSERT_EQUALS( mutablebson::unordered( _client.findOne( ns(), Query() ) ), mutablebson::unordered( fromjson( "{'_id':0,f:{g:{a:{b:2},h:{b:3}}}}" ) ) ); } }; class ArrayEmbeddedSet : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,z:[4,'b']}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "z.0" << "a" ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,z:['a','b']}" ) ); } }; class AttemptEmbedInExistingNum : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:1}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 1 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:1}" ) ) == 0 ); } }; class AttemptEmbedConflictsWithOtherSet : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 2 << "a.b" << 1 ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0}" ) ); } }; class ModMasksEmbeddedConflict : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:2}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 2 << "a.b" << 1 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:2}}" ) ) == 0 ); } }; class ModOverwritesExistingObject : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:2}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << BSON( "c" << 2 ) ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{c:2}}" ) ) == 0 ); } }; class InvalidEmbeddedSet : public Fail { public: virtual void doIt() { _client.update( ns(), Query(), BSON( "$set" << BSON( "a." << 1 ) ) ); } }; class UpsertMissingEmbedded : public SetBase { public: void run() { _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b" << 1 ) ), true ); ASSERT( !_client.findOne( ns(), QUERY( "a.b" << 1 ) ).isEmpty() ); } }; class Push : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,5]}" ) ); } }; class PushInvalidEltType : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:1}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:1}" ) ) == 0 ); } }; class PushConflictsWithOtherMod : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << 1 ) <<"$push" << BSON( "a" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[1]}" ) ) == 0 ); } }; class PushFromNothing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[5]}" ) ); } }; class PushFromEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[5]}" ) ) == 0 ); } }; class PushInsideNothing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a.b" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:[5]}}" ) ) == 0 ); } }; class CantPushInsideOtherMod : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a" << BSONObj() ) << "$push" << BSON( "a.b" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0}" ) ) == 0 ); } }; class CantPushTwice : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 4 ) << "$push" << BSON( "a" << 5 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:[]}" ) ) == 0 ); } }; class SetEncapsulationConflictsWithExistingType : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b.c" << 4.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 ); } }; class CantPushToParent : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << 4.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 ); } }; class PushEachSimple : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); // { $push : { a : { $each : [ 2, 3 ] } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 << 3 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2,3]}" ) ); } }; class PushEachFromEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); // { $push : { a : { $each : [ 1, 2, 3 ] } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2,3]}" ) ); } }; class PushSliceBelowFull : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); // { $push : { a : { $each : [ 2 ] , $slice : -3 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 ) << "$slice" << -3 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); } }; class PushSliceReachedFullExact : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); // { $push : { a : { $each : [ 2 ] , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); } }; class PushSliceReachedFullWithEach : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1]}" ) ); // { $push : { a : { $each : [ 2 , 3 ] , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 2 << 3 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); } }; class PushSliceReachedFullWithBoth : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ] , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); } }; class PushSliceToZero : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << 0 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[]}" ) ); } }; class PushSliceToZeroFromNothing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); // { $push : { a : { $each : [ 3 ] , $slice : 0 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << 0 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[]}" ) ); } }; class PushSliceFromNothing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); // { $push : { a : { $each : [ 1 , 2 ] , $slice : -3 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 ) << "$slice" << -3 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1,2]}" ) ); } }; class PushSliceLongerThanSliceFromNothing : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0}" ) ); // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); } }; class PushSliceFromEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); // { $push : { a : { $each : [ 1 ] , $slice : -3 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 ) << "$slice" << -3 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[1]}" ) ); } }; class PushSliceLongerThanSliceFromEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[]}" ) ); // { $push : { a : { $each : [ 1 , 2 , 3 ] , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 1 << 2 << 3 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson( "{'_id':0,a:[2,3]}" ) ); } }; class PushSliceTwoFields : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2],b:[3,4]}" ) ); // { $push: { a: { $each: [ 5 ] , $slice : -2 }, { b: $each: [ 6 ] , $slice: -1 } } } BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); BSONObj objB = BSON( "$each" << BSON_ARRAY( 6 ) << "$slice" << -1 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "b" << objB ) ) ); ASSERT_EQUALS( _client.findOne( ns(), Query() ) , fromjson("{'_id':0,a:[2,5],b:[6]}")); } }; class PushSliceAndNormal : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2],b:[3]}" ) ); // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { b : 4 } } BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON("$push" << BSON("a" << objA << "b" << 4))); ASSERT_EQUALS(_client.findOne(ns(), Query()) , fromjson("{'_id':0,a:[2,5],b:[3,4]}")); } }; class PushSliceTwoFieldsConflict : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1],b:[3]}" ) ); // { $push: { a: { $each: [ 5 ] , $slice: -2 } , { a: $each: [ 6 ] , $slice: -1 } } } BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); BSONObj other = BSON( "$each" << BSON_ARRAY( 6 ) << "$slice" << -1 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "a" << other ) ) ); ASSERT(_client.findOne( ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}"))==0); } }; class PushSliceAndNormalConflict : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1],b:[3]}" ) ); // { $push : { a : { $each : [ 5 ] , $slice : -2 } , { a : 4 } } } BSONObj objA = BSON( "$each" << BSON_ARRAY( 5 ) << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << objA << "a" << 4 ) ) ); ASSERT(_client.findOne( ns(), Query()).woCompare(fromjson("{'_id':0,a:[1],b:[3]}"))==0); } }; class PushSliceInvalidEachType : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : 3 , $slice : -2 } } } BSONObj pushObj = BSON( "$each" << 3 << "$slice" << -2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT( _client.findOne(ns(), Query()).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); } }; class PushSliceInvalidSliceType : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ], $slice : [ -2 ] } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << BSON_ARRAY(-2) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); } }; class PushSliceInvalidSliceValue : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ], $slice : 2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << 2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); } }; class PushSliceInvalidSliceDouble : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ], $slice : -2.1 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << -2.1 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); } }; class PushSliceValidSliceDouble : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ], $slice : -2.0 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$slice" << -2.0 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT_EQUALS(_client.findOne(ns(), Query()) , fromjson("{'_id':0,a:[2,3]}")); } }; class PushSliceInvalidSlice : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:[1,2]}" ) ); // { $push : { a : { $each : [ 3 ], $xxxx : 2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY(3) << "$xxxx" << 2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "a" << pushObj ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare(fromjson("{'_id':0,a:[1,2]}")) == 0); } }; // // We'd like to test the ability of $push with $sort in the following sequence of tests. We // try to enumerate all the possibilities of where the final element would come from: the // document, the $push itself, or both. // class PushSortBase : public ClientBase { public: ~PushSortBase() { _client.dropCollection( ns() ); } protected: enum UpdateType { // Sorts ascending and slices the back of the array. TOPK_ASC = 0, // Sorts descending and slices the front of the array. TOPK_DESC = 1, // Sorts ascending and slices the back of the array. BOTTOMK_ASC = 2, // Sorts descending and slices the front of the array. BOTTOMK_DESC = 3 }; const char* ns() { return "unittest.updatetests.PushSortBase"; } void setParams( const BSONArray& fields, const BSONArray& values, const BSONArray& sort, int size ) { _fieldNames = fields; _fieldValues = values; _sortFields = sort; _sliceSize = size; } /** * Generates the update expression portion of an update command given one of the * possible types of update. */ BSONObj getUpdate( int updateType ) { BSONObjBuilder updateBuilder; BSONObjBuilder pushBuilder( updateBuilder.subobjStart( "$push" ) ); BSONObjBuilder fieldBuilder( pushBuilder.subobjStart( "x" ) ); // Builds $each: [ {a:1,b:1,...}, {a:2,b:2,...}, ... ] BSONArrayBuilder eachBuilder( fieldBuilder.subarrayStart( "$each" ) ); BSONObjIterator itVal( _fieldValues ); while ( itVal.more() ) { BSONObjBuilder eachObjBuilder; BSONElement val = itVal.next(); BSONObjIterator itName( _fieldNames ); while ( itName.more() ) { BSONElement name = itName.next(); eachObjBuilder.append( name.String(), val.Int() ); } eachBuilder.append( eachObjBuilder.done() ); } eachBuilder.done(); // Builds $slice portion. fieldBuilder.append( "$slice", updateType < 2 ? -_sliceSize : _sliceSize); // Builds $sort: portion BSONObjBuilder patternBuilder( fieldBuilder.subobjStart( "$sort" ) ); BSONObjIterator itSort( _sortFields ); while ( itSort.more() ) { BSONElement sortField = itSort.next(); patternBuilder.append( sortField.String(), updateType%2 ? -1 : 1 ); } patternBuilder.done(); fieldBuilder.done(); pushBuilder.done(); return updateBuilder.obj(); } void check( BSONObj expected ) { std::cout << expected.toString() << std::endl; std::cout << _client.findOne( ns(), Query() ) << std::endl; ASSERT( _client.findOne( ns(), Query() ).woCompare( expected ) == 0 ); } private: BSONArray _fieldNames; BSONArray _fieldValues; BSONArray _sortFields; int _sliceSize; }; class PushSortBelowFull : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams( BSON_ARRAY( "a" << "b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 3 ); // Generates the four variations below (but for now we're only using negative slice). // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-3, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:3, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1}]}" ) ); BSONObj result; BSONObj expected; switch ( i ) { case TOPK_ASC: case BOTTOMK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: case BOTTOMK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ) ; ASSERT_EQUALS( result, expected ); break; } } } }; class PushSortReachedFullExact : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1}]}" ) ); BSONObj result; BSONObj expected; switch (i) { case TOPK_ASC: case BOTTOMK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: case BOTTOMK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; } } } }; class PushSortReachedFullWithBoth : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}" ) ); BSONObj result; BSONObj expected; switch ( i ) { case TOPK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; } } } }; class PushSortToZero : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 0 ); // Generates the four variations below (but for now we're only using negative slice). // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1,b:1},{a:3,b:3}]}" ) ); BSONObj result; BSONObj expected; _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[]}" ); ASSERT_EQUALS( result, expected ); } } }; class PushSortToZeroFromNothing : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams( BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 ), BSON_ARRAY( "b" ), 0 ); // Generates the four variations below (but for now we're only using negative slice). // TOPK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ {a:2,b:2} ], $slice:0, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0}" ) ); BSONObj result; BSONObj expected; _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[]}" ); ASSERT_EQUALS( result, expected ); } } }; class PushSortFromNothing : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 ), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // = [ {a:2,b:2}, {a:1,b:1} ] // Generates the four variations below // TOPK_ASC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0}" ) ); BSONObj result; BSONObj expected; switch (i) { case TOPK_ASC: case BOTTOMK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: case BOTTOMK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; } } } }; class PushSortLongerThanSliceFromNothing : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 << 3), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] // TOPK_ASC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0}" ) ); BSONObj result; BSONObj expected; switch (i) { case TOPK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; } } } }; class PushSortFromEmpty : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 ), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // = [ {a:2,b:2}, {a:1,b:1} ] // TOPK_ASC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[]}" ) ); BSONObj result; BSONObj expected; switch (i) { case TOPK_ASC: case BOTTOMK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:1,b:1},{a:2,b:2}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: case BOTTOMK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; } } } }; class PushSortLongerThanSliceFromEmpty : public PushSortBase { public: void run() { // With the following parameters // fields in values in // the each array each array field to sort size setParams(BSON_ARRAY( "a"<<"b" ), BSON_ARRAY( 2 << 1 << 3), BSON_ARRAY( "b" ), 2 ); // Generates the four variations below (but for now we're only using negative slice). // = [ {a:2,b:2}, {a:1,b:1}, {a:3,b:3} ] // TOPK_ASC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:1 } } } // TOPK_DESC: $push: { x: { $each: [ ], $slice:-2, $sort: { b:-1 } } } // BOTTOMK_ASC: $push: { x: { $each: [ ], $slice:2, $sort: { b:1 } } } // BOTTOMK_DESC: $push: { x: { $each: [ ], $slice:2, $sort: { b:-1 } } } for ( int i = 0; i < 2; i++ ) { // i < 4 when we have positive $slice _client.dropCollection( ns() ); _client.insert( ns(), fromjson( "{'_id':0,x:[]}" ) ); BSONObj result; BSONObj expected; switch (i) { case TOPK_ASC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:3,b:3}]}" ); ASSERT_EQUALS( result, expected ); break; case TOPK_DESC: _client.update( ns(), Query(), getUpdate(i) ); result = _client.findOne( ns(), Query() ); expected = fromjson( "{'_id':0,x:[{a:2,b:2},{a:1,b:1}]}" ); ASSERT_EQUALS( result, expected ); break; case BOTTOMK_ASC: case BOTTOMK_DESC: // Implement me. break; } } } }; namespace { /** * Comparator between two BSONObjects that takes in consideration only the keys and * direction described in the sort pattern. * * TODO: This was pulled from update_internal.h, we should verify that these tests work * with the new update framework $push sorter. */ struct ProjectKeyCmp { BSONObj sortPattern; ProjectKeyCmp( BSONObj pattern ) : sortPattern( pattern) {} int operator()( const BSONObj& left, const BSONObj& right ) const { BSONObj keyLeft = left.extractFields( sortPattern, true ); BSONObj keyRight = right.extractFields( sortPattern, true ); return keyLeft.woCompare( keyRight, sortPattern ) < 0; } }; } // namespace class PushSortSortMixed { public: void run() { BSONObj objs[3]; objs[0] = fromjson( "{a:1, b:1}" ); objs[1] = fromjson( "{a:3, b:1}" ); objs[2] = fromjson( "{a:2, b:3}" ); vector workArea; for ( int i = 0; i < 3; i++ ) { workArea.push_back( objs[i] ); } sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("b" << 1 << "a" << -1) ) ); ASSERT_EQUALS( workArea[0], objs[1] ); ASSERT_EQUALS( workArea[1], objs[0] ); ASSERT_EQUALS( workArea[2], objs[2] ); } }; class PushSortSortOutOfOrderFields { public: void run() { BSONObj objs[3]; objs[0] = fromjson( "{b:1, a:1}" ); objs[1] = fromjson( "{a:3, b:2}" ); objs[2] = fromjson( "{b:3, a:2}" ); vector workArea; for ( int i = 0; i < 3; i++ ) { workArea.push_back( objs[i] ); } sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("a" << 1 << "b" << 1) ) ); ASSERT_EQUALS( workArea[0], objs[0] ); ASSERT_EQUALS( workArea[1], objs[2] ); ASSERT_EQUALS( workArea[2], objs[1] ); } }; class PushSortSortExtraFields { public: void run() { BSONObj objs[3]; objs[0] = fromjson( "{b:1, c:2, a:1}" ); objs[1] = fromjson( "{c:1, a:3, b:2}" ); objs[2] = fromjson( "{b:3, a:2}" ); vector workArea; for ( int i = 0; i < 3; i++ ) { workArea.push_back( objs[i] ); } sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("a" << 1 << "b" << 1) ) ); ASSERT_EQUALS( workArea[0], objs[0] ); ASSERT_EQUALS( workArea[1], objs[2] ); ASSERT_EQUALS( workArea[2], objs[1] ); } }; class PushSortSortMissingFields { public: void run() { BSONObj objs[3]; objs[0] = fromjson( "{a:2, b:2}" ); objs[1] = fromjson( "{a:1}" ); objs[2] = fromjson( "{a:3, b:3, c:3}" ); vector workArea; for ( int i = 0; i < 3; i++ ) { workArea.push_back( objs[i] ); } sort( workArea.begin(), workArea.end(), ProjectKeyCmp( BSON("b" << 1 << "c" << 1) ) ); ASSERT_EQUALS( workArea[0], objs[1] ); ASSERT_EQUALS( workArea[1], objs[0] ); ASSERT_EQUALS( workArea[2], objs[2] ); } }; class PushSortSortNestedFields { public: void run() { BSONObj objs[3]; objs[0] = fromjson( "{a:{b:{c:2, d:0}}}" ); objs[1] = fromjson( "{a:{b:{c:1, d:2}}}" ); objs[2] = fromjson( "{a:{b:{c:3, d:1}}}" ); vector workArea; for ( int i = 0; i < 3; i++ ) { workArea.push_back( objs[i] ); } sort( workArea.begin(), workArea.end(), ProjectKeyCmp( fromjson( "{'a.b.d':-1}" ) ) ); ASSERT_EQUALS( workArea[0], objs[1] ); ASSERT_EQUALS( workArea[1], objs[2] ); ASSERT_EQUALS( workArea[2], objs[0] ); sort( workArea.begin(), workArea.end(), ProjectKeyCmp( fromjson( "{'a.b':1}" ) ) ); ASSERT_EQUALS( workArea[0], objs[1] ); ASSERT_EQUALS( workArea[1], objs[0] ); ASSERT_EQUALS( workArea[2], objs[2] ); } }; class PushSortInvalidSortPattern : public SetBase { public: void run() { // Sort pattern validation is made during update command checking. Therefore, to // catch bad patterns, we have to write updated that use them. BSONObj expected = fromjson( "{'_id':0,x:[{a:1}, {a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a..d:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( "a..d" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a.:1} } } } pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( "a." << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.b:1} } } } pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( ".b" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {.:1} } } } pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( "." << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {'':1} } } } pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( "" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidEachType : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ 3 ], $slice:-2, $sort : {a:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( 3 ) << "$slice" << -2 << "$sort" << BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidSortType : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : 2} } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << 2 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidSortValue : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:2, $sort : {a:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << 2 << "$sort" << BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidSortDouble : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2.1, $sort : {a:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2.1 << "$sort" << BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortValidSortDouble : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ) ); // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : {a:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2.0 << "$sort" << BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj expected = fromjson( "{'_id':0,x:[{a:2},{a:3}]}" ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidSortSort : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2.0, $sort : [2, 1] } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2.0 << "$sort" << BSON_ARRAY( 2 << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidSortSortOrder : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:2}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:3} ], $slice:-2, $sort : {a:10} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 3 ) ) << "$slice" << -2 << "$sort" << BSON( "a" << 10 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvertedSortAndSlice : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,x:[{a:1},{a:3}]}" ) ); // { $push : { x : { $each : [ {a:2} ], $sort: {a:1}, $slice:-2 } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 2 ) ) << "$sort" << BSON( "a" << 1 ) << "$slice" << -2.0 ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj expected = fromjson( "{'_id':0,x:[{a:2},{a:3}]}" ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class PushSortInvalidDuplicatedSort : public SetBase { public: void run() { BSONObj expected = fromjson( "{'_id':0,x:[{a:1},{a:3}]}" ); _client.insert( ns(), expected ); // { $push : { x : { $each : [ {a:2} ], $sort : {a:1}, $sort: {a:1} } } } BSONObj pushObj = BSON( "$each" << BSON_ARRAY( BSON( "a" << 2 ) ) << "$sort" << BSON( "a" << 1 ) << "$sort" << BSON( "a" << 1 ) ); _client.update( ns(), Query(), BSON( "$push" << BSON( "x" << pushObj ) ) ); BSONObj result = _client.findOne( ns(), Query() ); ASSERT_EQUALS( result, expected ); } }; class CantIncParent : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:4}}" ) ); _client.update( ns(), Query(), BSON( "$inc" << BSON( "a" << 4.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:4}}" ) ) == 0 ); } }; class DontDropEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:{}}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.c" << 4.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:{},c:4}}" ) ) == 0 ); } }; class InsertInEmpty : public SetBase { public: void run() { _client.insert( ns(), fromjson( "{'_id':0,a:{b:{}}}" ) ); _client.update( ns(), Query(), BSON( "$set" << BSON( "a.b.f" << 4.0 ) ) ); ASSERT( _client.findOne( ns(), Query() ).woCompare( fromjson( "{'_id':0,a:{b:{f:4}}}" ) ) == 0 ); } }; class IndexParentOfMod : public SetBase { public: void run() { ASSERT_OK(dbtests::createIndex( &_txn, ns(), BSON( "a" << 1 ) )); _client.insert( ns(), fromjson( "{'_id':0}" ) ); _client.update( ns(), Query(), fromjson( "{$set:{'a.b':4}}" ) ); ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , _client.findOne( ns(), Query() ) ); ASSERT_EQUALS( fromjson( "{'_id':0,a:{b:4}}" ) , _client.findOne( ns(), fromjson( "{'a.b':4}" ) ) ); // make sure the index works } }; class PreserveIdWithIndex : public SetBase { // Not using $set, but base class is still useful public: void run() { _client.insert( ns(), BSON( "_id" << 55 << "i" << 5 ) ); _client.update( ns(), BSON( "i" << 5 ), BSON( "i" << 6 ) ); ASSERT( !_client.findOne( ns(), Query( BSON( "_id" << 55 ) ).hint ( "{\"_id\":1}" ) ).isEmpty() ); } }; class CheckNoMods : public SetBase { public: void run() { _client.update( ns(), BSONObj(), BSON( "i" << 5 << "$set" << BSON( "q" << 3 ) ), true ); ASSERT( error() ); } }; class UpdateMissingToNull : public SetBase { public: void run() { _client.insert( ns(), BSON( "a" << 5 ) ); _client.update( ns(), BSON( "a" << 5 ), fromjson( "{$set:{b:null}}" ) ); ASSERT_EQUALS( jstNULL, _client.findOne( ns(), QUERY( "a" << 5 ) ).getField( "b" ).type() ); } }; /** SERVER-4777 */ class TwoModsWithinDuplicatedField : public SetBase { public: void run() { _client.insert( ns(), BSON( "_id" << 0 << "a" << 1 << "x" << BSONObj() << "x" << BSONObj() << "z" << 5 ) ); _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 ) ) ); ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 << "x" << BSON( "b" << 1 << "c" << 1 ) << "x" << BSONObj() << "z" << 5 ), _client.findOne( ns(), BSONObj() ) ); } }; /** SERVER-4777 */ class ThreeModsWithinDuplicatedField : public SetBase { public: void run() { _client.insert( ns(), BSON( "_id" << 0 << "x" << BSONObj() << "x" << BSONObj() << "x" << BSONObj() ) ); _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 << "x.d" << 1 ) ) ); ASSERT_EQUALS( BSON( "_id" << 0 << "x" << BSON( "b" << 1 << "c" << 1 << "d" << 1 ) << "x" << BSONObj() << "x" << BSONObj() ), _client.findOne( ns(), BSONObj() ) ); } }; class TwoModsBeforeExistingField : public SetBase { public: void run() { _client.insert( ns(), BSON( "_id" << 0 << "x" << 5 ) ); _client.update( ns(), BSONObj(), BSON( "$set" << BSON( "a" << 1 << "b" << 1 << "x" << 10 ) ) ); ASSERT_EQUALS( mutablebson::unordered( BSON( "_id" << 0 << "a" << 1 << "b" << 1 << "x" << 10 ) ), mutablebson::unordered( _client.findOne( ns(), BSONObj() ) ) ); } }; namespace basic { class Base : public ClientBase { protected: virtual const char * ns() = 0; virtual void dotest() = 0; void insert( const BSONObj& o ) { _client.insert( ns() , o ); } void update( const BSONObj& m ) { _client.update( ns() , BSONObj() , m ); } BSONObj findOne() { return _client.findOne( ns() , BSONObj() ); } void test( const char* initial , const char* mod , const char* after ) { test( fromjson( initial ) , fromjson( mod ) , fromjson( after ) ); } void test( const BSONObj& initial , const BSONObj& mod , const BSONObj& after ) { _client.dropCollection( ns() ); insert( initial ); update( mod ); ASSERT_EQUALS( after , findOne() ); _client.dropCollection( ns() ); } public: Base() {} virtual ~Base() { } void run() { _client.dropCollection( ns() ); dotest(); _client.dropCollection( ns() ); } }; class SingleTest : public Base { virtual BSONObj initial() = 0; virtual BSONObj mod() = 0; virtual BSONObj after() = 0; void dotest() { test( initial() , mod() , after() ); } }; class inc1 : public SingleTest { virtual BSONObj initial() { return BSON( "_id" << 1 << "x" << 1 ); } virtual BSONObj mod() { return BSON( "$inc" << BSON( "x" << 2 ) ); } virtual BSONObj after() { return BSON( "_id" << 1 << "x" << 3 ); } virtual const char * ns() { return "unittests.inc1"; } }; class inc2 : public SingleTest { virtual BSONObj initial() { return BSON( "_id" << 1 << "x" << 1 ); } virtual BSONObj mod() { return BSON( "$inc" << BSON( "x" << 2.5 ) ); } virtual BSONObj after() { return BSON( "_id" << 1 << "x" << 3.5 ); } virtual const char * ns() { return "unittests.inc2"; } }; class inc3 : public SingleTest { virtual BSONObj initial() { return BSON( "_id" << 1 << "x" << 537142123123LL ); } virtual BSONObj mod() { return BSON( "$inc" << BSON( "x" << 2 ) ); } virtual BSONObj after() { return BSON( "_id" << 1 << "x" << 537142123125LL ); } virtual const char * ns() { return "unittests.inc3"; } }; class inc4 : public SingleTest { virtual BSONObj initial() { return BSON( "_id" << 1 << "x" << 537142123123LL ); } virtual BSONObj mod() { return BSON( "$inc" << BSON( "x" << 2LL ) ); } virtual BSONObj after() { return BSON( "_id" << 1 << "x" << 537142123125LL ); } virtual const char * ns() { return "unittests.inc4"; } }; class inc5 : public SingleTest { virtual BSONObj initial() { return BSON( "_id" << 1 << "x" << 537142123123LL ); } virtual BSONObj mod() { return BSON( "$inc" << BSON( "x" << 2.0 ) ); } virtual BSONObj after() { return BSON( "_id" << 1 << "x" << 537142123125LL ); } virtual const char * ns() { return "unittests.inc5"; } }; class inc6 : public Base { virtual const char * ns() { return "unittests.inc6"; } virtual BSONObj initial() { return BSONObj(); } virtual BSONObj mod() { return BSONObj(); } virtual BSONObj after() { return BSONObj(); } void dotest() { long long start = numeric_limits::max() - 5; long long max = numeric_limits::max() + 5ll; _client.insert( ns() , BSON( "x" << (int)start ) ); ASSERT( findOne()["x"].type() == NumberInt ); while ( start < max ) { update( BSON( "$inc" << BSON( "x" << 1 ) ) ); start += 1; ASSERT_EQUALS( start , findOne()["x"].numberLong() ); // SERVER-2005 } ASSERT( findOne()["x"].type() == NumberLong ); } }; class bit1 : public Base { const char * ns() { return "unittests.bit1"; } void dotest() { test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "and" << 2 ) ) ) , BSON( "_id" << 1 << "x" << ( 3 & 2 ) ) ); test( BSON( "_id" << 1 << "x" << 1 ) , BSON( "$bit" << BSON( "x" << BSON( "or" << 4 ) ) ) , BSON( "_id" << 1 << "x" << ( 1 | 4 ) ) ); test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "and" << 2 << "or" << 8 ) ) ) , BSON( "_id" << 1 << "x" << ( ( 3 & 2 ) | 8 ) ) ); test( BSON( "_id" << 1 << "x" << 3 ) , BSON( "$bit" << BSON( "x" << BSON( "or" << 2 << "and" << 8 ) ) ) , BSON( "_id" << 1 << "x" << ( ( 3 | 2 ) & 8 ) ) ); } }; class unset : public Base { const char * ns() { return "unittests.unset"; } void dotest() { test( "{_id:1,x:1}" , "{$unset:{x:1}}" , "{_id:1}" ); } }; class setswitchint : public Base { const char * ns() { return "unittests.int1"; } void dotest() { test( BSON( "_id" << 1 << "x" << 1 ) , BSON( "$set" << BSON( "x" << 5.6 ) ) , BSON( "_id" << 1 << "x" << 5.6 ) ); test( BSON( "_id" << 1 << "x" << 5.6 ) , BSON( "$set" << BSON( "x" << 1 ) ) , BSON( "_id" << 1 << "x" << 1 ) ); } }; }; class All : public Suite { public: All() : Suite( "update" ) { } void setupTests() { add< ModId >(); add< ModNonmodMix >(); add< InvalidMod >(); add< ModNotFirst >(); add< ModDuplicateFieldSpec >(); add< IncNonNumber >(); add< PushAllNonArray >(); add< PullAllNonArray >(); add< IncTargetNonNumber >(); add< SetNum >(); add< SetString >(); add< SetStringDifferentLength >(); add< SetStringToNum >(); add< SetStringToNumInPlace >(); add< SetOnInsertFromEmpty >(); add< SetOnInsertFromNonExistent >(); add< SetOnInsertFromNonExistentWithQuery >(); add< SetOnInsertFromNonExistentWithQueryOverField >(); add< SetOnInsertMissingField >(); add< SetOnInsertExisting >(); add< SetOnInsertMixed >(); add< SetOnInsertMissingParent >(); add< ModDotted >(); add< SetInPlaceDotted >(); add< SetRecreateDotted >(); add< SetMissingDotted >(); add< SetAdjacentDotted >(); add< IncMissing >(); add< MultiInc >(); add< UnorderedNewSet >(); add< UnorderedNewSetAdjacent >(); add< ArrayEmbeddedSet >(); add< AttemptEmbedInExistingNum >(); add< AttemptEmbedConflictsWithOtherSet >(); add< ModMasksEmbeddedConflict >(); add< ModOverwritesExistingObject >(); add< InvalidEmbeddedSet >(); add< UpsertMissingEmbedded >(); add< Push >(); add< PushInvalidEltType >(); add< PushConflictsWithOtherMod >(); add< PushFromNothing >(); add< PushFromEmpty >(); add< PushInsideNothing >(); add< CantPushInsideOtherMod >(); add< CantPushTwice >(); add< SetEncapsulationConflictsWithExistingType >(); add< CantPushToParent >(); add< PushEachSimple >(); add< PushEachFromEmpty >(); add< PushSliceBelowFull >(); add< PushSliceReachedFullExact >(); add< PushSliceReachedFullWithEach >(); add< PushSliceReachedFullWithBoth >(); add< PushSliceToZero >(); add< PushSliceToZeroFromNothing >(); add< PushSliceFromNothing >(); add< PushSliceLongerThanSliceFromNothing >(); add< PushSliceFromEmpty >(); add< PushSliceLongerThanSliceFromEmpty >(); add< PushSliceTwoFields >(); add< PushSliceAndNormal >(); add< PushSliceTwoFieldsConflict >(); add< PushSliceAndNormalConflict >(); add< PushSliceInvalidEachType >(); add< PushSliceInvalidSliceType >(); add< PushSliceInvalidSliceValue >(); add< PushSliceInvalidSliceDouble >(); add< PushSliceValidSliceDouble >(); add< PushSliceInvalidSlice >(); add< PushSortBelowFull >(); add< PushSortReachedFullExact >(); add< PushSortReachedFullWithBoth >(); add< PushSortToZero >(); add< PushSortToZeroFromNothing >(); add< PushSortFromNothing >(); add< PushSortLongerThanSliceFromNothing >(); add< PushSortFromEmpty >(); add< PushSortLongerThanSliceFromEmpty >(); add< PushSortSortMixed >(); add< PushSortSortOutOfOrderFields >(); add< PushSortSortExtraFields >(); add< PushSortSortMissingFields >(); add< PushSortSortNestedFields >(); add< PushSortInvalidSortPattern >(); add< PushSortInvalidEachType >(); add< PushSortInvalidSortType >(); add< PushSortInvalidSortValue >(); add< PushSortInvalidSortDouble >(); add< PushSortValidSortDouble >(); add< PushSortInvalidSortSort >(); add< PushSortInvalidSortSortOrder >(); add< PushSortInvertedSortAndSlice >(); add< PushSortInvalidDuplicatedSort >(); add< CantIncParent >(); add< DontDropEmpty >(); add< InsertInEmpty >(); add< IndexParentOfMod >(); add< PreserveIdWithIndex >(); add< CheckNoMods >(); add< UpdateMissingToNull >(); add< TwoModsWithinDuplicatedField >(); add< ThreeModsWithinDuplicatedField >(); add< TwoModsBeforeExistingField >(); add< basic::inc1 >(); add< basic::inc2 >(); add< basic::inc3 >(); add< basic::inc4 >(); add< basic::inc5 >(); add< basic::inc6 >(); add< basic::bit1 >(); add< basic::unset >(); add< basic::setswitchint >(); } }; SuiteInstance myall; } // namespace UpdateTests