// Tests to see what validity checks are done for 10gen specific object construction

// Takes a list of constructors and returns a new list with an extra entry for each constructor with
// "new" prepended
function addConstructorsWithNew(constructorList) {
    function prependNew(constructor) {
        return "new " + constructor;
    }

    var valid = constructorList.valid;
    var invalid = constructorList.invalid;
    // We use slice(0) here to make a copy of our lists
    var validWithNew = valid.concat(valid.slice(0).map(prependNew));
    var invalidWithNew = invalid.concat(invalid.slice(0).map(prependNew));
    return {"valid": validWithNew, "invalid": invalidWithNew};
}

function clientEvalConstructorTest(constructorList) {
    constructorList = addConstructorsWithNew(constructorList);
    constructorList.valid.forEach(function(constructor) {
        try {
            eval(constructor);
        } catch (e) {
            throw("valid constructor: " + constructor + " failed in eval context: " + e);
        }
    });
    constructorList.invalid.forEach(function(constructor) {
        assert.throws(function() {
            eval(constructor);
        }, [], "invalid constructor did not throw error in eval context: " + constructor);
    });
}

function dbEvalConstructorTest(constructorList) {
    assert.writeOK(db.evalConstructors.insert({}), "db must exist for eval to succeed");
    assert(db.evalConstructors.drop());
    constructorList = addConstructorsWithNew(constructorList);
    constructorList.valid.forEach(function(constructor) {
        try {
            db.eval(constructor);
        } catch (e) {
            throw("valid constructor: " + constructor + " failed in db.eval context: " + e);
        }
    });
    constructorList.invalid.forEach(function(constructor) {
        assert.throws(function() {
            db.eval(constructor);
        }, [], "invalid constructor did not throw error in db.eval context: " + constructor);
    });
}

function mapReduceConstructorTest(constructorList) {
    constructorList = addConstructorsWithNew(constructorList);
    t = db.mr_constructors;
    t.drop();

    t.save({"partner": 1, "visits": 9});
    t.save({"partner": 2, "visits": 9});
    t.save({"partner": 1, "visits": 11});
    t.save({"partner": 1, "visits": 30});
    t.save({"partner": 2, "visits": 41});
    t.save({"partner": 2, "visits": 41});

    constructorList.valid.forEach(function(constructor) {
        try {
            m = eval("dummy = function(){ emit( \"test\" , " + constructor + " ) }");

            r = eval("dummy = function( k , v ){ return { test : " + constructor + " } }");

            res = t.mapReduce(m, r, {out: "mr_constructors_out", scope: {xx: 1}});
        } catch (e) {
            throw("valid constructor: " + constructor + " failed in mapReduce context: " + e);
        }
    });
    constructorList.invalid.forEach(function(constructor) {
        m = eval("dummy = function(){ emit( \"test\" , " + constructor + " ) }");

        r = eval("dummy = function( k , v ){ return { test : " + constructor + " } }");

        assert.throws(function() {
            res = t.mapReduce(m, r, {out: "mr_constructors_out", scope: {xx: 1}});
        }, [], "invalid constructor did not throw error in mapReduce context: " + constructor);
    });

    db.mr_constructors_out.drop();
    t.drop();
}

function whereConstructorTest(constructorList) {
    constructorList = addConstructorsWithNew(constructorList);
    t = db.where_constructors;
    t.drop();
    assert.writeOK(t.insert({x: 1}));

    constructorList.valid.forEach(function(constructor) {
        try {
            t.findOne({$where: constructor});
        } catch (e) {
            throw("valid constructor: " + constructor + " failed in $where query: " + e);
        }
    });
    constructorList.invalid.forEach(function(constructor) {
        assert.throws(function() {
            t.findOne({$where: constructor});
        }, [], "invalid constructor did not throw error in $where query: " + constructor);
    });
}

var dbrefConstructors = {
    "valid": [
        "DBRef(\"namespace\", 0)",
        "DBRef(\"namespace\", \"test\")",
        "DBRef(\"namespace\", \"test\", \"database\")",
        "DBRef(\"namespace\", ObjectId())",
        "DBRef(\"namespace\", ObjectId(\"000000000000000000000000\"))",
        "DBRef(\"namespace\", ObjectId(\"000000000000000000000000\"), \"database\")",
    ],
    "invalid": [
        "DBRef()",
        "DBRef(true, ObjectId())",
        "DBRef(true, ObjectId(), true)",
        "DBRef(\"namespace\")",
        "DBRef(\"namespace\", ObjectId(), true)",
        "DBRef(\"namespace\", ObjectId(), 123)",
    ]
};

var dbpointerConstructors = {
    "valid": [
        "DBPointer(\"namespace\", ObjectId())",
        "DBPointer(\"namespace\", ObjectId(\"000000000000000000000000\"))",
    ],
    "invalid": [
        "DBPointer()",
        "DBPointer(true, ObjectId())",
        "DBPointer(\"namespace\", 0)",
        "DBPointer(\"namespace\", \"test\")",
        "DBPointer(\"namespace\")",
        "DBPointer(\"namespace\", ObjectId(), true)",
    ]
};

var objectidConstructors = {
    "valid": [
        'ObjectId()',
        'ObjectId("FFFFFFFFFFFFFFFFFFFFFFFF")',
    ],
    "invalid": [
        'ObjectId(5)',
        'ObjectId("FFFFFFFFFFFFFFFFFFFFFFFQ")',
    ]
};

var timestampConstructors = {
    "valid": [
        'Timestamp()',
        'Timestamp(0,0)',
        'Timestamp(1.0,1.0)',
    ],
    "invalid": [
        'Timestamp(0)',
        'Timestamp(0,0,0)',
        'Timestamp("test","test")',
        'Timestamp("test",0)',
        'Timestamp(0,"test")',
        'Timestamp(true,true)',
        'Timestamp(true,0)',
        'Timestamp(0,true)',
        'Timestamp(Math.pow(2,32),Math.pow(2,32))',
        'Timestamp(0,Math.pow(2,32))',
        'Timestamp(Math.pow(2,32),0)',
        'Timestamp(-1,-1)',
        'Timestamp(-1,0)',
        'Timestamp(0,-1)'
    ]
};

var bindataConstructors = {
    "valid": [
        'BinData(0,"test")',
    ],
    "invalid": [
        'BinData(0,"test", "test")',
        'BinData()',
        'BinData(-1, "")',
        'BinData(256, "")',
        'BinData("string","aaaa")',
        'BinData(0, true)',
        'BinData(0, null)',
        'BinData(0, undefined)',
        'BinData(0, {})',
        'BinData(0, [])',
        'BinData(0, function () {})',
    ]
};

var uuidConstructors = {
    "valid": [
        'UUID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")',
    ],
    "invalid": [
        'UUID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0)',
        'UUID()',
        'UUID("aa")',
        'UUID("invalidhex")',
        'UUID("invalidhexbutstilltherequiredlen")',
        'UUID(true)',
        'UUID(null)',
        'UUID(undefined)',
        'UUID({})',
        'UUID([])',
        'UUID(function () {})',
    ]
};

var md5Constructors = {
    "valid": [
        'MD5("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")',
    ],
    "invalid": [
        'MD5("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0)',
        'MD5()',
        'MD5("aa")',
        'MD5("invalidhex")',
        'MD5("invalidhexbutstilltherequiredlen")',
        'MD5(true)',
        'MD5(null)',
        'MD5(undefined)',
        'MD5({})',
        'MD5([])',
        'MD5(function () {})',
    ]
};

var hexdataConstructors = {
    "valid": [
        'HexData(0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")',
        'HexData(0, "")',
        'HexData(0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")',
        'HexData(0, "000000000000000000000005")',  // SERVER-9605
    ],
    "invalid": [
        'HexData(0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0)',
        'HexData()',
        'HexData(0)',
        'HexData(0, 100)',
        'HexData(-1, "")',
        'HexData(256, "")',
        'HexData(0, "aaa")',
        'HexData("string","aaaa")',
        'HexData(0, true)',
        'HexData(0, null)',
        'HexData(0, undefined)',
        'HexData(0, {})',
        'HexData(0, [])',
        'HexData(0, function () {})',
        'HexData(0, "invalidhex")',
    ]
};

var dateConstructors = {
    "valid": [
        'Date()',
        'Date(0)',
        'Date(0,0)',
        'Date(0,0,0)',
        'Date("foo")',
    ],
    "invalid": []
};

clientEvalConstructorTest(dbrefConstructors);
clientEvalConstructorTest(dbpointerConstructors);
clientEvalConstructorTest(objectidConstructors);
clientEvalConstructorTest(timestampConstructors);
clientEvalConstructorTest(bindataConstructors);
clientEvalConstructorTest(uuidConstructors);
clientEvalConstructorTest(md5Constructors);
clientEvalConstructorTest(hexdataConstructors);
clientEvalConstructorTest(dateConstructors);

dbEvalConstructorTest(dbrefConstructors);
dbEvalConstructorTest(dbpointerConstructors);
dbEvalConstructorTest(objectidConstructors);
dbEvalConstructorTest(timestampConstructors);
dbEvalConstructorTest(bindataConstructors);
dbEvalConstructorTest(uuidConstructors);
dbEvalConstructorTest(md5Constructors);
dbEvalConstructorTest(hexdataConstructors);
dbEvalConstructorTest(dateConstructors);

mapReduceConstructorTest(dbrefConstructors);
mapReduceConstructorTest(dbpointerConstructors);
mapReduceConstructorTest(objectidConstructors);
mapReduceConstructorTest(timestampConstructors);
mapReduceConstructorTest(bindataConstructors);
mapReduceConstructorTest(uuidConstructors);
mapReduceConstructorTest(md5Constructors);
mapReduceConstructorTest(hexdataConstructors);
mapReduceConstructorTest(dateConstructors);

whereConstructorTest(dbrefConstructors);
whereConstructorTest(dbpointerConstructors);
whereConstructorTest(objectidConstructors);
whereConstructorTest(timestampConstructors);
whereConstructorTest(bindataConstructors);
whereConstructorTest(uuidConstructors);
whereConstructorTest(md5Constructors);
whereConstructorTest(hexdataConstructors);
whereConstructorTest(dateConstructors);
