//
// Ensures GLE correctly reports basic write stats and failures
// Note that test should work correctly with and without write commands.
//

var options = { separateConfig : true };

var st = new ShardingTest({ shards : 2, mongos : 1, other : options });
st.stopBalancer();

var mongos = st.s0;
var admin = mongos.getDB( "admin" );
var config = mongos.getDB( "config" );
var coll = mongos.getCollection( jsTestName() + ".coll" );
var shards = config.shards.find().toArray();

assert.commandWorked( admin.runCommand({ enableSharding : coll.getDB().toString() }) );
printjson( admin.runCommand({ movePrimary : coll.getDB().toString(), to : shards[0]._id }) );
assert.commandWorked( admin.runCommand({ shardCollection : coll.toString(), key : { _id : 1 } }) );
assert.commandWorked( admin.runCommand({ split : coll.toString(), middle : { _id : 0 } }) );
assert.commandWorked( admin.runCommand({ moveChunk : coll.toString(),
                                         find : { _id : 0 },
                                         to : shards[1]._id }) );

st.printShardingStatus();

var gle = null;

//
// Successful insert
coll.remove({});
coll.insert({ _id : -1 });
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert.eq(coll.count(), 1);

//
// Successful update
coll.remove({});
coll.insert({ _id : 1 });
coll.update({ _id : 1 }, { $set : { foo : "bar" } });
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert(gle.updatedExisting);
assert.eq(gle.n, 1);
assert.eq(coll.count(), 1);

//
// Successful multi-update
coll.remove({});
coll.insert({ _id : 1 });
coll.update({ }, { $set : { foo : "bar" } }, false, true);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert(gle.updatedExisting);
assert.eq(gle.n, 1);
assert.eq(coll.count(), 1);

//
// Successful upsert
coll.remove({});
coll.update({ _id : 1 }, { _id : 1 }, true);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert(!gle.updatedExisting);
assert.eq(gle.n, 1);
assert.eq(gle.upserted, 1);
assert.eq(coll.count(), 1);

//
// Successful upserts
coll.remove({});
coll.update({ _id : -1 }, { _id : -1 }, true);
coll.update({ _id : 1 }, { _id : 1 }, true);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert(!gle.updatedExisting);
assert.eq(gle.n, 1);
assert.eq(gle.upserted, 1);
assert.eq(coll.count(), 2);

//
// Successful remove
coll.remove({});
coll.insert({ _id : 1 });
coll.remove({ _id : 1 });
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert('err' in gle);
assert(!gle.err);
assert.eq(gle.n, 1);
assert.eq(coll.count(), 0);

//
// Error on one host during update
coll.remove({});
coll.update({ _id : 1 }, { $invalid : "xxx" }, true);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert(gle.err);
assert(gle.code);
assert(!gle.errmsg);
assert(gle.singleShard);
assert.eq(coll.count(), 0);

//
// Error on two hosts during remove
coll.remove({});
coll.remove({ $invalid : 'remove' });
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert(gle.err);
assert(gle.code);
assert(!gle.errmsg);
assert(gle.shards);
assert.eq(coll.count(), 0);

//
// Repeated calls to GLE should work
coll.remove({});
coll.update({ _id : 1 }, { $invalid : "xxx" }, true);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert(gle.err);
assert(gle.code);
assert(!gle.errmsg);
assert(gle.singleShard);
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert(gle.err);
assert(gle.code);
assert(!gle.errmsg);
assert(gle.singleShard);
assert.eq(coll.count(), 0);

//
// First shard down
//

//
// Successful bulk insert on two hosts, host dies before gle (error contacting host)
coll.remove({});
coll.insert([{ _id : 1 }, { _id : -1 }]);
// Wait for write to be written to shards before shutting it down.
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
MongoRunner.stopMongod( st.shard0 );
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
// Should get an error about contacting dead host.
assert(!gle.ok);
assert(gle.errmsg);

//
// Failed insert on two hosts, first host dead
// NOTE: This is DIFFERENT from 2.4, since we don't need to contact a host we didn't get
// successful writes from.
coll.remove({ _id : 1 });
// The insert throws if write commands are enabled, since we get a response
if ( coll.getMongo().useWriteCommands() ) {
    assert.throws( function() {
        coll.insert([{ _id : 1 }, { _id : -1 }]);
    });
}
else {
    coll.insert([{ _id : 1 }, { _id : -1 }]);
}
printjson(gle = coll.getDB().runCommand({ getLastError : 1 }));
assert(gle.ok);
assert(gle.err);
assert.eq(coll.count({ _id : 1 }), 1);

jsTest.log("DONE!");

st.stop();

