TEST_F(DocumentSourceMatchTest, RedactSafePortion) {
    auto expCtx = getExpCtx();
    auto assertExpectedRedactSafePortion = [&expCtx](string input, string safePortion) {
        try {
            auto match = DocumentSourceMatch::create(fromjson(input), expCtx);
            ASSERT_BSONOBJ_EQ(match->redactSafePortion(), fromjson(safePortion));
        } catch (...) {
            LOGV2(20899, "Problem with redactSafePortion() of: {input}", "input"_attr = input);
            throw;
        }
    };

    // Empty
    assertExpectedRedactSafePortion("{}", "{}");

    // Basic allowed things
    assertExpectedRedactSafePortion("{a:1}", "{a:1}");

    assertExpectedRedactSafePortion("{a:'asdf'}", "{a:'asdf'}");

    assertExpectedRedactSafePortion("{a:/asdf/i}", "{a:/asdf/i}");

    assertExpectedRedactSafePortion("{a: {$regex: 'adsf'}}", "{a: {$regex: 'adsf'}}");

    assertExpectedRedactSafePortion("{a: {$regex: 'adsf', $options: 'i'}}",
                                    "{a: {$regex: 'adsf', $options: 'i'}}");

    assertExpectedRedactSafePortion("{a: {$mod: [1, 0]}}", "{a: {$mod: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$type: 1}}", "{a: {$type: 1}}");

    // Basic disallowed things
    assertExpectedRedactSafePortion("{a: null}", "{}");

    assertExpectedRedactSafePortion("{a: {}}", "{}");

    assertExpectedRedactSafePortion("{a: []}", "{}");

    assertExpectedRedactSafePortion("{'a.0': 1}", "{}");

    assertExpectedRedactSafePortion("{'a.0.b': 1}", "{}");

    assertExpectedRedactSafePortion("{a: {$ne: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$nin: [1, 2, 3]}}", "{}");

    assertExpectedRedactSafePortion("{a: {$exists: true}}",
                                    "{}");  // could be allowed but currently isn't

    assertExpectedRedactSafePortion("{a: {$exists: false}}", "{}");  // can never be allowed

    assertExpectedRedactSafePortion("{a: {$size: 1}}", "{}");

    assertExpectedRedactSafePortion("{$nor: [{a:1}]}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMinItems: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMaxItems: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaUniqueItems: true}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMinLength: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMaxLength: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaFmod: [4.5, 2.3]}}", "{}");

    assertExpectedRedactSafePortion(
        "{a: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$gt: 0}}}}}",
        "{}");

    assertExpectedRedactSafePortion(
        "{a: {$_internalSchemaAllElemMatchFromIndex: [3, {a: {$lt: 4}}]}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaType: 2}}", "{}");

    // In some cases, $_internalExprEq could be redact-safe (just like a regular $eq match
    // expression), but this optimization is not yet implemented.
    assertExpectedRedactSafePortion("{a: {$_internalExprEq: 2}}", "{}");

    // Combinations
    assertExpectedRedactSafePortion("{a:1, b: 'asdf'}", "{a:1, b: 'asdf'}");

    assertExpectedRedactSafePortion("{a:1, b: null}", "{a:1}");

    assertExpectedRedactSafePortion("{a:null, b: null}", "{}");

    // $elemMatch

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b: 1}}}", "{a: {$elemMatch: {b: 1}}}");

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b:null}}}", "{}");

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b:null, c:1}}}",
                                    "{a: {$elemMatch: {c: 1}}}");

    // explicit $and
    assertExpectedRedactSafePortion("{$and:[{a: 1}]}", "{$and:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: 1}, {b: null}]}", "{$and:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: 1}, {b: null, c:1}]}", "{$and:[{a: 1}, {c:1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: null}, {b: null}]}", "{}");

    // explicit $or
    assertExpectedRedactSafePortion("{$or:[{a: 1}]}", "{$or:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$or:[{a: 1}, {b: null}]}", "{}");

    assertExpectedRedactSafePortion("{$or:[{a: 1}, {b: null, c:1}]}", "{$or:[{a: 1}, {c:1}]}");

    assertExpectedRedactSafePortion("{$or:[{a: null}, {b: null}]}", "{}");

    assertExpectedRedactSafePortion("{}", "{}");

    // $all and $in
    assertExpectedRedactSafePortion("{a: {$all: [1, 0]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$all: [1, 0, null]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$all: [{$elemMatch: {b:1}}]}}",
                                    "{}");  // could be allowed but currently isn't

    assertExpectedRedactSafePortion("{a: {$all: [1, 0, null]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$in: [1, 0]}}", "{a: {$in: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$in: [1, 0, null]}}", "{}");

    {
        const char* comparisonOps[] = {"$gt", "$lt", "$gte", "$lte", nullptr};
        for (int i = 0; comparisonOps[i]; i++) {
            const char* op = comparisonOps[i];
            assertExpectedRedactSafePortion(string("{a: {") + op + ": 1}}",
                                            string("{a: {") + op + ": 1}}");

            // $elemMatch takes direct expressions ...
            assertExpectedRedactSafePortion(string("{a: {$elemMatch: {") + op + ": 1}}}",
                                            string("{a: {$elemMatch: {") + op + ": 1}}}");

            // ... or top-level style full matches
            assertExpectedRedactSafePortion(string("{a: {$elemMatch: {b: {") + op + ": 1}}}}",
                                            string("{a: {$elemMatch: {b: {") + op + ": 1}}}}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": null}}", "{}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": {}}}", "{}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": []}}", "{}");

            assertExpectedRedactSafePortion(string("{'a.0': {") + op + ": null}}", "{}");

            assertExpectedRedactSafePortion(string("{'a.0.b': {") + op + ": null}}", "{}");
        }
    }
}
TEST_F(DocumentSourceMatchTest, RedactSafePortion) {
    auto expCtx = getExpCtx();
    auto assertExpectedRedactSafePortion = [&expCtx](string input, string safePortion) {
        try {
            auto match = DocumentSourceMatch::create(fromjson(input), expCtx);
            ASSERT_BSONOBJ_EQ(match->redactSafePortion(), fromjson(safePortion));
        } catch (...) {
            LOGV2(20899, "Problem with redactSafePortion() of: {input}", "input"_attr = input);
            throw;
        }
    };

     
    assertExpectedRedactSafePortion("{}", "{}");

     
    assertExpectedRedactSafePortion("{a:1}", "{a:1}");

    assertExpectedRedactSafePortion("{a:'asdf'}", "{a:'asdf'}");

    assertExpectedRedactSafePortion("{a:/asdf/i}", "{a:/asdf/i}");

    assertExpectedRedactSafePortion("{a: {$regex: 'adsf'}}", "{a: {$regex: 'adsf'}}");

    assertExpectedRedactSafePortion("{a: {$regex: 'adsf', $options: 'i'}}",
                                    "{a: {$regex: 'adsf', $options: 'i'}}");

    assertExpectedRedactSafePortion("{a: {$mod: [1, 0]}}", "{a: {$mod: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$type: 1}}", "{a: {$type: 1}}");

     
    assertExpectedRedactSafePortion("{a: null}", "{}");

    assertExpectedRedactSafePortion("{a: {}}", "{}");

    assertExpectedRedactSafePortion("{a: []}", "{}");

    assertExpectedRedactSafePortion("{'a.0': 1}", "{}");

    assertExpectedRedactSafePortion("{'a.0.b': 1}", "{}");

    assertExpectedRedactSafePortion("{a: {$ne: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$nin: [1, 2, 3]}}", "{}");

    assertExpectedRedactSafePortion("{a: {$exists: true}}",
                                    "{}");   

    assertExpectedRedactSafePortion("{a: {$exists: false}}", "{}");   

    assertExpectedRedactSafePortion("{a: {$size: 1}}", "{}");

    assertExpectedRedactSafePortion("{$nor: [{a:1}]}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMinItems: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMaxItems: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaUniqueItems: true}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMinLength: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaMaxLength: 1}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaFmod: [4.5, 2.3]}}", "{}");

    assertExpectedRedactSafePortion(
        "{a: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$gt: 0}}}}}",
        "{}");

    assertExpectedRedactSafePortion(
        "{a: {$_internalSchemaAllElemMatchFromIndex: [3, {a: {$lt: 4}}]}}", "{}");

    assertExpectedRedactSafePortion("{a: {$_internalSchemaType: 2}}", "{}");

     
     
    assertExpectedRedactSafePortion("{a: {$_internalExprEq: 2}}", "{}");

     
    assertExpectedRedactSafePortion("{a:1, b: 'asdf'}", "{a:1, b: 'asdf'}");

    assertExpectedRedactSafePortion("{a:1, b: null}", "{a:1}");

    assertExpectedRedactSafePortion("{a:null, b: null}", "{}");

     

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b: 1}}}", "{a: {$elemMatch: {b: 1}}}");

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b:null}}}", "{}");

    assertExpectedRedactSafePortion("{a: {$elemMatch: {b:null, c:1}}}",
                                    "{a: {$elemMatch: {c: 1}}}");

     
    assertExpectedRedactSafePortion("{$and:[{a: 1}]}", "{$and:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: 1}, {b: null}]}", "{$and:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: 1}, {b: null, c:1}]}", "{$and:[{a: 1}, {c:1}]}");

    assertExpectedRedactSafePortion("{$and:[{a: null}, {b: null}]}", "{}");

     
    assertExpectedRedactSafePortion("{$or:[{a: 1}]}", "{$or:[{a: 1}]}");

    assertExpectedRedactSafePortion("{$or:[{a: 1}, {b: null}]}", "{}");

    assertExpectedRedactSafePortion("{$or:[{a: 1}, {b: null, c:1}]}", "{$or:[{a: 1}, {c:1}]}");

    assertExpectedRedactSafePortion("{$or:[{a: null}, {b: null}]}", "{}");

    assertExpectedRedactSafePortion("{}", "{}");

     
    assertExpectedRedactSafePortion("{a: {$all: [1, 0]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$all: [1, 0, null]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$all: [{$elemMatch: {b:1}}]}}",
                                    "{}");   

    assertExpectedRedactSafePortion("{a: {$all: [1, 0, null]}}", "{a: {$all: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$in: [1, 0]}}", "{a: {$in: [1, 0]}}");

    assertExpectedRedactSafePortion("{a: {$in: [1, 0, null]}}", "{}");

    {
        const char* comparisonOps[] = {"$gt", "$lt", "$gte", "$lte", nullptr};
        for (int i = 0; comparisonOps[i]; i++) {
            const char* op = comparisonOps[i];
            assertExpectedRedactSafePortion(string("{a: {") + op + ": 1}}",
                                            string("{a: {") + op + ": 1}}");

             
            assertExpectedRedactSafePortion(string("{a: {$elemMatch: {") + op + ": 1}}}",
                                            string("{a: {$elemMatch: {") + op + ": 1}}}");

             
            assertExpectedRedactSafePortion(string("{a: {$elemMatch: {b: {") + op + ": 1}}}}",
                                            string("{a: {$elemMatch: {b: {") + op + ": 1}}}}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": null}}", "{}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": {}}}", "{}");

            assertExpectedRedactSafePortion(string("{a: {") + op + ": []}}", "{}");

            assertExpectedRedactSafePortion(string("{'a.0': {") + op + ": null}}", "{}");

            assertExpectedRedactSafePortion(string("{'a.0.b': {") + op + ": null}}", "{}");
        }
    }
}