text: /**
   *    Copyright (C) 2013 MongoDB Inc.
   *
   *    This program is free software: you can redistribute it and/or  modify
   *    it under the terms of the GNU Affero General Public License, version 3,
   *    as published by the Free Software Foundation.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU Affero General Public License for more details.
   *
   *    You should have received a copy of the GNU Affero General Public License
   *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   *    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 <boost/optional.hpp>
  #include <boost/optional/optional_io.hpp>
  
  #include "mongo/db/json.h"
  #include "mongo/db/namespace_string.h"
  #include "mongo/db/query/lite_parsed_query.h"
  #include "mongo/unittest/unittest.h"
  
  namespace mongo {
  namespace {
  
  using std::unique_ptr;
  using unittest::assertGet;
  
  static const NamespaceString testns("testdb.testcoll");
  
  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->getNToReturn());
      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->getNToReturn());
      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());
  }
  
  TEST(LiteParsedQueryTest, ForbidTailableWithNonNaturalSort) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "tailable: true,"
          "sort: {a: 1}}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain);
      ASSERT_NOT_OK(result.getStatus());
  }
  
  TEST(LiteParsedQueryTest, AllowTailableWithNaturalSort) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "tailable: true,"
          "sort: {$natural: 1}}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain);
      ASSERT_OK(result.getStatus());
      ASSERT_TRUE(result.getValue()->isTailable());
      ASSERT_EQ(result.getValue()->getSort(), BSON("$natural" << 1));
  }
  
  // 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, MakeAsFindCmdDefaultArgs) {
      auto lpq = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"));
  
      ASSERT_EQUALS("test.ns", lpq->ns());
  
      ASSERT_EQUALS(BSONObj(), lpq->getFilter());
      ASSERT_EQUALS(BSONObj(), lpq->getProj());
      ASSERT_EQUALS(BSONObj(), lpq->getSort());
      ASSERT_EQUALS(BSONObj(), lpq->getHint());
      ASSERT_EQUALS(BSONObj(), lpq->getReadConcern());
  
      ASSERT_FALSE(lpq->getSkip());
      ASSERT_FALSE(lpq->getLimit());
      ASSERT_FALSE(lpq->getBatchSize());
      ASSERT_FALSE(lpq->getNToReturn());
      ASSERT_TRUE(lpq->wantMore());
  
      ASSERT_FALSE(lpq->isExplain());
      ASSERT_EQ("", lpq->getComment());
      ASSERT_EQUALS(0, lpq->getMaxScan());
      ASSERT_EQUALS(0, lpq->getMaxTimeMS());
  
      ASSERT_EQUALS(BSONObj(), lpq->getMin());
      ASSERT_EQUALS(BSONObj(), lpq->getMax());
  
      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->isAllowPartialResults());
  }
  
  TEST(LiteParsedQueryTest, MakeFindCmdAllArgs) {
      auto lpq = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"),
                                                BSON("a" << 1),
                                                BSON("b" << 1),
                                                BSON("c" << 1),
                                                BSON("d" << 1),
                                                BSON("e" << 1),
                                                4,
                                                5,
                                                6,
                                                boost::none,
                                                false,
                                                true,
                                                "this is a comment",
                                                7,
                                                8,
                                                BSON("e" << 1),
                                                BSON("f" << 1),
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true);
  
      ASSERT_EQUALS("test.ns", lpq->ns());
  
      ASSERT_EQUALS(BSON("a" << 1), lpq->getFilter());
      ASSERT_EQUALS(BSON("b" << 1), lpq->getProj());
      ASSERT_EQUALS(BSON("c" << 1), lpq->getSort());
      ASSERT_EQUALS(BSON("d" << 1), lpq->getHint());
      ASSERT_EQUALS(BSON("e" << 1), lpq->getReadConcern());
  
      ASSERT_EQ(4, *lpq->getSkip());
      ASSERT_EQ(5, *lpq->getLimit());
      ASSERT_EQ(6, *lpq->getBatchSize());
      ASSERT_FALSE(lpq->getNToReturn());
      ASSERT_FALSE(lpq->wantMore());
      ASSERT_EQ(6, *lpq->getEffectiveBatchSize());
  
      ASSERT_TRUE(lpq->isExplain());
      ASSERT_EQ("this is a comment", lpq->getComment());
      ASSERT_EQUALS(7, lpq->getMaxScan());
      ASSERT_EQUALS(8, lpq->getMaxTimeMS());
  
      ASSERT_EQUALS(BSON("e" << 1), lpq->getMin());
      ASSERT_EQUALS(BSON("f" << 1), lpq->getMax());
  
      ASSERT_TRUE(lpq->returnKey());
      ASSERT_TRUE(lpq->showRecordId());
      ASSERT_TRUE(lpq->isSnapshot());
      ASSERT_TRUE(lpq->hasReadPref());
      ASSERT_TRUE(lpq->isTailable());
      ASSERT_TRUE(lpq->isSlaveOk());
      ASSERT_TRUE(lpq->isOplogReplay());
      ASSERT_TRUE(lpq->isNoCursorTimeout());
      ASSERT_TRUE(lpq->isAwaitData());
      ASSERT_TRUE(lpq->isAllowPartialResults());
  }
  
  TEST(LiteParsedQueryTest, MakeAsFindCmdNToReturn) {
      auto lpq = LiteParsedQuery::makeAsFindCmd(NamespaceString("test.ns"),
                                                BSON("a" << 1),
                                                BSON("b" << 1),
                                                BSON("c" << 1),
                                                BSON("d" << 1),
                                                BSON("e" << 1),
                                                4,
                                                boost::none,
                                                boost::none,
                                                5,
                                                false,
                                                true,
                                                "this is a comment",
                                                7,
                                                8,
                                                BSON("e" << 1),
                                                BSON("f" << 1),
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true,
                                                true);
  
      ASSERT_EQUALS("test.ns", lpq->ns());
  
      ASSERT_EQUALS(BSON("a" << 1), lpq->getFilter());
      ASSERT_EQUALS(BSON("b" << 1), lpq->getProj());
      ASSERT_EQUALS(BSON("c" << 1), lpq->getSort());
      ASSERT_EQUALS(BSON("d" << 1), lpq->getHint());
      ASSERT_EQUALS(BSON("e" << 1), lpq->getReadConcern());
  
      ASSERT_EQ(4, *lpq->getSkip());
      ASSERT_FALSE(lpq->getLimit());
      ASSERT_FALSE(lpq->getBatchSize());
      ASSERT_EQ(5, *lpq->getNToReturn());
      ASSERT_FALSE(lpq->wantMore());
      ASSERT_EQ(5, *lpq->getEffectiveBatchSize());
  
      ASSERT_TRUE(lpq->isExplain());
      ASSERT_EQ("this is a comment", lpq->getComment());
      ASSERT_EQUALS(7, lpq->getMaxScan());
      ASSERT_EQUALS(8, lpq->getMaxTimeMS());
  
      ASSERT_EQUALS(BSON("e" << 1), lpq->getMin());
      ASSERT_EQUALS(BSON("f" << 1), lpq->getMax());
  
      ASSERT_TRUE(lpq->returnKey());
      ASSERT_TRUE(lpq->showRecordId());
      ASSERT_TRUE(lpq->isSnapshot());
      ASSERT_TRUE(lpq->hasReadPref());
      ASSERT_TRUE(lpq->isTailable());
      ASSERT_TRUE(lpq->isSlaveOk());
      ASSERT_TRUE(lpq->isOplogReplay());
      ASSERT_TRUE(lpq->isNoCursorTimeout());
      ASSERT_TRUE(lpq->isAwaitData());
      ASSERT_TRUE(lpq->isAllowPartialResults());
  }
  
  //
  // 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,"
          "oplogReplay: true,"
          "noCursorTimeout: true,"
          "awaitData: true,"
          "allowPartialResults: 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->isAllowPartialResults());
  }
  
  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},"
          "readConcern: {e: 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()));
      BSONObj expectedReadConcern = BSON("e" << 1);
      ASSERT_EQUALS(0, expectedReadConcern.woCompare(lpq->getReadConcern()));
      ASSERT_EQUALS(3, *lpq->getLimit());
      ASSERT_EQUALS(5, *lpq->getSkip());
      ASSERT_EQUALS(90, *lpq->getBatchSize());
      ASSERT(lpq->wantMore());
  }
  
  TEST(LiteParsedQueryTest, ParseFromCommandLargeLimit) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "filter: {a: 1},"
          "limit: 8000000000}");  // 8 * 1000 * 1000 * 1000
      const NamespaceString nss("test.testns");
      const bool isExplain = false;
      unique_ptr<LiteParsedQuery> lpq(
          assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain)));
  
      ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *lpq->getLimit());
  }
  
  TEST(LiteParsedQueryTest, ParseFromCommandLargeBatchSize) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "filter: {a: 1},"
          "batchSize: 8000000000}");  // 8 * 1000 * 1000 * 1000
      const NamespaceString nss("test.testns");
      const bool isExplain = false;
      unique_ptr<LiteParsedQuery> lpq(
          assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain)));
  
      ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *lpq->getBatchSize());
  }
  
  TEST(LiteParsedQueryTest, ParseFromCommandLargeSkip) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "filter: {a: 1},"
          "skip: 8000000000}");  // 8 * 1000 * 1000 * 1000
      const NamespaceString nss("test.testns");
      const bool isExplain = false;
      unique_ptr<LiteParsedQuery> lpq(
          assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain)));
  
      ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *lpq->getSkip());
  }
  
  //
  // 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},"
          "allowPartialResults: 3}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain);
      ASSERT_NOT_OK(result.getStatus());
  }
  
  TEST(LiteParsedQueryTest, ParseFromCommandReadConcernWrongType) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "filter:  {a: 1},"
          "readConcern: 'foo'}");
      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, ParseFromCommandSkipIsZero) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "skip: 0,"
          "filter: {a: 3}}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      unique_ptr<LiteParsedQuery> lpq(
          assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain)));
      ASSERT_EQ(BSON("a" << 3), lpq->getFilter());
      ASSERT_FALSE(lpq->getSkip());
  }
  
  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, ParseFromCommandLimitIsZero) {
      BSONObj cmdObj = fromjson(
          "{find: 'testns',"
          "limit: 0,"
          "filter: {a: 3}}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      unique_ptr<LiteParsedQuery> lpq(
          assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain)));
      ASSERT_EQ(BSON("a" << 3), lpq->getFilter());
      ASSERT_FALSE(lpq->getLimit());
  }
  
  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_FALSE(lpq->getNToReturn());
  }
  
  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_TRUE(lpq->getNToReturn());
  }
  
  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, ParseCommandIgnoreShardVersionField) {
      BSONObj cmdObj = fromjson("{find: 'test.testns', shardVersion: 'foo'}");
      const NamespaceString nss("test.testns");
      bool isExplain = false;
      auto result = LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain);
      ASSERT_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_FALSE(lpq->getSkip());
      ASSERT_FALSE(lpq->getLimit());
  
      ASSERT_EQUALS(true, lpq->wantMore());
      ASSERT_FALSE(lpq->getNToReturn());
      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->isAllowPartialResults());
  }
  
  //
  // 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());
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSStringValueFails) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << "foo");
      ASSERT_NOT_OK(LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]));
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSNonIntegralValueFails) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << 100.3);
      ASSERT_NOT_OK(LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]));
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSOutOfRangeDoubleFails) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << 1e200);
      ASSERT_NOT_OK(LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]));
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSNegativeValueFails) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << -400);
      ASSERT_NOT_OK(LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]));
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSZeroSucceeds) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << 0);
      auto maxTime = LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]);
      ASSERT_OK(maxTime);
      ASSERT_EQ(maxTime.getValue(), 0);
  }
  
  TEST(LiteParsedQueryTest, ParseMaxTimeMSPositiveInRangeSucceeds) {
      BSONObj maxTimeObj = BSON(LiteParsedQuery::cmdOptionMaxTimeMS << 300);
      auto maxTime = LiteParsedQuery::parseMaxTimeMS(maxTimeObj[LiteParsedQuery::cmdOptionMaxTimeMS]);
      ASSERT_OK(maxTime);
      ASSERT_EQ(maxTime.getValue(), 300);
  }
  
  }  // namespace mongo
  }  // namespace
  