// Validate that write blocking mode prevents reaping of user, but not system, TTL indexes
// @tags: [
//   creates_and_authenticates_user,
//   requires_auth,
//   requires_fcv_60,
//   requires_non_retryable_commands,
//   requires_replication,
//   # system.js stored functions only work for collections that live on the db-primary shard so
//   # we have to make sure it wont be moved anywhere by the balancer
//   assumes_balancer_off,
// ]

import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";

// Test on replset primary
const rst = new ReplSetTest({
    nodes: 1,
    nodeOptions: {
        setParameter: {
            "ttlMonitorSleepSecs": 1,
        }
    }
});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();

function runTest(conn, testCase) {
    const admin = conn.getDB("admin");

    // Make sure the TTLMonitor is disabled, while not holding any global locks
    const pauseTtl = configureFailPoint(primary, 'hangTTLMonitorBetweenPasses');
    pauseTtl.wait();

    function runTTLMonitor() {
        const ttlPass = admin.serverStatus().metrics.ttl.passes;
        assert.commandWorked(
            admin.runCommand({configureFailPoint: "hangTTLMonitorBetweenPasses", mode: {skip: 1}}));
        assert.soon(() => admin.serverStatus().metrics.ttl.passes >= ttlPass + 1,
                    "TTL monitor didn't run before timing out.");
    }

    // Set up data and TTL indexes
    for (const target of testCase) {
        target.db = conn.getDB(target.dbname);
        target.col = target.db.getCollection(target.colName);

        target.col.insertOne({"createdAt": new Date(), "logEvent": 2, "logMessage": "Success!"});

        assert.commandWorked(target.col.createIndex({"createdAt": 1}, {expireAfterSeconds: 0}));
    }

    // Enable global write block mode
    assert.commandWorked(admin.runCommand({setUserWriteBlockMode: 1, global: true}));
    for (const target of testCase) {
        assert.eq(1, target.col.count());
    }

    // Run the TTLMonitor, and expect user collections to remain unreaped
    runTTLMonitor();
    for (const target of testCase) {
        if (target.expectReap) {
            assert.eq(0, target.col.count());
        } else {
            assert.eq(1, target.col.count());
            checkLog.containsJson(conn, 5400703, {
                "error": {
                    "code": ErrorCodes.UserWritesBlocked,
                    "codeName": "UserWritesBlocked",
                    "errmsg":
                        "Plan executor error during delete :: caused by :: User writes blocked"
                }
            });
        }
    }

    // Disable write blocking, then run the TTLMonitor, expecting it to reap all collections
    assert.commandWorked(admin.runCommand({setUserWriteBlockMode: 1, global: false}));
    runTTLMonitor();

    for (const target of testCase) {
        // All documents should be reaped now
        assert.soon(() => target.col.count() == 0);

        // Finally, cleanup the collection
        if (target.colName != "system.js") {
            assert(target.col.drop());
        }
    }
}

runTest(primary, [
    // Collections in a regular database should not be reaped
    {dbname: "db", colName: "user_write_blocking_ttl_index", expectReap: false},
    // Not even system collections in a regular database should be reaped
    {dbname: "db2", colName: "system.js", expectReap: false},
    // Collections in system databases should be reaped
    {dbname: "admin", colName: "user_write_blocking_ttl_index", expectReap: true},
    {dbname: "config", colName: "user_write_blocking_ttl_index", expectReap: true},
    {dbname: "local", colName: "user_write_blocking_ttl_index", expectReap: true}
]);
rst.stopSet();
