/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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/scripting/bson_template_evaluator.h" #include "mongo/db/jsobj.h" #include "mongo/unittest/unittest.h" namespace mongo { using std::string; namespace { void common_rand_tests(string op, BsonTemplateEvaluator* t) { // Test failure when the arguments are not integers BSONObjBuilder builder1; BSONObj randObj = BSON(op << BSON_ARRAY("hello" << "world")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("randField" << randObj), builder1)); // Test failure when operator does not exists BSONObjBuilder builder2; randObj = BSON("#RAND_OP_NOT_EXISTS" << BSON_ARRAY(5 << 0)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusBadOperator, t->evaluate(BSON("randField" << randObj), builder2)); // Test failure when arguments are not correct (max < min) BSONObjBuilder builder3; randObj = BSON(op << BSON_ARRAY(5 << 0)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("randField" << randObj), builder3)); // Test failure when operators are arbitrarily nested // {id: { #op: [ { #op: [0, 5] }, 10] } BSONObjBuilder builder4; BSONObj innerRandObj = BSON(op << BSON_ARRAY(0 << 5)); BSONObj outerRandObj = BSON(op << BSON_ARRAY(innerRandObj << 10)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("randField" << outerRandObj), builder4)); } TEST(BSONTemplateEvaluatorTest, RAND_INT) { BsonTemplateEvaluator t(1234567); int randValue1, randValue2; common_rand_tests("#RAND_INT", &t); // Test success with a single element BSONObjBuilder builder5; BSONObj randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj), builder5)); BSONObj obj5 = builder5.obj(); ASSERT_EQUALS(obj5.nFields(), 1); randValue1 = obj5["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success with two #RAND_INT elements BSONObjBuilder builder6; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "randField2" << randObj), builder6)); // Test success with #RAND_INT as first element BSONObjBuilder builder8; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj << "hello" << "world" << "id" << 1), builder8)); BSONObj obj8 = builder8.obj(); ASSERT_EQUALS(obj8.nFields(), 3); randValue1 = obj8["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success with #RAND_INT as the middle element BSONObjBuilder builder9; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << 1 << "randField" << randObj << "hello" << "world"), builder9)); BSONObj obj9 = builder9.obj(); ASSERT_EQUALS(obj9.nFields(), 3); randValue1 = obj9["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success with #RAND_INT as the first and the last element BSONObjBuilder builder10; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "hello" << "world" << "randField2" << randObj), builder10)); BSONObj obj10 = builder10.obj(); ASSERT_EQUALS(obj10.nFields(), 3); randValue1 = obj10["randField1"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); randValue2 = obj10["randField2"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue2, 1); ASSERT_LESS_THAN(randValue2, 5); // Test success when one of the element is an array BSONObjBuilder builder11; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("testArray" << BSON_ARRAY(0 << 5 << 10 << 20) << "hello" << "world" << "randField" << randObj), builder11)); BSONObj obj11 = builder11.obj(); ASSERT_EQUALS(obj11.nFields(), 3); randValue1 = obj11["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success with a 3rd argument to #RAND_INT BSONObjBuilder builder12; randObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5 << 4)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << randObj), builder12)); BSONObj obj12 = builder12.obj(); ASSERT_EQUALS(obj12.nFields(), 1); randValue1 = obj12["id"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 4); ASSERT_LESS_THAN_OR_EQUALS(randValue1, 16); } TEST(BSONTemplateEvaluatorTest, RAND_INT_PLUS_THREAD) { BsonTemplateEvaluator t(2345678); t.setId(1); int randValue1, randValue2; common_rand_tests("#RAND_INT_PLUS_THREAD", &t); // Test success with a single element BSONObjBuilder builder5; BSONObj randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj), builder5)); BSONObj obj5 = builder5.obj(); ASSERT_EQUALS(obj5.nFields(), 1); randValue1 = obj5["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); // Test success with two #RAND_INT_PLUS_THREAD elements BSONObjBuilder builder6; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "randField2" << randObj), builder6)); // Test success with #RAND_INT_PLUS_THREAD as first element BSONObjBuilder builder8; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj << "hello" << "world" << "id" << 1), builder8)); BSONObj obj8 = builder8.obj(); ASSERT_EQUALS(obj8.nFields(), 3); randValue1 = obj8["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); // Test success with #RAND_INT_PLUS_THREAD as the middle element BSONObjBuilder builder9; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << 1 << "randField" << randObj << "hello" << "world"), builder9)); BSONObj obj9 = builder9.obj(); ASSERT_EQUALS(obj9.nFields(), 3); randValue1 = obj9["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); // Test success with #RAND_INT_PLUS_THREAD as the first and the last element BSONObjBuilder builder10; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "hello" << "world" << "randField2" << randObj), builder10)); BSONObj obj10 = builder10.obj(); ASSERT_EQUALS(obj10.nFields(), 3); BSONObjIterator iter10(obj10); randValue1 = obj10["randField1"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); randValue2 = obj10["randField2"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue2, 5); ASSERT_LESS_THAN(randValue2, 10); // Test success when one of the element is an array BSONObjBuilder builder11; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("testArray" << BSON_ARRAY(0 << 5 << 10 << 20) << "hello" << "world" << "randField" << randObj), builder11)); BSONObj obj11 = builder11.obj(); ASSERT_EQUALS(obj11.nFields(), 3); randValue1 = obj11["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); // Test success with a 3rd argument to #RAND_INT_PLUS_THREAD to confirm its ignored BSONObjBuilder builder12; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5 << 4)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << randObj), builder12)); BSONObj obj12 = builder12.obj(); ASSERT_EQUALS(obj12.nFields(), 1); randValue1 = obj12["id"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN_OR_EQUALS(randValue1, 10); // Test success with a single element for a zero _id BsonTemplateEvaluator t2(3456789); t2.setId(0); BSONObjBuilder builder13; randObj = BSON("#RAND_INT_PLUS_THREAD" << BSON_ARRAY(1 << 5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t2.evaluate(BSON("randField" << randObj), builder13)); BSONObj obj13 = builder13.obj(); ASSERT_EQUALS(obj13.nFields(), 1); randValue1 = obj13["randField"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); } TEST(BSONTemplateEvaluatorTest, SEQ_INT) { std::unique_ptr t(new BsonTemplateEvaluator(131415)); BSONObj seqObj; BSONObj expectedObj; // Error if missing 'step'. BSONObjBuilder builder1; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "start" << 0)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder1)); // Error if missing 'start'. BSONObjBuilder builder2; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "step" << 1)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder2)); // Error if missing 'seq_iq'. BSONObjBuilder builder3; seqObj = BSON("#SEQ_INT" << BSON("start" << 0 << "step" << 1)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder3)); // Error if 'step' is not a number. BSONObjBuilder builder4; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "start" << 0 << "step" << "foo")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder4)); // Error if 'start' is not a number. BSONObjBuilder builder5; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "start" << true << "step" << 1)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder5)); // Error if 'seq_id' is not a number. BSONObjBuilder builder6; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << BSON("foo" << 1) << "start" << 0 << "step" << 1)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder6)); // Error if 'mod' is not a number. BSONObjBuilder builder7; seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "start" << 0 << "step" << 1 << "mod" << "foo")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->evaluate(BSON("seqField" << seqObj), builder7)); // Test an increasing sequence: -4, -2, 0, 2, ... seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 0 << "start" << -4 << "step" << 2)); for (int i = -4; i <= 2; i += 2) { BSONObjBuilder builder8; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("seqField" << seqObj), builder8)); expectedObj = BSON("seqField" << i); ASSERT_EQUALS(0, expectedObj.woCompare(builder8.obj())); } // Test a decreasing sequence: 5, 0, -5, -10 seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 1 << "start" << 5 << "step" << -5)); for (int i = 5; i >= -10; i -= 5) { BSONObjBuilder builder9; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("seqField" << seqObj), builder9)); expectedObj = BSON("seqField" << i); ASSERT_EQUALS(0, expectedObj.woCompare(builder9.obj())); } // Test multiple sequences in the same document. In order for this to // work the two sequences must have different sequence IDs. // // seq_id 2: 0, 1, 2, 3, ... // seq_id 3: 0, -1, -2, -3, ... BSONObj seqObj1 = BSON("#SEQ_INT" << BSON("seq_id" << 2 << "start" << 0 << "step" << 1)); BSONObj seqObj2 = BSON("#SEQ_INT" << BSON("seq_id" << 3 << "start" << 0 << "step" << -1)); BSONObj seqObjFull = BSON("seqField1" << seqObj1 << "seqField2" << seqObj2); for (int i = 0; i <= 3; i++) { BSONObjBuilder builder10; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(seqObjFull, builder10)); expectedObj = BSON("seqField1" << i << "seqField2" << -i); ASSERT_EQUALS(0, expectedObj.woCompare(builder10.obj())); } // Test that the 'unique: true' option correctly puts the ID of the // bson template evaluator into the high order byte of a 64 bit integer. long long evaluatorId = 9; t->setId(evaluatorId); seqObj1 = BSON("#SEQ_INT" << BSON("seq_id" << 4 << "start" << 8 << "step" << 1)); seqObj2 = BSON("#SEQ_INT" << BSON("seq_id" << 5 << "start" << 8 << "step" << 1 << "unique" << true)); // Without 'unique: true'. BSONObjBuilder builder11; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("seqField" << seqObj1), builder11)); expectedObj = BSON("seqField" << 8); ASSERT_EQUALS(0, expectedObj.woCompare(builder11.obj())); // With 'unique: true'. BSONObjBuilder builder12; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("seqField" << seqObj2), builder12)); // The template evaluator id of 9 goes in the high-order byte. long long expectedSeqNum = (evaluatorId << ((sizeof(long long) - 1) * 8)) + 8; expectedObj = BSON("seqField" << expectedSeqNum); ASSERT_EQUALS(0, expectedObj.woCompare(builder12.obj())); // Test a sequence using "mod": 0, 1, 2, 0, 1 seqObj = BSON("#SEQ_INT" << BSON("seq_id" << 6 << "start" << 0 << "step" << 1 << "mod" << 3)); for (int i = 0; i <= 5; i++) { BSONObjBuilder builder13; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->evaluate(BSON("seqField" << seqObj), builder13)); expectedObj = BSON("seqField" << (i % 3)); ASSERT_EQUALS(0, expectedObj.woCompare(builder13.obj())); } // Test that you can't set an id if it is more than 7 bits wide. ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t->setId(127)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t->setId(128)); } TEST(BSONTemplateEvaluatorTest, RAND_STRING) { BsonTemplateEvaluator t(4567890); // Test failure when the arguments to RAND_STRING is not an integer BSONObjBuilder builder1; BSONObj randObj = BSON("#RAND_STRING" << BSON_ARRAY("hello")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("randField" << randObj), builder1)); // Test failure when there is more than 1 argument to RAND_STRING BSONObjBuilder builder2; randObj = BSON("#RAND_STRING" << BSON_ARRAY(2 << 8)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("randField" << randObj), builder2)); // Test failure when length argument to RAND_STRING is 0 BSONObjBuilder builder3; randObj = BSON("#RAND_STRING" << BSON_ARRAY(0)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("randField" << randObj), builder3)); // Test success with a single element BSONObjBuilder builder4; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj), builder4)); BSONObj obj4 = builder4.obj(); ASSERT_EQUALS(obj4.nFields(), 1); ASSERT_EQUALS(obj4.firstElement().str().length(), 5U); // Test success with two #RAND_STRING elements BSONObjBuilder builder5; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "randField2" << randObj), builder5)); BSONObj obj5 = builder5.obj(); ASSERT_EQUALS(obj5.nFields(), 2); BSONObjIterator iter5(obj5); ASSERT_EQUALS(iter5.next().str().length(), 5U); ASSERT_EQUALS(iter5.next().str().length(), 5U); // Test success with #RAND_STRING as the last element BSONObjBuilder builder6; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << 1 << "hello" << "world" << "randField" << randObj), builder6)); BSONObj obj6 = builder6.obj(); ASSERT_EQUALS(obj6.nFields(), 3); BSONObjIterator iter6(obj6); iter6++; ASSERT_EQUALS(iter6.next().str().length(), 5U); // Test success with #RAND_STRING as first element BSONObjBuilder builder7; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << randObj << "hello" << "world" << "id" << 1), builder7)); BSONObj obj7 = builder7.obj(); ASSERT_EQUALS(obj7.nFields(), 3); ASSERT_EQUALS(obj7.firstElement().str().length(), 5U); // Test success with #RAND_STRING as the middle element BSONObjBuilder builder8; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << 1 << "randField" << randObj << "hello" << "world"), builder8)); BSONObj obj8 = builder8.obj(); ASSERT_EQUALS(obj8.nFields(), 3); BSONObjIterator iter8(obj8); iter8++; ASSERT_EQUALS((*iter8).str().length(), 5U); // Test success with #RAND_STRING as the first and the last element BSONObjBuilder builder10; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField1" << randObj << "hello" << "world" << "randField2" << randObj), builder10)); BSONObj obj10 = builder10.obj(); ASSERT_EQUALS(obj10.nFields(), 3); BSONObjIterator iter10(obj10); ASSERT_EQUALS((*iter10).str().length(), 5U); iter10++; ASSERT_EQUALS(iter10.next().str().length(), 5U); // Test success when one of the element is an array BSONObjBuilder builder11; randObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("testArray" << BSON_ARRAY(0 << 5 << 10 << 20) << "hello" << "world" << "randField" << randObj), builder11)); BSONObj obj11 = builder11.obj(); ASSERT_EQUALS(obj11.nFields(), 3); BSONObjIterator iter11(obj11); iter11++; ASSERT_EQUALS(iter11.next().str().length(), 5U); } TEST(BSONTemplateEvaluatorTest, CONCAT) { BsonTemplateEvaluator t(5678901); // Test failure when the arguments to #CONCAT has only one argument BSONObjBuilder builder1; BSONObj concatObj = BSON("#CONCAT" << BSON_ARRAY("hello")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("concatField" << concatObj), builder1)); // Test success when all arguments to #CONCAT are strings BSONObjBuilder builder2; concatObj = BSON("#CONCAT" << BSON_ARRAY("hello" << " " << "world")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("concatField" << concatObj), builder2)); BSONObj obj2 = builder2.obj(); ASSERT_EQUALS(obj2.nFields(), 1); BSONObj expectedObj = BSON("concatField" << "hello world"); ASSERT_BSONOBJ_EQ(obj2, expectedObj); // Test success when some arguments to #CONCAT are integers BSONObjBuilder builder3; concatObj = BSON("#CONCAT" << BSON_ARRAY("F" << 1 << "racing")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("concatField" << concatObj), builder3)); BSONObj obj3 = builder3.obj(); ASSERT_EQUALS(obj3.nFields(), 1); expectedObj = BSON("concatField" << "F1racing"); ASSERT_BSONOBJ_EQ(obj3, expectedObj); // Test success with #CONCAT as first element and last element BSONObjBuilder builder4; concatObj = BSON("#CONCAT" << BSON_ARRAY("hello" << " " << "world")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("concatField1" << concatObj << "middleKey" << 1 << "concatField2" << concatObj), builder4)); BSONObj obj4 = builder4.obj(); ASSERT_EQUALS(obj4.nFields(), 3); expectedObj = BSON("concatField1" << "hello world" << "middleKey" << 1 << "concatField2" << "hello world"); ASSERT_BSONOBJ_EQ(obj4, expectedObj); // Test success when one of the arguments to #CONCAT is an array BSONObjBuilder builder5; concatObj = BSON("#CONCAT" << BSON_ARRAY("hello" << BSON_ARRAY(1 << 10) << "world")); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("concatField" << concatObj), builder5)); BSONObj obj5 = builder5.obj(); ASSERT_EQUALS(obj5.nFields(), 1); expectedObj = BSON("concatField" << "hello[ 1, 10 ]world"); ASSERT_BSONOBJ_EQ(obj5, expectedObj); } TEST(BSONTemplateEvaluatorTest, OID) { BsonTemplateEvaluator t(6789012); BSONObj oidObj = BSON("#OID" << 1); // Error: field must be "_id" BSONObjBuilder builder1; ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("notIdField" << oidObj), builder1)); // Success. BSONObjBuilder builder2; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("_id" << oidObj), builder2)); } TEST(BSONTemplateEvaluatorTest, COMBINED_OPERATORS) { BsonTemplateEvaluator t(7890123); BSONObj randIntObj = BSON("#RAND_INT" << BSON_ARRAY(0 << 5)); BSONObj randStrObj = BSON("#RAND_STRING" << BSON_ARRAY(5)); // Test success when #RAND_INT, and #RAND_STRING are combined BSONObjBuilder builder1; ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randInt" << randIntObj << "randStr" << randStrObj), builder1)); BSONObj obj1 = builder1.obj(); ASSERT_EQUALS(obj1.nFields(), 2); BSONObjIterator iter1(obj1); int randInt = iter1.next().numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randInt, 0); ASSERT_LESS_THAN(randInt, 5); string randStr = iter1.next().str(); ASSERT_EQUALS(randStr.length(), 5U); // Test success when the #CONCAT and #RAND_INT and #RAND_STRING are combined BSONObjBuilder builder2; BSONObj concatObj = BSON("#CONCAT" << BSON_ARRAY(randIntObj << " hello world " << randStrObj)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("concatField" << concatObj), builder2)); BSONObj obj2 = builder2.obj(); ASSERT_EQUALS(obj2.nFields(), 1); // check that the resulting string has a length of 19. // randIntObj.length = 1, " hello world " = 13, randStrObj.length = 5 // so total string length should 1 + 13 + 5 = 19 ASSERT_EQUALS(obj2.firstElement().str().length(), 19U); } // Test #VARIABLE TEST(BSONTemplateEvaluatorTest, VARIABLE) { BsonTemplateEvaluator t(8901234); int value1; // Test failure when the variable has not been set // {id: { #VARIABLE: "foo" } } BSONObjBuilder builder1; BSONObj innerObj = BSON("#VARIABLE" << "foo"); ASSERT_EQUALS(BsonTemplateEvaluator::StatusOpEvaluationError, t.evaluate(BSON("id" << innerObj), builder1)); // Test success when the variable has been set // test2 := 42 // {id: { #VARIABLE: "test2" } } t.setVariable("test2", BSON("test2" << 42).getField("test2")); BSONObjBuilder builder2; innerObj = BSON("#VARIABLE" << "test2"); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << innerObj), builder2)); BSONObj obj2 = builder2.obj(); value1 = obj2["id"].numberInt(); ASSERT_EQUALS(value1, 42); } // Test template recursion and other general features TEST(BSONTemplateEvaluatorTest, NESTING) { BsonTemplateEvaluator t(8901234); int randValue1, randValue2; // Test failure when operators are arbitrarily nested // {id: { #op: [ { #op: [0, 5] }, 10] } BSONObjBuilder builder1; BSONObj innerObj = BSON("#RAND_INT" << BSON_ARRAY(0 << 5)); BSONObj outerObj = BSON("#RAND_INT" << BSON_ARRAY(innerObj << 10)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("randField" << outerObj), builder1)); // Test success when operators are arbitrarily nested // {foo: { bar: { #op: [1, 5] } } } BSONObjBuilder builder2; innerObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); outerObj = BSON("bar" << innerObj); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("foo" << outerObj), builder2)); BSONObj obj2 = builder2.obj(); BSONElement obj2_foo = obj2["foo"]; randValue1 = obj2_foo["bar"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success when operators are arbitrarily nested within multiple elements // {id: { foo: "hi", bar: { baz: { #op, [1, 5] } } } } BSONObjBuilder builder3; innerObj = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); BSONObj bazObj = BSON("baz" << innerObj); outerObj = BSON("foo" << "hi" << "bar" << bazObj); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << outerObj), builder3)); BSONObj obj3 = builder3.obj(); BSONElement obj3_id = obj3["id"]; BSONElement obj3_bar = obj3_id["bar"]; randValue1 = obj3_bar["baz"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); // Test success when operators are arbitrarily nested within multiple elements // {id: { foo: "hi", bar: { #op: [1, 5] }, baz: { baz_a: { #op, [5, 10] }, baz_b: { #op, [10, // 15] }, baz_c: "bye" } } BSONObjBuilder builder4; BSONObj barObj4 = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); BSONObj bazObj4a = BSON("#RAND_INT" << BSON_ARRAY(5 << 10)); BSONObj bazObj4b = BSON("#RAND_INT" << BSON_ARRAY(10 << 15)); BSONObj bazObj4 = BSON("baz_a" << bazObj4a << "baz_b" << bazObj4b << "baz_c" << "bye"); outerObj = BSON("foo" << "hi" << "bar" << barObj4 << "baz" << bazObj4); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(BSON("id" << outerObj), builder4)); BSONObj obj4 = builder4.obj(); BSONElement obj4_id = obj4["id"]; randValue1 = obj4_id["bar"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); BSONElement obj4_baz = obj4_id["baz"]; randValue1 = obj4_baz["baz_a"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 5); ASSERT_LESS_THAN(randValue1, 10); randValue1 = obj4_baz["baz_b"].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 10); ASSERT_LESS_THAN(randValue1, 15); // Test Failure for an invalid op deeper in the template // { op: "let", target: "x", value: {"#NOT_A_VALID_OP": [0, 1000]}} BSONObjBuilder builder5; innerObj = BSON("#NOT_A_VALID_OP" << BSON_ARRAY(0 << 1000)); outerObj = BSON("op" << "let" << "target" << "x" << "value" << innerObj); ASSERT_EQUALS(BsonTemplateEvaluator::StatusBadOperator, t.evaluate(outerObj, builder5)); // Test success for elements in an array that need evaluation // { foo: "hi", bar: [ { #op: [1, 5] }, { #op: [5, 10], { baz: 42 }, 7 ] } BSONObjBuilder builder6; BSONObj elem1 = BSON("#RAND_INT" << BSON_ARRAY(1 << 5)); BSONObj elem2 = BSON("#RAND_INT" << BSON_ARRAY(5 << 10)); BSONObj elem3 = BSON("baz" << 42); outerObj = BSON("foo" << "hi" << "bar" << BSON_ARRAY(elem1 << elem2 << elem3 << 7)); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(outerObj, builder6)); BSONObj obj6 = builder6.obj(); BSONElement obj6_bar = obj6["bar"]; randValue1 = obj6_bar.Obj()[0].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue1, 1); ASSERT_LESS_THAN(randValue1, 5); randValue2 = obj6_bar.Obj()[1].numberInt(); ASSERT_GREATER_THAN_OR_EQUALS(randValue2, 5); ASSERT_LESS_THAN(randValue2, 10); // Test success for elements in an array that need evaluation // { foo: { #op: ["a", "b"]} , bar: "hi" } BSONObjBuilder builder7; innerObj = BSON("#CONCAT" << BSON_ARRAY("a" << "b")); outerObj = BSON("foo" << innerObj << "bar" << "hi"); ASSERT_EQUALS(BsonTemplateEvaluator::StatusSuccess, t.evaluate(outerObj, builder7)); BSONObj obj7 = builder7.obj(); BSONElement obj7_foo = obj7["foo"]; ASSERT_EQUALS(obj7_foo.String(), "ab"); } } // end anonymous namespace } // end namespace mongo