// Test for SERVER-9129
// Verify global scope data does not persist past logout or auth.
// NOTE: Each test case covers 3 state transitions:
//          no auth       -> auth user 'a'
//          auth user 'a' -> auth user 'b'
//          auth user 'b' -> logout
//
//       These transitions are tested for dbEval, $where, MapReduce and $group

var conn = MongoRunner.runMongod({smallfiles: ""});
var test = conn.getDB("test");

// insert a single document and add two test users
test.foo.insert({a: 1});
assert.eq(1, test.foo.findOne().a);
test.createUser({user: 'a', pwd: 'a', roles: jsTest.basicUserRoles});
test.createUser({user: 'b', pwd: 'b', roles: jsTest.basicUserRoles});

function missingOrEquals(string) {
    return 'function() { ' +
        'var global = function(){return this;}.call();'
        // Uncomment the next line when debugging.
        // + 'print(global.hasOwnProperty("someGlobal") ? someGlobal : "MISSING" );'
        +
        'return !global.hasOwnProperty("someGlobal")' +
        '    || someGlobal == unescape("' + escape(string) + '");' +
        '}()';
}

function testDbEval() {
    // set the global variable 'someGlobal' before authenticating
    test.eval('someGlobal = "noUsers";');

    // test new user auth causes scope to be cleared
    test.auth('a', 'a');
    assert(test.eval('return ' + missingOrEquals('a')), "dbEval: Auth user 'a'");

    // test auth as another user causes scope to be cleared
    test.eval('someGlobal = "a";');
    test.auth('b', 'b');
    assert(test.eval('return ' + missingOrEquals('a&b')), "dbEval: Auth user 'b'");

    // test user logout causes scope to be cleared
    test.eval('someGlobal = "a&b";');
    test.logout();
    assert(test.eval('return ' + missingOrEquals('noUsers')), "dbEval: log out");
}
testDbEval();
testDbEval();

// test $where
function testWhere() {
    // set the global variable 'someGlobal' before authenticating
    test.foo.findOne({$where: 'someGlobal = "noUsers";'});

    // test new user auth causes scope to be cleared
    test.auth('a', 'a');
    assert.eq(
        1, test.foo.count({$where: 'return ' + missingOrEquals('a')}), "$where: Auth user 'a");

    // test auth as another user causes scope to be cleared
    test.foo.findOne({$where: 'someGlobal = "a";'});
    test.auth('b', 'b');
    assert(test.foo.count({$where: 'return ' + missingOrEquals('a&b')}), "$where: Auth user 'b'");
    // test user logout causes scope to be cleared
    test.foo.findOne({$where: 'someGlobal = "a&b";'});
    test.logout();
    assert(test.foo.count({$where: 'return ' + missingOrEquals('noUsers')}), "$where: log out");
}
testWhere();
testWhere();

function testMapReduce() {
    var mapSet = function(string) {
        return Function('someGlobal = "' + string + '"');
    };
    var mapGet = function(string) {
        return Function('assert(' + missingOrEquals(string) + ')');
    };
    var reduce = function(k, v) {};
    var setGlobalInMap = function(string) {
        test.foo.mapReduce(mapSet(string), reduce, {out: {inline: 1}});
    };
    var getGlobalFromMap = function(string) {
        test.foo.mapReduce(mapGet(string), reduce, {out: {inline: 1}});
    };

    // set the global variable 'someGlobal' before authenticating
    setGlobalInMap('noUsers');

    // test new user auth causes scope to be cleared
    test.auth('a', 'a');
    assert.doesNotThrow(function() {
        getGlobalFromMap('a');
    }, [], "M/R: Auth user 'a'");

    // test auth as another user causes scope to be cleared
    setGlobalInMap('a');
    test.auth('b', 'b');
    assert.doesNotThrow(function() {
        getGlobalFromMap('a&b');
    }, [], "M/R: Auth user 'b'");

    // test user logout causes scope to be cleared
    setGlobalInMap('a&b');
    test.logout();
    assert.doesNotThrow(function() {
        getGlobalFromMap('noUsers');
    }, [], "M/R: Log out");
}
testMapReduce();
testMapReduce();

function testGroup() {
    var setGlobalInGroup = function(string) {
        return test.foo.group({
            key: 'a',
            reduce: Function('doc1', 'agg', 'someGlobal = "' + string + '"'),
            initial: {}
        });
    };
    var getGlobalFromGroup = function(string) {
        return test.foo.group({
            key: 'a',
            reduce: Function('doc1', 'agg', 'assert(' + missingOrEquals(string) + ')'),
            initial: {}
        });
    };

    // set the global variable 'someGlobal' before authenticating
    setGlobalInGroup('noUsers');

    // test new user auth causes scope to be cleared
    test.auth('a', 'a');
    assert.doesNotThrow(getGlobalFromGroup, ['a'], "Group: Auth user 'a'");

    // test auth as another user causes scope to be cleared
    setGlobalInGroup('a');
    test.auth('b', 'b');
    assert.doesNotThrow(getGlobalFromGroup, ['a&b'], "Group: Auth user 'b'");

    // test user logout causes scope to be cleared
    setGlobalInGroup('a&b');
    test.logout();
    assert.doesNotThrow(getGlobalFromGroup, ['noUsers'], "Group: Log out");
}
testGroup();
testGroup();
