/**
 * Tests that the createIndex command accepts a prepareUnique field and works accordingly.
 *
 * @tags: [assumes_no_implicit_collection_creation_after_drop]
 */

const collName_prefix = "index_prepareUnique";
let count = 0;
const coll1 = db.getCollection(collName_prefix + count++);
coll1.drop();

// The 'prepareUnique' option doesn't affect existing keys in the index.
assert.commandWorked(coll1.insert({_id: 0, a: 1}));
assert.commandWorked(coll1.insert({_id: 1, a: 1}));

// Starts rejecting new duplicate keys.
assert.commandWorked(coll1.createIndex({a: 1}, {prepareUnique: true}));

// Disallows creating another index on the same key with a different option.
assert.commandFailedWithCode(coll1.createIndex({a: 1}, {prepareUnique: false}),
                             ErrorCodes.IndexOptionsConflict);

// Checks the index is rejecting duplicates but accepting other keys.
assert.commandFailedWithCode(coll1.insert({_id: 2, a: 1}), ErrorCodes.DuplicateKey);
assert.commandWorked(coll1.insert({_id: 3, a: 2}));

// Checks that the prepareUnique field exists when getIndexes is called.
let indexesWithPrepareUnique = coll1.getIndexes().filter(function(doc) {
    return friendlyEqual(doc.prepareUnique, true);
});
assert.eq(1, indexesWithPrepareUnique.length);

// Removes the field and checks the index works as a regular index.
assert.commandWorked(coll1.runCommand(
    {collMod: coll1.getName(), index: {keyPattern: {a: 1}, prepareUnique: false}}));
assert.commandWorked(coll1.insert({_id: 2, a: 1}));

// Checks that the prepareUnique field is removed.
indexesWithPrepareUnique = coll1.getIndexes().filter(function(doc) {
    return friendlyEqual(doc.prepareUnique, true);
});
assert.eq(0, indexesWithPrepareUnique.length);

// Setting 'prepareUnique' on a unique index will be a no-op.
const coll2 = db.getCollection(collName_prefix + count++);
coll2.drop();

assert.commandWorked(coll2.createIndex({a: 1}, {unique: true}));
assert.commandWorked(
    db.runCommand({collMod: coll2.getName(), index: {keyPattern: {a: 1}, prepareUnique: true}}));
// Checks that the prepareUnique field does not exist when getIndexes is called.
indexesWithPrepareUnique = coll2.getIndexes().filter(function(doc) {
    return friendlyEqual(doc.prepareUnique, true);
});
assert.eq(0, indexesWithPrepareUnique.length);

// The 'prepareUnique' and 'unique' options cannot be both set when creating an index.
const coll3 = db.getCollection(collName_prefix + count++);
coll3.drop();

assert.commandFailedWithCode(coll3.createIndex({a: 1}, {unique: true, prepareUnique: true}),
                             ErrorCodes.CannotCreateIndex);

// The 'prepareUnique: false' can be passed along with the 'unique' option when creating an index.
const coll4 = db.getCollection(collName_prefix + count++);
coll4.drop();

assert.commandWorked(coll4.createIndex({a: 1}, {unique: true, prepareUnique: false}));
// Checks that the prepareUnique field does not exist when getIndexes is called.
indexesWithPrepareUnique = coll4.getIndexes().filter(function(doc) {
    return friendlyEqual(doc.prepareUnique, true);
});
assert.eq(0, indexesWithPrepareUnique.length);