/**
 * Tests the behavior of cleanupOrphaned when the resumable range deleter is enabled. That is, tests
 * that cleanupOrphaned
 *
 * 1) Ignores the 'startingFromKey' parameter
 * 2) Waits for all possibly orphaned ranges to be deleted before returning
 * 3) Does not return 'stoppedAtKey', to preserve the former API of not returning 'stoppedAtKey'
 *    once all orphans have been deleted.
 */

(function() {

load("jstests/libs/fail_point_util.js");

const dbName = "test";
const collName = "foo";
const ns = dbName + "." + collName;

var st = new ShardingTest({shards: 2});

jsTest.log("Shard and split a collection");
assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
assert.commandWorked(st.s.adminCommand({movePrimary: dbName, to: st.shard0.shardName}));
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 1}}));

jsTest.log("Insert some documents");
const numDocs = 100;
var bulk = st.s.getCollection(ns).initializeUnorderedBulkOp();
for (var i = 0; i < numDocs; i++) {
    bulk.insert({_id: i});
}
assert.commandWorked(bulk.execute());

jsTest.log("Assert that there are no range deletion tasks");
assert.eq(0, st.shard0.getDB("config").getCollection("rangeDeletions").count());
assert.eq(0, st.shard1.getDB("config").getCollection("rangeDeletions").count());

let suspendRangeDeletionShard0 = configureFailPoint(st.shard0, "suspendRangeDeletion");

jsTest.log("Move two chunks to create two orphaned ranges on the donor");
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard1.shardName}));
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 1}, to: st.shard1.shardName}));

jsTest.log("Since the recipient does not have orphaned ranges, cleanupOrphaned should return");
assert.eq(0, st.shard1.getDB("config").getCollection("rangeDeletions").count());
assert.commandWorked(st.shard1.adminCommand({
    cleanupOrphaned: ns,
    startingFromKey: {_id: 50} /* The startingFromKey parameter should be ignored */
}));

jsTest.log("Since the donor has two orphaned ranges, cleanupOrphaned should block");
assert.eq(2, st.shard0.getDB("config").getCollection("rangeDeletions").count());
assert.commandFailedWithCode(st.shard0.adminCommand({
    cleanupOrphaned: ns,
    startingFromKey: {_id: 50} /* The startingFromKey parameter should be ignored */,
    maxTimeMS: 10 * 1000
}),
                             ErrorCodes.MaxTimeMSExpired);
assert.eq(numDocs, st.shard0.getDB(dbName).getCollection(collName).count());

jsTest.log("Once the donor can cleans up the ranges, cleanupOrphaned should eventually return.");
suspendRangeDeletionShard0.off();
const res = st.shard0.adminCommand({
    cleanupOrphaned: ns,
    startingFromKey: {_id: 50} /* The startingFromKey parameter should be ignored */
});
assert.commandWorked(res);
assert.eq(0, st.shard0.getDB("config").getCollection("rangeDeletions").count());
assert.eq(0, st.shard0.getDB(dbName).getCollection(collName).count());

// The result should not have contained 'stoppedAtKey', since to match the API of the original
// cleanupOrphaned command, no 'stoppedAtKey' is returned once all orphans have been deleted.
assert.eq(null, res.stoppedAtKey);

st.stop();
})();
