// expression_parser_leaf_test.cpp

/**
 *    Copyright (C) 2013 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <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/unittest/unittest.h"

#include "mongo/db/matcher/expression_parser.h"

#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_leaf.h"

namespace mongo {

    TEST( MatchExpressionParserLeafTest, SimpleEQ2 ) {
        BSONObj query = BSON( "x" << BSON( "$eq" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleEQUndefined ) {
        BSONObj query = BSON( "x" << BSON( "$eq" << BSONUndefined ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, SimpleGT1 ) {
        BSONObj query = BSON( "x" << BSON( "$gt" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleLT1 ) {
        BSONObj query = BSON( "x" << BSON( "$lt" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleGTE1 ) {
        BSONObj query = BSON( "x" << BSON( "$gte" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleLTE1 ) {
        BSONObj query = BSON( "x" << BSON( "$lte" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleNE1 ) {
        BSONObj query = BSON( "x" << BSON( "$ne" << 2 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleModBad1 ) {
        BSONObj query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( 3 << 2 ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( 3 ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( !result.isOK() );

        query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( 3 << 2 << 4 ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( !result.isOK() );

        query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( "q" << 2 ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( !result.isOK() );

        query = BSON( "x" << BSON( "$mod" << 3 ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( !result.isOK() );

        query = BSON( "x" << BSON( "$mod" << BSON( "a" << 1 << "b" << 2 ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( !result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, SimpleMod1 ) {
        BSONObj query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( 3 << 2 ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 4 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 8 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleModNotNumber ) {
        BSONObj query = BSON( "x" << BSON( "$mod" << BSON_ARRAY( 2 << "r" ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 4 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "a" ) ) );
    }


    TEST( MatchExpressionParserLeafTest, SimpleIN1 ) {
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY( 2 << 3 ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, INSingleDBRef ) {
        OID oid = OID::gen();
        BSONObj query =
            BSON( "x" << BSON( "$in" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        OID oidx = OID::gen();
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$id" << oid << "$ref" << "coll" << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$id" << oid << "$ref" << "coll" << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$id" << oid << "$ref" << "coll" << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oid << "$db" << "dbx" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON(  "$db" << "db" << "$ref" << "coll" << "$id" << oid ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) ) );
    }

    TEST( MatchExpressionParserLeafTest, INMultipleDBRef ) {
        OID oid = OID::gen();
        OID oidy = OID::gen();
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY(
            BSON( "$ref" << "colly" << "$id" << oidy << "$db" << "db" ) <<
            BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        OID oidx = OID::gen();
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$id" << oid << "$ref" << "coll" << "$db" << "db" ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oidy << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "colly" << "$id" << oid << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$id" << oid << "$ref" << "coll" << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "dbx" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$id" << oidy << "$ref" << "colly" << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "colly" << "$id" << oidx << "$db" << "db" ) ) ) ) );
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "dbx" ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "colly" << "$id" << oidy << "$db" << "db" ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "colly" << "$id" << oidy << "$db" << "db" ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "$db" << "db" ) <<
                BSON( "$ref" << "colly" << "$id" << oidy << "$db" << "db" ) ) ) ) );
    }

    TEST( MatchExpressionParserLeafTest, INDBRefWithOptionalField1 ) {
        OID oid = OID::gen();
        BSONObj query =
            BSON( "x" << BSON( "$in" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        OID oidx = OID::gen();
        ASSERT( !result.getValue()->matchesBSON(
            BSON( "x" << BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ) ) ) ) );
        ASSERT( result.getValue()->matchesBSON(
            BSON( "x" << BSON_ARRAY(
                BSON( "$ref" << "collx" << "$id" << oidx << "foo" << 12345 ) <<
                BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ) ) ) ) );
    }

    TEST( MatchExpressionParserLeafTest, INInvalidDBRefs ) {
        // missing $id
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY(
            BSON( "$ref" << "coll" ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        result = MatchExpressionParser::parse( query );

        // second field is not $id
        query = BSON( "x" << BSON( "$in" << BSON_ARRAY(
                    BSON( "$ref" << "coll" <<
                          "$foo" << 1 ) ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        OID oid = OID::gen();

        // missing $ref field
        query = BSON( "x" << BSON( "$in" << BSON_ARRAY(
                    BSON( "$id" << oid <<
                          "foo" << 3 ) ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        // missing $id and $ref field
        query = BSON( "x" << BSON( "$in" << BSON_ARRAY(
                    BSON( "$db" << "test" <<
                          "foo" << 3 ) ) ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

    }

    TEST( MatchExpressionParserLeafTest, INExpressionDocument ) {
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY( BSON( "$foo" << 1 ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INNotArray ) {
        BSONObj query = BSON( "x" << BSON( "$in" << 5 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INUndefined ) {
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY( BSONUndefined ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INNotElemMatch ) {
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY( BSON( "$elemMatch" << 1 ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INRegexTooLong ) {
        string tooLargePattern( 50 * 1000, 'z' );
        BSONObjBuilder inArray;
        inArray.appendRegex( "0", tooLargePattern, "" );
        BSONObjBuilder operand;
        operand.appendArray( "$in", inArray.obj() );
        BSONObj query = BSON( "x" << operand.obj() );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INRegexTooLong2 ) {
        string tooLargePattern( 50 * 1000, 'z' );
        BSONObj query = BSON( "x" << BSON( "$in" << BSON_ARRAY( BSON( "$regex" << tooLargePattern ) ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, INRegexStuff ) {
        BSONObjBuilder inArray;
        inArray.appendRegex( "0", "^a", "" );
        inArray.appendRegex( "1", "B", "i" );
        inArray.append( "2", 4 );
        BSONObjBuilder operand;
        operand.appendArray( "$in", inArray.obj() );

        BSONObj query = BSON( "a" << operand.obj() );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        BSONObj matchFirst = BSON( "a" << "ax" );
        BSONObj matchFirstRegex = BSONObjBuilder().appendRegex( "a", "^a", "" ).obj();
        BSONObj matchSecond = BSON( "a" << "qqb" );
        BSONObj matchSecondRegex = BSONObjBuilder().appendRegex( "a", "B", "i" ).obj();
        BSONObj matchThird = BSON( "a" << 4 );
        BSONObj notMatch = BSON( "a" << "l" );
        BSONObj notMatchRegex = BSONObjBuilder().appendRegex( "a", "B", "" ).obj();

        ASSERT( result.getValue()->matchesBSON( matchFirst ) );
        ASSERT( result.getValue()->matchesBSON( matchFirstRegex ) );
        ASSERT( result.getValue()->matchesBSON( matchSecond ) );
        ASSERT( result.getValue()->matchesBSON( matchSecondRegex ) );
        ASSERT( result.getValue()->matchesBSON( matchThird ) );
        ASSERT( !result.getValue()->matchesBSON( notMatch ) );
        ASSERT( !result.getValue()->matchesBSON( notMatchRegex ) );
    }

    TEST( MatchExpressionParserLeafTest, SimpleNIN1 ) {
        BSONObj query = BSON( "x" << BSON( "$nin" << BSON_ARRAY( 2 << 3 ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 1 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 2 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 3 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, NINNotArray ) {
        BSONObj query = BSON( "x" << BSON( "$nin" << 5 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }


    TEST( MatchExpressionParserLeafTest, Regex1 ) {
        BSONObjBuilder b;
        b.appendRegex( "x", "abc", "i" );
        BSONObj query = b.obj();
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "ABC" ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "AC" ) ) );
    }

    TEST( MatchExpressionParserLeafTest, Regex2 ) {
        BSONObj query = BSON( "x" << BSON( "$regex" << "abc" << "$options" << "i" ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "ABC" ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "AC" ) ) );
    }

    TEST( MatchExpressionParserLeafTest, Regex3 ) {
        BSONObj query = BSON( "x" << BSON( "$options" << "i" << "$regex" << "abc" ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        log() << "result: " << result << endl;
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "ABC" ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "AC" ) ) );
    }


    TEST( MatchExpressionParserLeafTest, RegexBad ) {
        BSONObj query = BSON( "x" << BSON( "$regex" << "abc" << "$optionas" << "i" ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        // $regex does not with numbers
        query = BSON( "x" << BSON( "$regex" << 123 ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        query = BSON( "x" << BSON( "$regex" << BSON_ARRAY("abc") ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        query = BSON( "x" << BSON( "$optionas" << "i" ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );

        query = BSON( "x" << BSON( "$options" << "i" ) );
        result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

    TEST( MatchExpressionParserLeafTest, ExistsYes1 ) {
        BSONObjBuilder b;
        b.appendBool( "$exists", true );
        BSONObj query = BSON( "x" << b.obj() );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "y" << "AC" ) ) );
    }

    TEST( MatchExpressionParserLeafTest, ExistsNO1 ) {
        BSONObjBuilder b;
        b.appendBool( "$exists", false );
        BSONObj query = BSON( "x" << b.obj() );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( result.getValue()->matchesBSON( BSON( "y" << "AC" ) ) );
    }

    TEST( MatchExpressionParserLeafTest, Type1 ) {
        BSONObj query = BSON( "x" << BSON( "$type" << String ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, Type2 ) {
        BSONObj query = BSON( "x" << BSON( "$type" << (double)NumberDouble ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( result.getValue()->matchesBSON( BSON( "x" << 5.3 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, TypeDoubleOperator ) {
        BSONObj query = BSON( "x" << BSON( "$type" << 1.5 ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5.3 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, TypeNull ) {
        BSONObj query = BSON( "x" << BSON( "$type" << jstNULL ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSONObj() ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
        BSONObjBuilder b;
        b.appendNull( "x" );
        ASSERT( result.getValue()->matchesBSON( b.obj() ) );
    }

    TEST( MatchExpressionParserLeafTest, TypeBadType ) {
        BSONObjBuilder b;
        b.append( "$type", ( JSTypeMax + 1 ) );
        BSONObj query = BSON( "x" << b.obj() );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_TRUE( result.isOK() );

        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5.3 ) ) );
        ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 5 ) ) );
    }

    TEST( MatchExpressionParserLeafTest, TypeBad ) {
        BSONObj query = BSON( "x" << BSON( "$type" << BSON( "x" << 1 ) ) );
        StatusWithMatchExpression result = MatchExpressionParser::parse( query );
        ASSERT_FALSE( result.isOK() );
    }

}
