diff options
Diffstat (limited to 'src/mongo/db/matcher/expression_serialization_test.cpp')
| -rw-r--r-- | src/mongo/db/matcher/expression_serialization_test.cpp | 352 |
1 files changed, 345 insertions, 7 deletions
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp index 78517a52634..3b25631cd0f 100644 --- a/src/mongo/db/matcher/expression_serialization_test.cpp +++ b/src/mongo/db/matcher/expression_serialization_test.cpp @@ -37,7 +37,16 @@ #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/matcher/matcher.h" +#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h" +#include "mongo/db/matcher/schema/expression_internal_schema_cond.h" +#include "mongo/db/matcher/schema/expression_internal_schema_eq.h" +#include "mongo/db/matcher/schema/expression_internal_schema_fmod.h" +#include "mongo/db/matcher/schema/expression_internal_schema_max_items.h" #include "mongo/db/matcher/schema/expression_internal_schema_max_length.h" +#include "mongo/db/matcher/schema/expression_internal_schema_max_properties.h" +#include "mongo/db/matcher/schema/expression_internal_schema_min_items.h" +#include "mongo/db/matcher/schema/expression_internal_schema_min_length.h" +#include "mongo/db/matcher/schema/expression_internal_schema_min_properties.h" #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/unittest/unittest.h" @@ -49,9 +58,7 @@ using std::string; using std::unique_ptr; BSONObj serialize(MatchExpression* match) { - BSONObjBuilder bob; - match->serialize(&bob, true); - return bob.obj(); + return match->serialize(); } TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) { @@ -355,7 +362,6 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithTripleNotSerializesCorrectly) { ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); } - TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); Matcher original(fromjson("{x: {$size: 2}}"), @@ -630,6 +636,32 @@ TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) { ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); } +TEST(SerializeBasic, ExpressionRegexWithoutOptionsSerializesShapeCorrectly) { + auto query = fromjson(R"({x: {$regex: ".*"}})"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + SerializationOptions opts; + opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue; + ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex" + << "\\?")), + objMatch.getValue()->serialize(opts)); +} + +TEST(SerializeBasic, ExpressionRegexWithOptionsSerializesShapeCorrectly) { + auto query = fromjson(R"({x: {$regex: ".*", $options: "m"}})"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + SerializationOptions opts; + opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue; + ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex" + << "\\?" + << "$options" + << "i")), + objMatch.getValue()->serialize(opts)); +} + TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); Matcher original(fromjson("{x: /a.b/}"), @@ -1093,7 +1125,7 @@ TEST(SerializeBasic, ExpressionNotWithDirectPathExpSerializesCorrectly) { // direct path expression child, instead creating a NOT -> AND -> path expression. This test // manually constructs such an expression in case it ever turns up, since that should still be // able to serialize. - auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}}"); + auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}"); auto equalityRHSElem = originalBSON["a"]["$not"]["$eq"]; auto equalityExpression = std::make_unique<EqualityMatchExpression>("a"_sd, equalityRHSElem); @@ -1679,7 +1711,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaMaxLengthSerializesCorrect TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}}"), + Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}"), expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); @@ -1690,7 +1722,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) { BSONObjBuilder builder; ASSERT_BSONOBJ_EQ( *reserialized.getQuery(), - fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}}")); + fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}")); ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); } @@ -1848,5 +1880,311 @@ TEST(SerializeInternalBinDataSubType, ExpressionBinDataSubTypeSerializesCorrectl ASSERT_TRUE(original.matches(obj)); } +TEST(SerializeInternalSchema, AllowedPropertiesRedactsCorrectly) { + + auto query = fromjson( + "{$_internalSchemaAllowedProperties: {properties: ['a', 'b']," + "namePlaceholder: 'i', patternProperties: [], otherwise: {i: 0}}}"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + + SerializationOptions opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$_internalSchemaAllowedProperties": { + "properties": "?array<?string>", + "namePlaceholder": "i", + "patternProperties": [], + "otherwise": { + "HASH<i>": { + "$eq": "?number" + } + } + } + })", + objMatch.getValue()->serialize(opts)); +} + +/** + * Helper function for parsing and creating MatchExpressions. + */ +std::unique_ptr<InternalSchemaCondMatchExpression> createCondMatchExpression(BSONObj condition, + BSONObj thenBranch, + BSONObj elseBranch) { + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto conditionExpr = MatchExpressionParser::parse(condition, expCtx); + ASSERT_OK(conditionExpr.getStatus()); + auto thenBranchExpr = MatchExpressionParser::parse(thenBranch, expCtx); + ASSERT_OK(thenBranchExpr.getStatus()); + auto elseBranchExpr = MatchExpressionParser::parse(elseBranch, expCtx); + + std::array<std::unique_ptr<MatchExpression>, 3> expressions = { + {std::move(conditionExpr.getValue()), + std::move(thenBranchExpr.getValue()), + std::move(elseBranchExpr.getValue())}}; + + auto cond = std::make_unique<InternalSchemaCondMatchExpression>(std::move(expressions)); + + return cond; +} + +TEST(SerializeInternalSchema, CondMatchRedactsCorrectly) { + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + auto conditionQuery = BSON("age" << BSON("$lt" << 18)); + auto thenQuery = BSON("job" + << "student"); + auto elseQuery = BSON("job" + << "engineer"); + auto cond = createCondMatchExpression(conditionQuery, thenQuery, elseQuery); + BSONObjBuilder bob; + cond->serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$_internalSchemaCond": [ + { + "HASH<age>": { + "$lt": "?number" + } + }, + { + "HASH<job>": { + "$eq": "?string" + } + }, + { + "HASH<job>": { + "$eq": "?string" + } + } + ] + })", + bob.done()); +} + +TEST(SerializeInternalSchema, FmodMatchRedactsCorrectly) { + InternalSchemaFmodMatchExpression m("a"_sd, Decimal128(1.7), Decimal128(2)); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + BSONObjBuilder bob; + m.serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"a":{"$_internalSchemaFmod":["?number","?number"]}})", + bob.done()); +} + +TEST(SerializeInternalSchema, MatchArrayIndexRedactsCorrectly) { + auto query = fromjson( + "{foo: {$_internalSchemaMatchArrayIndex:" + "{index: 0, namePlaceholder: 'i', expression: {i: {$type: 'number'}}}}}"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + + BSONObjBuilder bob; + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + objMatch.getValue()->serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "HASH<foo>": { + "$_internalSchemaMatchArrayIndex": { + "index": "?number", + "namePlaceholder": "HASH<i>", + "expression": { + "HASH<i>": { + "$type": ['number'] + } + } + } + } + })", + bob.done()); +} + +TEST(SerializeInternalSchema, MaxItemsRedactsCorrectly) { + InternalSchemaMaxItemsMatchExpression maxItems("a.b"_sd, 2); + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMaxItems":"?number"})", + maxItems.getSerializedRightHandSide(opts)); +} + +TEST(SerializeInternalSchema, MaxLengthRedactsCorrectly) { + InternalSchemaMaxLengthMatchExpression maxLength("a"_sd, 2); + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMaxLength":"?number"})", + maxLength.getSerializedRightHandSide(opts)); +} + +TEST(SerializeInternalSchema, MinItemsRedactsCorrectly) { + InternalSchemaMinItemsMatchExpression minItems("a.b"_sd, 2); + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMinItems":"?number"})", + minItems.getSerializedRightHandSide(opts)); +} + +TEST(SerializeInternalSchema, MinLengthRedactsCorrectly) { + InternalSchemaMinLengthMatchExpression minLength("a"_sd, 2); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMinLength":"?number"})", + minLength.getSerializedRightHandSide(opts)); +} + +TEST(SerializeInternalSchema, MinPropertiesRedactsCorrectly) { + InternalSchemaMinPropertiesMatchExpression minProperties(5); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + + BSONObjBuilder bob; + minProperties.serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMinProperties":"?number"})", + bob.done()); +} + +TEST(SerializeInternalSchema, ObjectMatchRedactsCorrectly) { + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + auto query = fromjson( + " {a: {$_internalSchemaObjectMatch: {" + " c: {$eq: 3}" + " }}}"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"HASH<a>":{"$_internalSchemaObjectMatch":{"HASH<c>":{"$eq":"?number"}}}})", + objMatch.getValue()->serialize(opts)); +} + +TEST(SerializeInternalSchema, RootDocEqRedactsCorrectly) { + auto query = fromjson("{$_internalSchemaRootDocEq: {a:1, b: {c: 1, d: [1]}}}"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$_internalSchemaRootDocEq": { + "HASH<a>": "?number", + "HASH<b>": { + "HASH<c>": "?number", + "HASH<d>": [ + "?number" + ] + } + } + })", + objMatch.getValue()->serialize(opts)); +} + +TEST(SerializeInternalSchema, BinDataEncryptedTypeRedactsCorrectly) { + MatcherTypeSet typeSet; + typeSet.bsonTypes.insert(BSONType::String); + typeSet.bsonTypes.insert(BSONType::Date); + InternalSchemaBinDataEncryptedTypeExpression e("a"_sd, std::move(typeSet)); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaBinDataEncryptedType":[2,9]})", + e.getSerializedRightHandSide(opts)); +} + +TEST(SerializeInternalSchema, BinDataFLE2EncryptedTypeRedactsCorrectly) { + InternalSchemaBinDataFLE2EncryptedTypeExpression e("ssn"_sd, BSONType::String); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaBinDataFLE2EncryptedType":[2]})", + e.getSerializedRightHandSide(opts)); +} + +TEST(SerializesInternalSchema, MaxPropertiesRedactsCorrectly) { + InternalSchemaMaxPropertiesMatchExpression maxProperties(5); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString}; + + BSONObjBuilder bob; + maxProperties.serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$_internalSchemaMaxProperties":"?number"})", + bob.done()); +} + +TEST(SerializesInternalSchema, EqRedactsCorrectly) { + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + auto query = fromjson("{$_internalSchemaEq: {a:1, b: {c: 1, d: [1]}}}"); + BSONObjBuilder bob; + InternalSchemaEqMatchExpression e("a"_sd, query.firstElement()); + e.serialize(&bob, opts); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "HASH<a>": { + "$_internalSchemaEq": { + "HASH<a>": "?number", + "HASH<b>": { + "HASH<c>": "?number", + "HASH<d>": [ + "?number" + ] + } + } + } + })", + bob.done()); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, RedactsExpressionCorrectly) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto expr = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(expr.getStatus()); + auto elemMatchExpr = dynamic_cast<const InternalSchemaAllElemMatchFromIndexMatchExpression*>( + expr.getValue().get()); + + auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST; + + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$_internalSchemaAllElemMatchFromIndex": [ + "?number", + { + "HASH<a>": { + "$lt": "?number" + } + } + ] + })", + elemMatchExpr->getSerializedRightHandSide(opts)); +} + +TEST(SerializeBasic, SerializesNestedElemMatchCorrectly) { + auto query = fromjson(R"({a: {$elemMatch: {$elemMatch: {b: {$lt: 6, $gt: 4}}}}})"); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + auto objMatch = MatchExpressionParser::parse(query, expCtx); + ASSERT_OK(objMatch.getStatus()); + SerializationOptions opts; + opts.literalPolicy = LiteralSerializationPolicy::kToDebugTypeString; + ASSERT_BSONOBJ_EQ_AUTO( + R"({"a": { + "$elemMatch": { + "$elemMatch": { + "$and": [ + { + "b": { + "$lt": "?number" + } + }, + { + "b": { + "$gt": "?number" + } + } + ] + } + } + } + })", + objMatch.getValue()->serialize(opts)); +} } // namespace } // namespace mongo |
