/*
 * Test that the server is able to roll back a 'dropDatabase' entry correctly.  This test creates
 * a collection, then executes a 'dropDatabase' command, partitioning the primary such that the
 * final 'dropDatabase' oplog entry is not replicated. The test then forces rollback of that entry.
 *
 * The 'dropDatabase' command drops each collection, ensures that the last drop is committed,
 * and only then logs a 'dropDatabase' oplog entry. This is therefore the only entry that could
 * get rolled back.
 */

(function() {

load("jstests/replsets/libs/rollback_test.js");

const testName = "rollback_drop_database";
const oldDbName = "oldDatabase";
const newDbName = "newDatabase";

let rollbackTest = new RollbackTest(testName);
let rollbackNode = rollbackTest.getPrimary();
let syncSourceNode = rollbackTest.getSecondary();

// Perform initial insert (common operation).
assert.commandWorked(rollbackNode.getDB(oldDbName)["beforeRollback"].insert({"num": 1}));

// Set a failpoint on the original primary, so that it blocks after it commits the last
// 'dropCollection' entry but before the 'dropDatabase' entry is logged.
assert.commandWorked(rollbackNode.adminCommand(
    {configureFailPoint: "dropDatabaseHangBeforeInMemoryDrop", mode: "alwaysOn"}));

// Issue a 'dropDatabase' command.
let dropDatabaseFn = function() {
    const rollbackDb = "oldDatabase";
    var primary = db.getMongo();
    jsTestLog("Dropping database " + rollbackDb + " on primary node " + primary.host);
    var dbToDrop = db.getSiblingDB(rollbackDb);
    assert.commandWorked(dbToDrop.dropDatabase({w: 1}));
};
let waitForDropDatabaseToFinish = startParallelShell(dropDatabaseFn, rollbackNode.port);

// Ensure that we've hit the failpoint before moving on.
checkLog.contains(rollbackNode,
                  "dropDatabase - fail point dropDatabaseHangBeforeInMemoryDrop enabled");

// Wait for the secondary to finish dropping the collection (the last replicated entry).
// We use the default 10-minute timeout for this.
assert.soon(function() {
    let res = syncSourceNode.getDB(oldDbName).getCollectionNames().includes("beforeRollback");
    return !res;
}, "Sync source did not finish dropping collection beforeRollback", 10 * 60 * 1000);

rollbackTest.transitionToRollbackOperations();

// Allow the final 'dropDatabase' entry to be logged on the now isolated primary.
// This is the rollback node's divergent oplog entry.
assert.commandWorked(rollbackNode.adminCommand(
    {configureFailPoint: "dropDatabaseHangBeforeInMemoryDrop", mode: "off"}));
waitForDropDatabaseToFinish();
assert.eq(false, rollbackNode.getDB(oldDbName).getCollectionNames().includes("beforeRollback"));
jsTestLog("Database " + oldDbName + " successfully dropped on primary node " + rollbackNode.host);

rollbackTest.transitionToSyncSourceOperationsBeforeRollback();

// Perform an insert on another database while interfacing with the new primary.
// This is the sync source's divergent oplog entry.
assert.commandWorked(syncSourceNode.getDB(newDbName)["afterRollback"].insert({"num": 2}));

rollbackTest.transitionToSyncSourceOperationsDuringRollback();
rollbackTest.transitionToSteadyStateOperations();

rollbackTest.stop();
})();
