/*
 * Tests that if a client sends "helloOk: true" as a part of their isMaster request,
 * mongod will replace "not master" error messages with "not primary".
 *
 * In practice, drivers will send "helloOk: true" in the initial handshake when
 * opening a connection to the database.
 */

(function() {
"use strict";

const dbName = "test";
const collName = "not_primary_errors_returned_if_client_sends_helloOk";
const rst = new ReplSetTest({nodes: 2});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
const secondary = rst.getSecondary();

assert.commandWorked(primary.getDB(dbName)[collName].insert({x: 1}));
rst.awaitReplication();

// Attempts to write to a secondary should fail with NotWritablePrimary.
let res = assert.commandFailedWithCode(secondary.getDB(dbName)[collName].insert({y: 2}),
                                       ErrorCodes.NotWritablePrimary,
                                       "insert did not fail with NotWritablePrimary");
// Since the shell opens connections without using "helloOk: true", the error message
// should include "not master".
assert(res.errmsg.includes("not master"), res);

// Set secondaryOk to false, disallowing reads on secondaries.
secondary.getDB(dbName).getMongo().setSecondaryOk(false);
assert.eq(secondary.getDB(dbName).getMongo().getSecondaryOk(), false);
res = assert.commandFailedWithCode(secondary.getDB(dbName).runCommand({find: collName}),
                                   ErrorCodes.NotPrimaryNoSecondaryOk,
                                   "find did not fail with NotPrimaryNoSecondaryOk");
// Since we did not send "helloOk: true", the error message should include "not master".
assert(res.errmsg.includes("not master"), res);

// An isMaster response will not contain "helloOk: true" if the client does not send
// helloOk in the request.
res = assert.commandWorked(secondary.getDB(dbName).adminCommand({isMaster: 1}));
assert.eq(res.helloOk, undefined);

try {
    // "helloOk" field type must be a boolean.
    secondary.getDB(dbName).adminCommand({isMaster: 1, helloOk: 3});
} catch (e) {
    assert.eq(e.code, ErrorCodes.TypeMismatch, "invalid helloOk field did not fail to parse");
}

// Run the isMaster command with "helloOk: true" on the secondary.
res = assert.commandWorked(secondary.getDB(dbName).adminCommand({isMaster: 1, helloOk: true}));
// The response should contain "helloOk: true", which indicates to the client that the
// server supports the hello command.
assert.eq(res.helloOk, true);

// Attempts to write to a secondary should fail with NotWritablePrimary.
res = assert.commandFailedWithCode(secondary.getDB(dbName)[collName].insert({z: 3}),
                                   ErrorCodes.NotWritablePrimary,
                                   "insert did not fail with NotWritablePrimary");
// Since we sent "helloOk: true", the error message should include "not primary".
assert(res.errmsg.includes("not primary"), res);
assert(!res.errmsg.includes("not master"), res);

// secondaryOk was already set to false, so the following read should still fail with
// NotPrimaryNoSecondaryOk.
assert.eq(secondary.getDB(dbName).getMongo().getSecondaryOk(), false);
res = assert.commandFailedWithCode(secondary.getDB(dbName).runCommand({find: collName}),
                                   ErrorCodes.NotPrimaryNoSecondaryOk,
                                   "find did not fail with NotPrimaryNoSecondaryOk");
// Since we sent "helloOk: true", the error message should include "not primary".
assert(res.errmsg.includes("not primary"), res);
assert(!res.errmsg.includes("not master"), res);

rst.stopSet();
})();