// Tests that the update lookup of an unsharded change stream will use the collection-default
// collation, regardless of the collation on the change stream.
//
// Collation is only supported with the find command, not with op query.
// @tags: [requires_find_command]
(function() {
    "use strict";

    // For supportsMajorityReadConcern().
    load("jstests/multiVersion/libs/causal_consistency_helpers.js");

    if (!supportsMajorityReadConcern()) {
        jsTestLog("Skipping test since storage engine doesn't support majority read concern.");
        return;
    }

    const rst = new ReplSetTest({nodes: 1});
    rst.startSet();
    rst.initiate();

    const db = rst.getPrimary().getDB("test");
    const coll = db[jsTestName()];
    const caseInsensitive = {locale: "en_US", strength: 2};
    assert.commandWorked(db.createCollection(coll.getName(), {collation: caseInsensitive}));

    // Insert some documents that have similar _ids, but differ by case and diacritics. These _ids
    // would all match the collation on the strengthOneChangeStream, but should not be confused
    // during the update lookup using the strength 2 collection default collation.
    assert.writeOK(coll.insert({_id: "abc", x: "abc"}));
    assert.writeOK(coll.insert({_id: "abç", x: "ABC"}));
    assert.writeOK(coll.insert({_id: "åbC", x: "AbÇ"}));

    const changeStreamDefaultCollation = coll.aggregate(
        [{$changeStream: {fullDocument: "updateLookup"}}, {$match: {"fullDocument.x": "abc"}}]);

    // Strength one will consider "ç" equal to "c" and "C".
    const strengthOneCollation = {locale: "en_US", strength: 1};
    const strengthOneChangeStream = coll.aggregate(
        [{$changeStream: {fullDocument: "updateLookup"}}, {$match: {"fullDocument.x": "abc"}}],
        {collation: strengthOneCollation});

    assert.writeOK(coll.update({_id: "abc"}, {$set: {updated: true}}));

    // Track the number of _id index usages to prove that the update lookup uses the _id index (and
    // therefore is using the correct collation for the lookup).
    function numIdIndexUsages() {
        return coll.aggregate([{$indexStats: {}}, {$match: {name: "_id_"}}])
            .toArray()[0]
            .accesses.ops;
    }
    const idIndexUsagesBeforeIteration = numIdIndexUsages();

    // Both cursors should produce a document describing this update, since the "x" value of the
    // first document will match both filters.
    assert.soon(() => changeStreamDefaultCollation.hasNext());
    assert.docEq(changeStreamDefaultCollation.next().fullDocument,
                 {_id: "abc", x: "abc", updated: true});
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 1);
    assert.docEq(strengthOneChangeStream.next().fullDocument,
                 {_id: "abc", x: "abc", updated: true});
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 2);

    assert.writeOK(coll.update({_id: "abç"}, {$set: {updated: true}}));
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 3);

    // Again, both cursors should produce a document describing this update.
    assert.soon(() => changeStreamDefaultCollation.hasNext());
    assert.docEq(changeStreamDefaultCollation.next().fullDocument,
                 {_id: "abç", x: "ABC", updated: true});
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 4);
    assert.docEq(strengthOneChangeStream.next().fullDocument,
                 {_id: "abç", x: "ABC", updated: true});
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 5);

    assert.writeOK(coll.update({_id: "åbC"}, {$set: {updated: true}}));
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 6);

    // Both $changeStream stages will see this update and both will look up the full document using
    // the foreign collection's default collation. However, the changeStreamDefaultCollation's
    // subsequent $match stage will reject the document because it does not consider "AbÇ" equal to
    // "abc". Only the strengthOneChangeStream will output the final document.
    assert.soon(() => strengthOneChangeStream.hasNext());
    assert.docEq(strengthOneChangeStream.next().fullDocument,
                 {_id: "åbC", x: "AbÇ", updated: true});
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 7);
    assert(!changeStreamDefaultCollation.hasNext());
    assert.eq(numIdIndexUsages(), idIndexUsagesBeforeIteration + 8);

    changeStreamDefaultCollation.close();
    strengthOneChangeStream.close();
    rst.stopSet();
}());
