/**
 * Tests that step-up to become a primary during rollback won't crash the server with
 * OplogOutOfOrder error after becoming a secondary again.
 * Exercises the fix for SERVER-61977.
 * @tags: [uses_transactions]
 */

import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {restartServerReplication, stopServerReplication} from "jstests/libs/write_concern_util.js";

const rst = new ReplSetTest({
    name: jsTestName(),
    nodes: 3,
    settings: {chainingAllowed: false, catchUpTimeoutMillis: 0},
    useBridge: true
});

rst.startSet();
rst.initiate();
const node0 = rst.getPrimary();
const node1 = rst.getSecondaries()[0];
const node2 = rst.getSecondaries()[1];
const testDB = node0.getDB('test');
const collName = jsTestName();
const coll = testDB[collName];

assert.commandWorked(testDB.createCollection(coll.getName()));
rst.awaitReplication();

stopServerReplication([node1, node2]);

// Add a non replicated write to the node0 to be rolled back later.
assert.commandWorked(coll.insert({_id: "write 1"}, {writeConcern: {w: 1}}));

let rollbackFp = configureFailPoint(node0, "rollbackHangBeforeTransitioningToRollback");
rst.stepUp(node2, {awaitReplicationBeforeStepUp: false});

// When node0 starts syncing from the node2, it will rollback the write that didn't get replicated.
rollbackFp.wait();

// Step-up node0 while it is hung at rollback.
rst.stepUp(node0, {awaitReplicationBeforeStepUp: false});

// Rollback will fail because we do not allow transitioning to follower mode from node0.
rollbackFp.off();

// Wait for node0 to step-up to avoid InterrupetedDueToReplStateChange errors in the inserts below.
restartServerReplication([node1, node2]);
rst.awaitNodesAgreeOnPrimary(rst.timeoutMS, rst.nodes, node0);
rst.awaitReplication();

// Advance the lastApplied on node0.
assert.commandWorked(coll.insert({_id: "write 2"}));
assert.commandWorked(coll.insert({_id: "write 3"}));
rst.awaitReplication();

// When node0 stepped down it will start syncing again from node2, and it shouldn't crash trying to
// apply entries older than the lastApplied.
rst.stepUp(node2);
rst.awaitNodesAgreeOnPrimary(rst.timeoutMS, rst.nodes, node2);
rst.awaitReplication();

// Verify node0 can replicate new writes.
assert.commandWorked(node2.getDB('test')[collName].insert({_id: "write 4"}));
rst.awaitReplication();

rst.stopSet();
