diff options
Diffstat (limited to 'src/mongo/db/query/lite_parsed_query_test.cpp')
-rw-r--r-- | src/mongo/db/query/lite_parsed_query_test.cpp | 1914 |
1 files changed, 972 insertions, 942 deletions
diff --git a/src/mongo/db/query/lite_parsed_query_test.cpp b/src/mongo/db/query/lite_parsed_query_test.cpp index eeae7786d6a..e395ca2c87a 100644 --- a/src/mongo/db/query/lite_parsed_query_test.cpp +++ b/src/mongo/db/query/lite_parsed_query_test.cpp @@ -39,956 +39,986 @@ namespace mongo { namespace { - using std::unique_ptr; - using unittest::assertGet; - - TEST(LiteParsedQueryTest, InitSortOrder) { - ASSERT_OK(LiteParsedQuery::makeAsOpQuery("testns", +using std::unique_ptr; +using unittest::assertGet; + +TEST(LiteParsedQueryTest, InitSortOrder) { + ASSERT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 1, + 0, + BSONObj(), + BSONObj(), + fromjson("{a: 1}"), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false) // explain + .getStatus()); +} + +TEST(LiteParsedQueryTest, InitSortOrderString) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", 0, 1, 0, BSONObj(), BSONObj(), + fromjson("{a: \"\"}"), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false) // explain + .getStatus()); +} + +TEST(LiteParsedQueryTest, GetFilter) { + unique_ptr<LiteParsedQuery> lpq(assertGet(LiteParsedQuery::makeAsOpQuery("testns", + 5, + 6, + 9, + BSON("x" << 5), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false))); // explain + + ASSERT_EQUALS(BSON("x" << 5), lpq->getFilter()); +} + +TEST(LiteParsedQueryTest, NumToReturn) { + unique_ptr<LiteParsedQuery> lpq(assertGet(LiteParsedQuery::makeAsOpQuery("testns", + 5, + 6, + 9, + BSON("x" << 5), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false))); // explain + + ASSERT_EQUALS(6, lpq->getBatchSize()); + ASSERT(lpq->wantMore()); +} + +TEST(LiteParsedQueryTest, NumToReturnNegative) { + unique_ptr<LiteParsedQuery> lpq(assertGet(LiteParsedQuery::makeAsOpQuery("testns", + 5, + -6, + 9, + BSON("x" << 5), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false))); // explain + + ASSERT_EQUALS(6, lpq->getBatchSize()); + ASSERT(!lpq->wantMore()); +} + +TEST(LiteParsedQueryTest, MinFieldsNotPrefixOfMax) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), fromjson("{a: 1}"), + fromjson("{b: 1}"), + false, // snapshot + false) // explain + .getStatus()); +} + +TEST(LiteParsedQueryTest, MinFieldsMoreThanMax) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), BSONObj(), BSONObj(), BSONObj(), + fromjson("{a: 1, b: 1}"), + fromjson("{a: 1}"), false, // snapshot false) // explain - .getStatus()); - } - - TEST(LiteParsedQueryTest, InitSortOrderString) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 1, - 0, - BSONObj(), - BSONObj(), - fromjson("{a: \"\"}"), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false) // explain - .getStatus()); - } - - TEST(LiteParsedQueryTest, GetFilter) { - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeAsOpQuery("testns", - 5, - 6, - 9, - BSON("x" << 5), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false))); // explain - - ASSERT_EQUALS(BSON("x" << 5 ), lpq->getFilter()); - } - - TEST(LiteParsedQueryTest, NumToReturn) { - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeAsOpQuery("testns", - 5, - 6, - 9, - BSON("x" << 5), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false))); // explain - - ASSERT_EQUALS(6, lpq->getBatchSize()); - ASSERT(lpq->wantMore()); - } - - TEST(LiteParsedQueryTest, NumToReturnNegative) { - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeAsOpQuery("testns", - 5, - -6, - 9, - BSON("x" << 5), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false))); // explain - - ASSERT_EQUALS(6, lpq->getBatchSize()); - ASSERT(!lpq->wantMore()); - } - - TEST(LiteParsedQueryTest, MinFieldsNotPrefixOfMax) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - fromjson("{a: 1}"), - fromjson("{b: 1}"), - false, // snapshot - false) // explain - .getStatus()); - } - - TEST(LiteParsedQueryTest, MinFieldsMoreThanMax) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - fromjson("{a: 1, b: 1}"), - fromjson("{a: 1}"), - false, // snapshot - false) // explain - .getStatus()); - } - - TEST(LiteParsedQueryTest, MinFieldsLessThanMax) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - fromjson("{a: 1}"), - fromjson("{a: 1, b: 1}"), - false, // snapshot - false) // explain - .getStatus()); - } - - // Helper function which returns the Status of creating a LiteParsedQuery object with the given - // parameters. - void assertLiteParsedQuerySuccess(const BSONObj& query, - const BSONObj& proj, - const BSONObj& sort) { - - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - query, - proj, - sort, - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false))); // explain - } - - // - // Test compatibility of various projection and sort objects. - // - - TEST(LiteParsedQueryTest, ValidSortProj) { - assertLiteParsedQuerySuccess(BSONObj(), - fromjson("{a: 1}"), - fromjson("{a: 1}")); - - assertLiteParsedQuerySuccess(BSONObj(), - fromjson("{a: {$meta: \"textScore\"}}"), - fromjson("{a: {$meta: \"textScore\"}}")); - } - - TEST(LiteParsedQueryTest, ForbidNonMetaSortOnFieldWithMetaProject) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - fromjson("{a: {$meta: \"textScore\"}}"), - fromjson("{a: 1}"), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false) // explain - .getStatus()); - - assertLiteParsedQuerySuccess(BSONObj(), - fromjson("{a: {$meta: \"textScore\"}}"), - fromjson("{b: 1}")); - } - - TEST(LiteParsedQueryTest, ForbidMetaSortOnFieldWithoutMetaProject) { - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - fromjson("{a: 1}"), - fromjson("{a: {$meta: \"textScore\"}}"), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false) // explain - .getStatus()); - - ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", - 0, - 0, - 0, - BSONObj(), - fromjson("{b: 1}"), - fromjson("{a: {$meta: \"textScore\"}}"), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false) // explain - .getStatus()); - } - - TEST(LiteParsedQueryTest, MakeFindCmd) { - auto result = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), - BSON("x" << 1), - 2); - ASSERT_OK(result.getStatus()); - - auto&& lpq = result.getValue(); - ASSERT_EQUALS("test.ns", lpq->ns()); - ASSERT_EQUALS(BSON("x" << 1), lpq->getFilter()); - ASSERT_EQUALS(2, lpq->getLimit()); - - ASSERT_EQUALS(BSONObj(), lpq->getProj()); - ASSERT_EQUALS(BSONObj(), lpq->getSort()); - ASSERT_EQUALS(BSONObj(), lpq->getHint()); - ASSERT_EQUALS(BSONObj(), lpq->getMin()); - ASSERT_EQUALS(BSONObj(), lpq->getMax()); - - ASSERT_EQUALS(0, lpq->getSkip()); - ASSERT_EQUALS(0, lpq->getMaxScan()); - ASSERT_EQUALS(0, lpq->getMaxTimeMS()); - ASSERT_EQUALS(0, lpq->getOptions()); - - ASSERT_FALSE(lpq->getBatchSize()); - - ASSERT_TRUE(lpq->isFromFindCommand()); - ASSERT_FALSE(lpq->isExplain()); - ASSERT_FALSE(lpq->returnKey()); - ASSERT_FALSE(lpq->showRecordId()); - ASSERT_FALSE(lpq->isSnapshot()); - ASSERT_FALSE(lpq->hasReadPref()); - ASSERT_FALSE(lpq->isTailable()); - ASSERT_FALSE(lpq->isSlaveOk()); - ASSERT_FALSE(lpq->isOplogReplay()); - ASSERT_FALSE(lpq->isNoCursorTimeout()); - ASSERT_FALSE(lpq->isAwaitData()); - ASSERT_FALSE(lpq->isExhaust()); - ASSERT_FALSE(lpq->isPartial()); - } - - TEST(LiteParsedQueryTest, MakeFindCmdNoLimit) { - auto result = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), - BSON("x" << 1), - boost::none); - ASSERT_OK(result.getStatus()); - - auto&& lpq = result.getValue(); - ASSERT_EQUALS("test.ns", lpq->ns()); - ASSERT_EQUALS(BSON("x" << 1), lpq->getFilter()); - - ASSERT_EQUALS(BSONObj(), lpq->getProj()); - ASSERT_EQUALS(BSONObj(), lpq->getSort()); - ASSERT_EQUALS(BSONObj(), lpq->getHint()); - ASSERT_EQUALS(BSONObj(), lpq->getMin()); - ASSERT_EQUALS(BSONObj(), lpq->getMax()); - - ASSERT_EQUALS(0, lpq->getSkip()); - ASSERT_EQUALS(0, lpq->getMaxScan()); - ASSERT_EQUALS(0, lpq->getMaxTimeMS()); - ASSERT_EQUALS(0, lpq->getOptions()); - - ASSERT_FALSE(lpq->getBatchSize()); - ASSERT_FALSE(lpq->getLimit()); - - ASSERT_TRUE(lpq->isFromFindCommand()); - ASSERT_FALSE(lpq->isExplain()); - ASSERT_FALSE(lpq->returnKey()); - ASSERT_FALSE(lpq->showRecordId()); - ASSERT_FALSE(lpq->isSnapshot()); - ASSERT_FALSE(lpq->hasReadPref()); - ASSERT_FALSE(lpq->isTailable()); - ASSERT_FALSE(lpq->isSlaveOk()); - ASSERT_FALSE(lpq->isOplogReplay()); - ASSERT_FALSE(lpq->isNoCursorTimeout()); - ASSERT_FALSE(lpq->isAwaitData()); - ASSERT_FALSE(lpq->isExhaust()); - ASSERT_FALSE(lpq->isPartial()); - } - - TEST(LiteParsedQueryTest, MakeFindCmdBadLimit) { - auto status = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), - BSON("x" << 1), - 0).getStatus(); - ASSERT_NOT_OK(status); - ASSERT_EQUALS(ErrorCodes::BadValue, status.code()); - } - - // - // Text meta BSON element validation - // - - bool isFirstElementTextScoreMeta(const char* sortStr) { - BSONObj sortObj = fromjson(sortStr); - BSONElement elt = sortObj.firstElement(); - bool result = LiteParsedQuery::isTextScoreMeta(elt); - return result; - } - - // Check validation of $meta expressions - TEST(LiteParsedQueryTest, IsTextScoreMeta) { - // Valid textScore meta sort - ASSERT(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\"}}")); - - // Invalid textScore meta sorts - ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: 1}}")); - ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"image\"}}")); - ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$world: \"textScore\"}}")); - ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\", b: 1}}")); - } - - // - // Sort order validation - // In a valid sort order, each element satisfies one of: - // 1. a number with value 1 - // 2. a number with value -1 - // 3. isTextScoreMeta - // - - TEST(LiteParsedQueryTest, ValidateSortOrder) { - // Valid sorts - ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{}"))); - ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: 1}"))); - ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: -1}"))); - ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"textScore\"}}"))); - - // Invalid sorts - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 100}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 0}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -100}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: Infinity}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -Infinity}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: true}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: false}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: null}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {b: 1}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: []}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: [1, 2, 3]}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"\"}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"bb\"}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: 1}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"image\"}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$world: \"textScore\"}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"textScore\"," - " b: 1}}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': 1}"))); - ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': -1}"))); - } - - // - // Tests for parsing a lite parsed query from a command BSON object. - // - - TEST(LiteParsedQueryTest, ParseFromCommandBasic) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 3}," - "sort: {a: 1}," - "projection: {_id: 0, a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandWithOptions) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 3}," - "sort: {a: 1}," - "projection: {_id: 0, a: 1}," - "showRecordId: true," - "maxScan: 1000}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - // Make sure the values from the command BSON are reflected in the LPQ. - ASSERT(lpq->showRecordId()); - ASSERT_EQUALS(1000, lpq->getMaxScan()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandHintAsString) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "hint: 'foo_1'}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - BSONObj hintObj = lpq->getHint(); - ASSERT_EQUALS(BSON("$hint" << "foo_1"), hintObj); - } - - TEST(LiteParsedQueryTest, ParseFromCommandValidSortProj) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "projection: {a: 1}," - "sort: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandValidSortProjMeta) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "projection: {a: {$meta: 'textScore'}}," - "sort: {a: {$meta: 'textScore'}}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandAllFlagsTrue) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "tailable: true," - "slaveOk: true," - "oplogReplay: true," - "noCursorTimeout: true," - "awaitData: true," - "partial: true}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - // Test that all the flags got set to true. - ASSERT(lpq->isTailable()); - ASSERT(lpq->isSlaveOk()); - ASSERT(lpq->isOplogReplay()); - ASSERT(lpq->isNoCursorTimeout()); - ASSERT(lpq->isAwaitData()); - ASSERT(lpq->isPartial()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandCommentWithValidMinMax) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "comment: 'the comment'," - "min: {a: 1}," - "max: {a: 2}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - ASSERT_EQUALS("the comment", lpq->getComment()); - BSONObj expectedMin = BSON("a" << 1); - ASSERT_EQUALS(0, expectedMin.woCompare(lpq->getMin())); - BSONObj expectedMax = BSON("a" << 2); - ASSERT_EQUALS(0, expectedMax.woCompare(lpq->getMax())); - } - - TEST(LiteParsedQueryTest, ParseFromCommandAllNonOptionFields) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "sort: {b: 1}," - "projection: {c: 1}," - "hint: {d: 1}," - "limit: 3," - "skip: 5," - "batchSize: 90," - "singleBatch: false}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - // Check the values inside the LPQ. - BSONObj expectedQuery = BSON("a" << 1); - ASSERT_EQUALS(0, expectedQuery.woCompare(lpq->getFilter())); - BSONObj expectedSort = BSON("b" << 1); - ASSERT_EQUALS(0, expectedSort.woCompare(lpq->getSort())); - BSONObj expectedProj = BSON("c" << 1); - ASSERT_EQUALS(0, expectedProj.woCompare(lpq->getProj())); - BSONObj expectedHint = BSON("d" << 1); - ASSERT_EQUALS(0, expectedHint.woCompare(lpq->getHint())); - ASSERT_EQUALS(3, lpq->getLimit()); - ASSERT_EQUALS(5, lpq->getSkip()); - ASSERT_EQUALS(90, lpq->getBatchSize()); - ASSERT(lpq->wantMore()); - } - - // - // Parsing errors where a field has the wrong type. - // - - TEST(LiteParsedQueryTest, ParseFromCommandQueryWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSortWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "sort: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandProjWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "projection: 'foo'}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSkipWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "skip: '5'," - "projection: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandLimitWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "limit: '5'," - "projection: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSingleBatchWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "singleBatch: 'false'," - "projection: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandCommentWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "comment: 1}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandMaxScanWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "maxScan: true," - "comment: 'foo'}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandMaxTimeMSWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "maxTimeMS: true}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandMaxWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "max: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandMinWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "min: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandReturnKeyWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "returnKey: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - - TEST(LiteParsedQueryTest, ParseFromCommandShowRecordIdWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "showRecordId: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSnapshotWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "snapshot: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandTailableWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "tailable: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSlaveOkWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "slaveOk: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandOplogReplayWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "oplogReplay: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandNoCursorTimeoutWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "noCursorTimeout: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandAwaitDataWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "tailable: true," - "awaitData: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandExhaustWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "exhaust: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandPartialWrongType) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "filter: {a: 1}," - "exhaust: 3}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - // - // Parsing errors where a field has the right type but a bad value. - // - - TEST(LiteParsedQueryTest, ParseFromCommandNegativeSkipError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "skip: -3," - "filter: {a: 3}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandNegativeLimitError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "limit: -3," - "filter: {a: 3}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandNegativeBatchSizeError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "batchSize: -10," - "filter: {a: 3}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandBatchSizeZero) { - BSONObj cmdObj = fromjson("{find: 'testns', batchSize: 0}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - ASSERT(lpq->getBatchSize()); - ASSERT_EQ(0, lpq->getBatchSize()); - - ASSERT(!lpq->getLimit()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandDefaultBatchSize) { - BSONObj cmdObj = fromjson("{find: 'testns'}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - ASSERT(!lpq->getBatchSize()); - ASSERT(!lpq->getLimit()); - } - - // - // Errors checked in LiteParsedQuery::validate(). - // - - TEST(LiteParsedQueryTest, ParseFromCommandMinMaxDifferentFieldsError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "min: {a: 3}," - "max: {b: 4}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSnapshotPlusSortError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "sort: {a: 3}," - "snapshot: true}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandSnapshotPlusHintError) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "snapshot: true," - "hint: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseCommandForbidNonMetaSortOnFieldWithMetaProject) { - BSONObj cmdObj; - - cmdObj = fromjson("{find: 'testns'," - "projection: {a: {$meta: 'textScore'}}," - "sort: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - - cmdObj = fromjson("{find: 'testns'," - "projection: {a: {$meta: 'textScore'}}," - "sort: {b: 1}}"); - ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); - } - - TEST(LiteParsedQueryTest, ParseCommandForbidMetaSortOnFieldWithoutMetaProject) { - BSONObj cmdObj; - - cmdObj = fromjson("{find: 'testns'," - "projection: {a: 1}," - "sort: {a: {$meta: 'textScore'}}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - - cmdObj = fromjson("{find: 'testns'," - "projection: {b: 1}," - "sort: {a: {$meta: 'textScore'}}}"); - result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseCommandForbidExhaust) { - BSONObj cmdObj = fromjson("{find: 'testns', exhaust: true}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseCommandIsFromFindCommand) { - BSONObj cmdObj = fromjson("{find: 'testns'}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - - ASSERT(lpq->isFromFindCommand()); - } - - TEST(LiteParsedQueryTest, ParseCommandNotFromFindCommand) { - std::unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeAsOpQuery("testns", - 5, - 6, - 9, - BSON( "x" << 5 ), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - BSONObj(), - false, // snapshot - false))); // explain - ASSERT(!lpq->isFromFindCommand()); - } - - TEST(LiteParsedQueryTest, ParseCommandAwaitDataButNotTailable) { - const NamespaceString nss("test.testns"); - BSONObj cmdObj = fromjson("{find: 'testns', awaitData: true}"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseCommandFirstFieldNotString) { - BSONObj cmdObj = fromjson("{find: 1}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, DefaultQueryParametersCorrect) { - BSONObj cmdObj = fromjson("{find: 'testns'}"); - - const NamespaceString nss("test.testns"); - std::unique_ptr<LiteParsedQuery> lpq( - assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, false))); - - ASSERT_EQUALS(0, lpq->getSkip()); - ASSERT_EQUALS(true, lpq->wantMore()); - ASSERT_EQUALS(true, lpq->isFromFindCommand()); - ASSERT_EQUALS(false, lpq->isExplain()); - ASSERT_EQUALS(0, lpq->getMaxScan()); - ASSERT_EQUALS(0, lpq->getMaxTimeMS()); - ASSERT_EQUALS(false, lpq->returnKey()); - ASSERT_EQUALS(false, lpq->showRecordId()); - ASSERT_EQUALS(false, lpq->isSnapshot()); - ASSERT_EQUALS(false, lpq->hasReadPref()); - ASSERT_EQUALS(false, lpq->isTailable()); - ASSERT_EQUALS(false, lpq->isSlaveOk()); - ASSERT_EQUALS(false, lpq->isOplogReplay()); - ASSERT_EQUALS(false, lpq->isNoCursorTimeout()); - ASSERT_EQUALS(false, lpq->isAwaitData()); - ASSERT_EQUALS(false, lpq->isExhaust()); - ASSERT_EQUALS(false, lpq->isPartial()); - } - - // - // Extra fields cause the parse to fail. - // - - TEST(LiteParsedQueryTest, ParseFromCommandForbidExtraField) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "snapshot: true," - "foo: {a: 1}}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - - TEST(LiteParsedQueryTest, ParseFromCommandForbidExtraOption) { - BSONObj cmdObj = fromjson("{find: 'testns'," - "snapshot: true," - "foo: true}"); - const NamespaceString nss("test.testns"); - bool isExplain = false; - auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); - ASSERT_NOT_OK(result.getStatus()); - } - -} // namespace mongo -} // namespace + .getStatus()); +} + +TEST(LiteParsedQueryTest, MinFieldsLessThanMax) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + fromjson("{a: 1}"), + fromjson("{a: 1, b: 1}"), + false, // snapshot + false) // explain + .getStatus()); +} + +// Helper function which returns the Status of creating a LiteParsedQuery object with the given +// parameters. +void assertLiteParsedQuerySuccess(const BSONObj& query, const BSONObj& proj, const BSONObj& sort) { + unique_ptr<LiteParsedQuery> lpq(assertGet(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + query, + proj, + sort, + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false))); // explain +} + +// +// Test compatibility of various projection and sort objects. +// + +TEST(LiteParsedQueryTest, ValidSortProj) { + assertLiteParsedQuerySuccess(BSONObj(), fromjson("{a: 1}"), fromjson("{a: 1}")); + + assertLiteParsedQuerySuccess(BSONObj(), + fromjson("{a: {$meta: \"textScore\"}}"), + fromjson("{a: {$meta: \"textScore\"}}")); +} + +TEST(LiteParsedQueryTest, ForbidNonMetaSortOnFieldWithMetaProject) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), + fromjson("{a: {$meta: \"textScore\"}}"), + fromjson("{a: 1}"), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false) // explain + .getStatus()); + + assertLiteParsedQuerySuccess( + BSONObj(), fromjson("{a: {$meta: \"textScore\"}}"), fromjson("{b: 1}")); +} + +TEST(LiteParsedQueryTest, ForbidMetaSortOnFieldWithoutMetaProject) { + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), + fromjson("{a: 1}"), + fromjson("{a: {$meta: \"textScore\"}}"), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false) // explain + .getStatus()); + + ASSERT_NOT_OK(LiteParsedQuery::makeAsOpQuery("testns", + 0, + 0, + 0, + BSONObj(), + fromjson("{b: 1}"), + fromjson("{a: {$meta: \"textScore\"}}"), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false) // explain + .getStatus()); +} + +TEST(LiteParsedQueryTest, MakeFindCmd) { + auto result = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), BSON("x" << 1), 2); + ASSERT_OK(result.getStatus()); + + auto&& lpq = result.getValue(); + ASSERT_EQUALS("test.ns", lpq->ns()); + ASSERT_EQUALS(BSON("x" << 1), lpq->getFilter()); + ASSERT_EQUALS(2, lpq->getLimit()); + + ASSERT_EQUALS(BSONObj(), lpq->getProj()); + ASSERT_EQUALS(BSONObj(), lpq->getSort()); + ASSERT_EQUALS(BSONObj(), lpq->getHint()); + ASSERT_EQUALS(BSONObj(), lpq->getMin()); + ASSERT_EQUALS(BSONObj(), lpq->getMax()); + + ASSERT_EQUALS(0, lpq->getSkip()); + ASSERT_EQUALS(0, lpq->getMaxScan()); + ASSERT_EQUALS(0, lpq->getMaxTimeMS()); + ASSERT_EQUALS(0, lpq->getOptions()); + + ASSERT_FALSE(lpq->getBatchSize()); + + ASSERT_TRUE(lpq->isFromFindCommand()); + ASSERT_FALSE(lpq->isExplain()); + ASSERT_FALSE(lpq->returnKey()); + ASSERT_FALSE(lpq->showRecordId()); + ASSERT_FALSE(lpq->isSnapshot()); + ASSERT_FALSE(lpq->hasReadPref()); + ASSERT_FALSE(lpq->isTailable()); + ASSERT_FALSE(lpq->isSlaveOk()); + ASSERT_FALSE(lpq->isOplogReplay()); + ASSERT_FALSE(lpq->isNoCursorTimeout()); + ASSERT_FALSE(lpq->isAwaitData()); + ASSERT_FALSE(lpq->isExhaust()); + ASSERT_FALSE(lpq->isPartial()); +} + +TEST(LiteParsedQueryTest, MakeFindCmdNoLimit) { + auto result = + LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), BSON("x" << 1), boost::none); + ASSERT_OK(result.getStatus()); + + auto&& lpq = result.getValue(); + ASSERT_EQUALS("test.ns", lpq->ns()); + ASSERT_EQUALS(BSON("x" << 1), lpq->getFilter()); + + ASSERT_EQUALS(BSONObj(), lpq->getProj()); + ASSERT_EQUALS(BSONObj(), lpq->getSort()); + ASSERT_EQUALS(BSONObj(), lpq->getHint()); + ASSERT_EQUALS(BSONObj(), lpq->getMin()); + ASSERT_EQUALS(BSONObj(), lpq->getMax()); + + ASSERT_EQUALS(0, lpq->getSkip()); + ASSERT_EQUALS(0, lpq->getMaxScan()); + ASSERT_EQUALS(0, lpq->getMaxTimeMS()); + ASSERT_EQUALS(0, lpq->getOptions()); + + ASSERT_FALSE(lpq->getBatchSize()); + ASSERT_FALSE(lpq->getLimit()); + + ASSERT_TRUE(lpq->isFromFindCommand()); + ASSERT_FALSE(lpq->isExplain()); + ASSERT_FALSE(lpq->returnKey()); + ASSERT_FALSE(lpq->showRecordId()); + ASSERT_FALSE(lpq->isSnapshot()); + ASSERT_FALSE(lpq->hasReadPref()); + ASSERT_FALSE(lpq->isTailable()); + ASSERT_FALSE(lpq->isSlaveOk()); + ASSERT_FALSE(lpq->isOplogReplay()); + ASSERT_FALSE(lpq->isNoCursorTimeout()); + ASSERT_FALSE(lpq->isAwaitData()); + ASSERT_FALSE(lpq->isExhaust()); + ASSERT_FALSE(lpq->isPartial()); +} + +TEST(LiteParsedQueryTest, MakeFindCmdBadLimit) { + auto status = + LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"), BSON("x" << 1), 0).getStatus(); + ASSERT_NOT_OK(status); + ASSERT_EQUALS(ErrorCodes::BadValue, status.code()); +} + +// +// Text meta BSON element validation +// + +bool isFirstElementTextScoreMeta(const char* sortStr) { + BSONObj sortObj = fromjson(sortStr); + BSONElement elt = sortObj.firstElement(); + bool result = LiteParsedQuery::isTextScoreMeta(elt); + return result; +} + +// Check validation of $meta expressions +TEST(LiteParsedQueryTest, IsTextScoreMeta) { + // Valid textScore meta sort + ASSERT(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\"}}")); + + // Invalid textScore meta sorts + ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: 1}}")); + ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"image\"}}")); + ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$world: \"textScore\"}}")); + ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\", b: 1}}")); +} + +// +// Sort order validation +// In a valid sort order, each element satisfies one of: +// 1. a number with value 1 +// 2. a number with value -1 +// 3. isTextScoreMeta +// + +TEST(LiteParsedQueryTest, ValidateSortOrder) { + // Valid sorts + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: 1}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: -1}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"textScore\"}}"))); + + // Invalid sorts + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 100}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 0}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -100}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: Infinity}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -Infinity}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: true}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: false}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: null}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {b: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: []}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: [1, 2, 3]}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"\"}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"bb\"}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"image\"}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$world: \"textScore\"}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson( + "{a: {$meta: \"textScore\"," + " b: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': 1}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': -1}"))); +} + +// +// Tests for parsing a lite parsed query from a command BSON object. +// + +TEST(LiteParsedQueryTest, ParseFromCommandBasic) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 3}," + "sort: {a: 1}," + "projection: {_id: 0, a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandWithOptions) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 3}," + "sort: {a: 1}," + "projection: {_id: 0, a: 1}," + "showRecordId: true," + "maxScan: 1000}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + // Make sure the values from the command BSON are reflected in the LPQ. + ASSERT(lpq->showRecordId()); + ASSERT_EQUALS(1000, lpq->getMaxScan()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandHintAsString) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "hint: 'foo_1'}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + BSONObj hintObj = lpq->getHint(); + ASSERT_EQUALS(BSON("$hint" + << "foo_1"), + hintObj); +} + +TEST(LiteParsedQueryTest, ParseFromCommandValidSortProj) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "projection: {a: 1}," + "sort: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandValidSortProjMeta) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "projection: {a: {$meta: 'textScore'}}," + "sort: {a: {$meta: 'textScore'}}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandAllFlagsTrue) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "tailable: true," + "slaveOk: true," + "oplogReplay: true," + "noCursorTimeout: true," + "awaitData: true," + "partial: true}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + // Test that all the flags got set to true. + ASSERT(lpq->isTailable()); + ASSERT(lpq->isSlaveOk()); + ASSERT(lpq->isOplogReplay()); + ASSERT(lpq->isNoCursorTimeout()); + ASSERT(lpq->isAwaitData()); + ASSERT(lpq->isPartial()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandCommentWithValidMinMax) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "comment: 'the comment'," + "min: {a: 1}," + "max: {a: 2}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + ASSERT_EQUALS("the comment", lpq->getComment()); + BSONObj expectedMin = BSON("a" << 1); + ASSERT_EQUALS(0, expectedMin.woCompare(lpq->getMin())); + BSONObj expectedMax = BSON("a" << 2); + ASSERT_EQUALS(0, expectedMax.woCompare(lpq->getMax())); +} + +TEST(LiteParsedQueryTest, ParseFromCommandAllNonOptionFields) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "sort: {b: 1}," + "projection: {c: 1}," + "hint: {d: 1}," + "limit: 3," + "skip: 5," + "batchSize: 90," + "singleBatch: false}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + // Check the values inside the LPQ. + BSONObj expectedQuery = BSON("a" << 1); + ASSERT_EQUALS(0, expectedQuery.woCompare(lpq->getFilter())); + BSONObj expectedSort = BSON("b" << 1); + ASSERT_EQUALS(0, expectedSort.woCompare(lpq->getSort())); + BSONObj expectedProj = BSON("c" << 1); + ASSERT_EQUALS(0, expectedProj.woCompare(lpq->getProj())); + BSONObj expectedHint = BSON("d" << 1); + ASSERT_EQUALS(0, expectedHint.woCompare(lpq->getHint())); + ASSERT_EQUALS(3, lpq->getLimit()); + ASSERT_EQUALS(5, lpq->getSkip()); + ASSERT_EQUALS(90, lpq->getBatchSize()); + ASSERT(lpq->wantMore()); +} + +// +// Parsing errors where a field has the wrong type. +// + +TEST(LiteParsedQueryTest, ParseFromCommandQueryWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSortWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "sort: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandProjWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "projection: 'foo'}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSkipWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "skip: '5'," + "projection: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandLimitWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "limit: '5'," + "projection: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSingleBatchWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "singleBatch: 'false'," + "projection: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandCommentWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "comment: 1}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandMaxScanWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "maxScan: true," + "comment: 'foo'}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandMaxTimeMSWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "maxTimeMS: true}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandMaxWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "max: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandMinWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "min: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandReturnKeyWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "returnKey: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + + +TEST(LiteParsedQueryTest, ParseFromCommandShowRecordIdWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "showRecordId: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSnapshotWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "snapshot: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandTailableWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "tailable: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSlaveOkWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "slaveOk: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandOplogReplayWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "oplogReplay: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandNoCursorTimeoutWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "noCursorTimeout: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandAwaitDataWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "tailable: true," + "awaitData: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandExhaustWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "exhaust: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandPartialWrongType) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "filter: {a: 1}," + "exhaust: 3}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +// +// Parsing errors where a field has the right type but a bad value. +// + +TEST(LiteParsedQueryTest, ParseFromCommandNegativeSkipError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "skip: -3," + "filter: {a: 3}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandNegativeLimitError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "limit: -3," + "filter: {a: 3}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandNegativeBatchSizeError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "batchSize: -10," + "filter: {a: 3}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandBatchSizeZero) { + BSONObj cmdObj = fromjson("{find: 'testns', batchSize: 0}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + ASSERT(lpq->getBatchSize()); + ASSERT_EQ(0, lpq->getBatchSize()); + + ASSERT(!lpq->getLimit()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandDefaultBatchSize) { + BSONObj cmdObj = fromjson("{find: 'testns'}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + ASSERT(!lpq->getBatchSize()); + ASSERT(!lpq->getLimit()); +} + +// +// Errors checked in LiteParsedQuery::validate(). +// + +TEST(LiteParsedQueryTest, ParseFromCommandMinMaxDifferentFieldsError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "min: {a: 3}," + "max: {b: 4}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSnapshotPlusSortError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "sort: {a: 3}," + "snapshot: true}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandSnapshotPlusHintError) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "snapshot: true," + "hint: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseCommandForbidNonMetaSortOnFieldWithMetaProject) { + BSONObj cmdObj; + + cmdObj = fromjson( + "{find: 'testns'," + "projection: {a: {$meta: 'textScore'}}," + "sort: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); + + cmdObj = fromjson( + "{find: 'testns'," + "projection: {a: {$meta: 'textScore'}}," + "sort: {b: 1}}"); + ASSERT_OK(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain).getStatus()); +} + +TEST(LiteParsedQueryTest, ParseCommandForbidMetaSortOnFieldWithoutMetaProject) { + BSONObj cmdObj; + + cmdObj = fromjson( + "{find: 'testns'," + "projection: {a: 1}," + "sort: {a: {$meta: 'textScore'}}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); + + cmdObj = fromjson( + "{find: 'testns'," + "projection: {b: 1}," + "sort: {a: {$meta: 'textScore'}}}"); + result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseCommandForbidExhaust) { + BSONObj cmdObj = fromjson("{find: 'testns', exhaust: true}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseCommandIsFromFindCommand) { + BSONObj cmdObj = fromjson("{find: 'testns'}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); + + ASSERT(lpq->isFromFindCommand()); +} + +TEST(LiteParsedQueryTest, ParseCommandNotFromFindCommand) { + std::unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeAsOpQuery("testns", + 5, + 6, + 9, + BSON("x" << 5), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + BSONObj(), + false, // snapshot + false))); // explain + ASSERT(!lpq->isFromFindCommand()); +} + +TEST(LiteParsedQueryTest, ParseCommandAwaitDataButNotTailable) { + const NamespaceString nss("test.testns"); + BSONObj cmdObj = fromjson("{find: 'testns', awaitData: true}"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseCommandFirstFieldNotString) { + BSONObj cmdObj = fromjson("{find: 1}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, DefaultQueryParametersCorrect) { + BSONObj cmdObj = fromjson("{find: 'testns'}"); + + const NamespaceString nss("test.testns"); + std::unique_ptr<LiteParsedQuery> lpq( + assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, false))); + + ASSERT_EQUALS(0, lpq->getSkip()); + ASSERT_EQUALS(true, lpq->wantMore()); + ASSERT_EQUALS(true, lpq->isFromFindCommand()); + ASSERT_EQUALS(false, lpq->isExplain()); + ASSERT_EQUALS(0, lpq->getMaxScan()); + ASSERT_EQUALS(0, lpq->getMaxTimeMS()); + ASSERT_EQUALS(false, lpq->returnKey()); + ASSERT_EQUALS(false, lpq->showRecordId()); + ASSERT_EQUALS(false, lpq->isSnapshot()); + ASSERT_EQUALS(false, lpq->hasReadPref()); + ASSERT_EQUALS(false, lpq->isTailable()); + ASSERT_EQUALS(false, lpq->isSlaveOk()); + ASSERT_EQUALS(false, lpq->isOplogReplay()); + ASSERT_EQUALS(false, lpq->isNoCursorTimeout()); + ASSERT_EQUALS(false, lpq->isAwaitData()); + ASSERT_EQUALS(false, lpq->isExhaust()); + ASSERT_EQUALS(false, lpq->isPartial()); +} + +// +// Extra fields cause the parse to fail. +// + +TEST(LiteParsedQueryTest, ParseFromCommandForbidExtraField) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "snapshot: true," + "foo: {a: 1}}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +TEST(LiteParsedQueryTest, ParseFromCommandForbidExtraOption) { + BSONObj cmdObj = fromjson( + "{find: 'testns'," + "snapshot: true," + "foo: true}"); + const NamespaceString nss("test.testns"); + bool isExplain = false; + auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain); + ASSERT_NOT_OK(result.getStatus()); +} + +} // namespace mongo +} // namespace |