diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2018-09-26 15:17:56 +0000 |
---|---|---|
committer | Sara Golemon <sara.golemon@mongodb.com> | 2018-10-05 23:33:33 +0000 |
commit | f9600749453db5dec58bd1bbfd967e16b1578a24 (patch) | |
tree | 22e0f6e8f773483ad12828d92f4bf8d1496fbe70 /src/mongo/idl | |
parent | 2db04b524dc5e2121b74829814db7c8e84e5696d (diff) | |
download | mongo-f9600749453db5dec58bd1bbfd967e16b1578a24.tar.gz |
SERVER-37168 Add validators for IDL fields
Diffstat (limited to 'src/mongo/idl')
-rw-r--r-- | src/mongo/idl/idl_test.cpp | 208 | ||||
-rw-r--r-- | src/mongo/idl/idl_test.h | 70 | ||||
-rw-r--r-- | src/mongo/idl/unittest.idl | 63 |
3 files changed, 340 insertions, 1 deletions
diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp index ed5ea3dc6ab..341509638f5 100644 --- a/src/mongo/idl/idl_test.cpp +++ b/src/mongo/idl/idl_test.cpp @@ -2401,6 +2401,214 @@ TEST(IDLChainedStruct, TestInline) { } } +TEST(IDLValidatedField, Int_basic_ranges) { + // Explicitly call setters. + Int_basic_ranges obj0; + obj0.setPositive_int(42); + ASSERT_THROWS(obj0.setPositive_int(0), AssertionException); + ASSERT_THROWS(obj0.setPositive_int(-42), AssertionException); + + ASSERT_THROWS(obj0.setNegative_int(42), AssertionException); + ASSERT_THROWS(obj0.setNegative_int(0), AssertionException); + obj0.setNegative_int(-42); + + obj0.setNon_negative_int(42); + obj0.setNon_negative_int(0); + ASSERT_THROWS(obj0.setNon_negative_int(-42), AssertionException); + + ASSERT_THROWS(obj0.setNon_positive_int(42), AssertionException); + obj0.setNon_positive_int(0); + obj0.setNon_positive_int(-42); + + ASSERT_THROWS(obj0.setByte_range_int(-1), AssertionException); + obj0.setByte_range_int(0); + obj0.setByte_range_int(127); + obj0.setByte_range_int(128); + obj0.setByte_range_int(255); + ASSERT_THROWS(obj0.setByte_range_int(256), AssertionException); + + // IDL ints *are* int32_t, so no number we can pass to the func will actually fail. + obj0.setRange_int(-2147483648); + obj0.setRange_int(-65536); + obj0.setRange_int(0); + obj0.setRange_int(65536); + obj0.setRange_int(2147483647); + + // Positive case parsing. + const auto tryPass = [](std::int32_t pos, + std::int32_t neg, + std::int32_t nonneg, + std::int32_t nonpos, + std::int32_t byte_range, + std::int32_t int_range) { + IDLParserErrorContext ctxt("root"); + auto doc = + BSON("positive_int" << pos << "negative_int" << neg << "non_negative_int" << nonneg + << "non_positive_int" + << nonpos + << "byte_range_int" + << byte_range + << "range_int" + << int_range); + auto obj = Int_basic_ranges::parse(ctxt, doc); + ASSERT_EQUALS(obj.getPositive_int(), pos); + ASSERT_EQUALS(obj.getNegative_int(), neg); + ASSERT_EQUALS(obj.getNon_negative_int(), nonneg); + ASSERT_EQUALS(obj.getNon_positive_int(), nonpos); + ASSERT_EQUALS(obj.getByte_range_int(), byte_range); + ASSERT_EQUALS(obj.getRange_int(), int_range); + }; + + // Negative case parsing. + const auto tryFail = [](std::int32_t pos, + std::int32_t neg, + std::int32_t nonneg, + std::int32_t nonpos, + std::int32_t byte_range, + std::int32_t int_range) { + IDLParserErrorContext ctxt("root"); + auto doc = + BSON("positive_int" << pos << "negative_int" << neg << "non_negative_int" << nonneg + << "non_positive_int" + << nonpos + << "byte_range_int" + << byte_range + << "range_int" + << int_range); + ASSERT_THROWS(Int_basic_ranges::parse(ctxt, doc), AssertionException); + }; + + tryPass(1, -1, 0, 0, 128, 65537); + tryFail(0, -1, 0, 0, 128, 65537); + tryFail(1, 0, 0, 0, 128, 65537); + tryFail(1, -1, -1, 0, 128, 65537); + tryFail(1, -1, 0, 1, 128, 65537); + tryFail(1, -1, 0, 0, 256, 65537); + tryFail(0, 0, -1, 1, 257, 0); + + tryPass(1000, -1000, 1, -1, 127, 0x7FFFFFFF); +} + +TEST(IDLValidatedField, Double_basic_ranges) { + // Explicitly call setters. + Double_basic_ranges obj0; + obj0.setPositive_double(42.0); + obj0.setPositive_double(0.000000000001); + ASSERT_THROWS(obj0.setPositive_double(0.0), AssertionException); + ASSERT_THROWS(obj0.setPositive_double(-42.0), AssertionException); + + ASSERT_THROWS(obj0.setNegative_double(42.0), AssertionException); + ASSERT_THROWS(obj0.setNegative_double(0.0), AssertionException); + obj0.setNegative_double(-0.000000000001); + obj0.setNegative_double(-42.0); + + obj0.setNon_negative_double(42.0); + obj0.setNon_negative_double(0.0); + ASSERT_THROWS(obj0.setNon_negative_double(-42.0), AssertionException); + + ASSERT_THROWS(obj0.setNon_positive_double(42.0), AssertionException); + obj0.setNon_positive_double(0.0); + obj0.setNon_positive_double(-42.0); + + ASSERT_THROWS(obj0.setRange_double(-12345678901234600000.0), AssertionException); + obj0.setRange_double(-12345678901234500000.0); + obj0.setRange_double(-3000000000.0); + obj0.setRange_double(0); + obj0.setRange_double(3000000000); + obj0.setRange_double(12345678901234500000.0); + ASSERT_THROWS(obj0.setRange_double(12345678901234600000.0), AssertionException); + + // Positive case parsing. + const auto tryPass = + [](double pos, double neg, double nonneg, double nonpos, double double_range) { + IDLParserErrorContext ctxt("root"); + auto doc = + BSON("positive_double" << pos << "negative_double" << neg << "non_negative_double" + << nonneg + << "non_positive_double" + << nonpos + << "range_double" + << double_range); + auto obj = Double_basic_ranges::parse(ctxt, doc); + ASSERT_EQUALS(obj.getPositive_double(), pos); + ASSERT_EQUALS(obj.getNegative_double(), neg); + ASSERT_EQUALS(obj.getNon_negative_double(), nonneg); + ASSERT_EQUALS(obj.getNon_positive_double(), nonpos); + ASSERT_EQUALS(obj.getRange_double(), double_range); + }; + + // Negative case parsing. + const auto tryFail = + [](double pos, double neg, double nonneg, double nonpos, double double_range) { + IDLParserErrorContext ctxt("root"); + auto doc = + BSON("positive_double" << pos << "negative_double" << neg << "non_negative_double" + << nonneg + << "non_positive_double" + << nonpos + << "range_double" + << double_range); + ASSERT_THROWS(Double_basic_ranges::parse(ctxt, doc), AssertionException); + }; + + tryPass(1, -1, 0, 0, 123456789012345); + tryFail(0, -1, 0, 0, 123456789012345); + tryFail(1, 0, 0, 0, 123456789012345); + tryFail(1, -1, -1, 0, 123456789012345); + tryFail(1, -1, 0, 1, 123456789012345); + tryFail(1, -1, 0, -1, 12345678901234600000.0); + tryPass(0.00000000001, -0.00000000001, 0.0, 0.0, 1.23456789012345); +} + +TEST(IDLValidatedField, Callback_validators) { + // Explicitly call setters. + Callback_validators obj0; + obj0.setInt_even(42); + ASSERT_THROWS(obj0.setInt_even(7), AssertionException); + obj0.setInt_even(0); + ASSERT_THROWS(obj0.setInt_even(-7), AssertionException); + obj0.setInt_even(-42); + + ASSERT_THROWS(obj0.setDouble_nearly_int(3.141592), AssertionException); + ASSERT_THROWS(obj0.setDouble_nearly_int(-2.71828), AssertionException); + obj0.setDouble_nearly_int(0.0); + obj0.setDouble_nearly_int(1.0); + obj0.setDouble_nearly_int(1.05); + obj0.setDouble_nearly_int(-123456789.01234500000); + + ASSERT_THROWS(obj0.setString_starts_with_x("whiskey"), AssertionException); + obj0.setString_starts_with_x("x-ray"); + ASSERT_THROWS(obj0.setString_starts_with_x("yankee"), AssertionException); + + // Positive case parsing. + const auto tryPass = + [](std::int32_t int_even, double double_nearly_int, StringData string_starts_with_x) { + IDLParserErrorContext ctxt("root"); + auto doc = BSON("int_even" << int_even << "double_nearly_int" << double_nearly_int + << "string_starts_with_x" + << string_starts_with_x); + auto obj = Callback_validators::parse(ctxt, doc); + ASSERT_EQUALS(obj.getInt_even(), int_even); + ASSERT_EQUALS(obj.getDouble_nearly_int(), double_nearly_int); + ASSERT_EQUALS(obj.getString_starts_with_x(), string_starts_with_x); + }; + + // Negative case parsing. + const auto tryFail = + [](std::int32_t int_even, double double_nearly_int, StringData string_starts_with_x) { + IDLParserErrorContext ctxt("root"); + auto doc = BSON("int_even" << int_even << "double_nearly_int" << double_nearly_int + << "string_starts_with_x" + << string_starts_with_x); + ASSERT_THROWS(Callback_validators::parse(ctxt, doc), AssertionException); + }; + + tryPass(42, 123456789.01, "x-ray"); + tryFail(43, 123456789.01, "x-ray"); + tryFail(42, 123456789.11, "x-ray"); + tryFail(42, 123456789.01, "uniform"); +} + // Positive: verify a command a string arg TEST(IDLTypeCommand, TestString) { IDLParserErrorContext ctxt("root"); diff --git a/src/mongo/idl/idl_test.h b/src/mongo/idl/idl_test.h new file mode 100644 index 00000000000..ba9cbf7dfe6 --- /dev/null +++ b/src/mongo/idl/idl_test.h @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#include "mongo/base/status.h" + +namespace mongo { +namespace idl { +namespace test { + +/** + * Validates the given number is even + */ +inline Status validateEvenNumber(std::int32_t value) { + if (value & 1) { + return {ErrorCodes::BadValue, "Value must be even"}; + } + return Status::OK(); +} + +/** + * Validates that the number presented is within 0.1 of an integer value + */ +inline Status validateNearlyInt(double value) { + value = fabs(value); + value = value - static_cast<std::uint64_t>(value); + if ((value > 0.1) && (value < 0.9)) { + return {ErrorCodes::BadValue, "Value is too far from being an integer"}; + } + return Status::OK(); +} + +/** + * Validates that the provided string starts with a given letter. + */ +template <char letter> +Status validateStartsWith(StringData value) { + if ((value.empty() || value[0] != letter)) { + return {ErrorCodes::BadValue, "Value does not begin with correct letter"}; + } + return Status::OK(); +} + +} // namespace test +} // namespace idl +} // namespace mongo diff --git a/src/mongo/idl/unittest.idl b/src/mongo/idl/unittest.idl index ddbe386fd91..56c175e095a 100644 --- a/src/mongo/idl/unittest.idl +++ b/src/mongo/idl/unittest.idl @@ -19,6 +19,7 @@ global: cpp_namespace: "mongo::idl::test" cpp_includes: - "mongo/idl/idl_test_types.h" + - "mongo/idl/idl_test.h" imports: - "mongo/idl/basic_types.idl" @@ -498,6 +499,67 @@ structs: ################################################################################################## # +# Using Validators +# +################################################################################################## + + int_basic_ranges: + description: Struct using basic range validators on ints + fields: + positive_int: + type: int + validator: { gt: 0 } + negative_int: + type: int + validator: { lt: 0 } + non_negative_int: + type: int + validator: { gte: 0 } + non_positive_int: + type: int + validator: { lte: 0 } + byte_range_int: + type: int + validator: { gte: 0, lt: 256 } + range_int: + type: int + validator: { gte: -2147483658, lte: 2147483647 } + + double_basic_ranges: + description: Struct using basic range validators on doubles + fields: + positive_double: + type: double + validator: { gt: 0.0 } + negative_double: + type: double + validator: { lt: 0.0 } + non_negative_double: + type: double + validator: { gte: 0.0 } + non_positive_double: + type: double + validator: { lte: 0.0 } + range_double: + type: double + validator: { gte: -12345678901234500000, lte: 12345678901234500000 } + + callback_validators: + description: Struct using fields with callback validators + fields: + int_even: + type: int + validator: { callback: 'validateEvenNumber' } + double_nearly_int: + type: double + validator: { callback: 'validateNearlyInt' } + string_starts_with_x: + type: string + validator: { callback: "validateStartsWith<'x'>" } + + +################################################################################################## +# # Test commands # ################################################################################################## @@ -618,4 +680,3 @@ commands: fields: field1: int - |