|
| 1 | +//////////////////////////////////////////////////////////////////////////////// |
| 2 | +/// DISCLAIMER |
| 3 | +/// |
| 4 | +/// Copyright 2020 ArangoDB GmbH, Cologne, Germany |
| 5 | +/// |
| 6 | +/// Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +/// you may not use this file except in compliance with the License. |
| 8 | +/// You may obtain a copy of the License at |
| 9 | +/// |
| 10 | +/// http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +/// |
| 12 | +/// Unless required by applicable law or agreed to in writing, software |
| 13 | +/// distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +/// See the License for the specific language governing permissions and |
| 16 | +/// limitations under the License. |
| 17 | +/// |
| 18 | +/// Copyright holder is ArangoDB GmbH, Cologne, Germany |
| 19 | +/// |
| 20 | +/// @author Andrei Lobov |
| 21 | +//////////////////////////////////////////////////////////////////////////////// |
| 22 | + |
| 23 | +#include "gtest/gtest.h" |
| 24 | + |
| 25 | +#include "fakeit.hpp" |
| 26 | + |
| 27 | +#include "Aql/ExpressionContext.h" |
| 28 | +#include "Aql/Functions.h" |
| 29 | +#include "Containers/SmallVector.h" |
| 30 | +#include "Transaction/Context.h" |
| 31 | +#include "Transaction/Methods.h" |
| 32 | +#include "IResearch/common.h" |
| 33 | +#include "Mocks/Servers.h" |
| 34 | + |
| 35 | +#include <velocypack/Builder.h> |
| 36 | +#include <velocypack/Iterator.h> |
| 37 | +#include <velocypack/Parser.h> |
| 38 | +#include <velocypack/Slice.h> |
| 39 | +#include <velocypack/velocypack-aliases.h> |
| 40 | +#include <set> |
| 41 | + |
| 42 | +using namespace arangodb; |
| 43 | +using namespace arangodb::aql; |
| 44 | +using namespace arangodb::containers; |
| 45 | + |
| 46 | +class InRangeFunctionTest : public ::testing::Test { |
| 47 | + public: |
| 48 | + InRangeFunctionTest() { |
| 49 | + arangodb::tests::init(); |
| 50 | + } |
| 51 | + |
| 52 | + protected: |
| 53 | + AqlValue evaluate(AqlValue const* attribute, |
| 54 | + AqlValue const* lower, |
| 55 | + AqlValue const* upper, |
| 56 | + AqlValue const* includeLower, |
| 57 | + AqlValue const* includeUpper, |
| 58 | + std::set<int>* warnings = nullptr) { |
| 59 | + fakeit::Mock<ExpressionContext> expressionContextMock; |
| 60 | + ExpressionContext& expressionContext = expressionContextMock.get(); |
| 61 | + fakeit::When(Method(expressionContextMock, registerWarning)).AlwaysDo([warnings](int c, char const*) { |
| 62 | + if (warnings) { |
| 63 | + warnings->insert(c); |
| 64 | + }}); |
| 65 | + fakeit::Mock<transaction::Context> trxCtxMock; |
| 66 | + fakeit::When(Method(trxCtxMock, getVPackOptions)).AlwaysDo([]() { |
| 67 | + static VPackOptions options; |
| 68 | + return &options; |
| 69 | + }); |
| 70 | + TRI_vocbase_t mockVocbase(TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server())); |
| 71 | + auto trx = server.createFakeTransaction(); |
| 72 | + SmallVector<AqlValue>::allocator_type::arena_type arena; |
| 73 | + SmallVector<AqlValue> params{ arena }; |
| 74 | + if (attribute) { |
| 75 | + params.emplace_back(*attribute); |
| 76 | + } |
| 77 | + if (lower) { |
| 78 | + params.emplace_back(*lower); |
| 79 | + } |
| 80 | + if (upper) { |
| 81 | + params.emplace_back(*upper); |
| 82 | + } |
| 83 | + if (includeLower) { |
| 84 | + params.emplace_back(*includeLower); |
| 85 | + } |
| 86 | + if (includeUpper) { |
| 87 | + params.emplace_back(*includeUpper); |
| 88 | + } |
| 89 | + return Functions::InRange(&expressionContext, trx.get(), params); |
| 90 | + } |
| 91 | + |
| 92 | + void assertInRangeFail(size_t line, |
| 93 | + std::set<int> const& expected_warnings, |
| 94 | + AqlValue const* attribute, |
| 95 | + AqlValue const* lower, |
| 96 | + AqlValue const* upper, |
| 97 | + AqlValue const* includeLower, |
| 98 | + AqlValue const* includeUpper) { |
| 99 | + SCOPED_TRACE(testing::Message("assertInRangeFail failed on line:") << line); |
| 100 | + std::set<int> warnings; |
| 101 | + ASSERT_TRUE(evaluate(attribute, lower, upper, includeLower, includeUpper, &warnings).isNull(false)); |
| 102 | + ASSERT_EQ(expected_warnings, warnings); |
| 103 | + } |
| 104 | + |
| 105 | + void assertInRange(size_t line, |
| 106 | + bool expectedValue, |
| 107 | + AqlValue const* attribute, |
| 108 | + AqlValue const* lower, |
| 109 | + AqlValue const* upper, |
| 110 | + bool includeLower, |
| 111 | + bool includeUpper) { |
| 112 | + SCOPED_TRACE(testing::Message("assertInRange failed on line:") << line); |
| 113 | + std::set<int> warnings; |
| 114 | + AqlValue includeLowerAql{ AqlValueHintBool(includeLower) }; |
| 115 | + AqlValue includeUpperAql{ AqlValueHintBool(includeUpper) }; |
| 116 | + auto value = evaluate(attribute, lower, upper, |
| 117 | + &includeLowerAql, |
| 118 | + &includeUpperAql, &warnings); |
| 119 | + ASSERT_TRUE(warnings.empty()); |
| 120 | + ASSERT_TRUE(value.isBoolean()); |
| 121 | + ASSERT_EQ(expectedValue, value.toBoolean()); |
| 122 | + } |
| 123 | + |
| 124 | + private: |
| 125 | + arangodb::tests::mocks::MockAqlServer server; |
| 126 | +}; |
| 127 | + |
| 128 | +TEST_F(InRangeFunctionTest, testValidArgs) { |
| 129 | + // strings |
| 130 | + { |
| 131 | + AqlValue foo("foo"); |
| 132 | + AqlValue boo("boo"); |
| 133 | + AqlValue poo("poo"); |
| 134 | + assertInRange(__LINE__, true, &foo, &boo, &poo, true, true); |
| 135 | + assertInRange(__LINE__, false, &foo, &poo, &boo, true, true); |
| 136 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, true); |
| 137 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, false); |
| 138 | + assertInRange(__LINE__, false, &foo, &foo, &poo, false, true); |
| 139 | + assertInRange(__LINE__, true, &foo, &boo, &foo, true, true); |
| 140 | + assertInRange(__LINE__, true, &foo, &boo, &foo, false, true); |
| 141 | + assertInRange(__LINE__, false, &foo, &boo, &foo, true, false); |
| 142 | + } |
| 143 | + // non ASCII |
| 144 | + { |
| 145 | + AqlValue foo("ПУИ"); |
| 146 | + AqlValue boo("ПУЗ"); |
| 147 | + AqlValue poo("ПУЙ"); |
| 148 | + assertInRange(__LINE__, true, &foo, &boo, &poo, true, true); |
| 149 | + assertInRange(__LINE__, false, &foo, &poo, &boo, true, true); |
| 150 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, true); |
| 151 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, false); |
| 152 | + assertInRange(__LINE__, false, &foo, &foo, &poo, false, true); |
| 153 | + assertInRange(__LINE__, true, &foo, &boo, &foo, true, true); |
| 154 | + assertInRange(__LINE__, true, &foo, &boo, &foo, false, true); |
| 155 | + assertInRange(__LINE__, false, &foo, &boo, &foo, true, false); |
| 156 | + } |
| 157 | + // numbers |
| 158 | + { |
| 159 | + AqlValue foo{ AqlValueHintInt(5) }; |
| 160 | + AqlValue boo{ AqlValueHintDouble(4.9999) }; |
| 161 | + AqlValue poo{ AqlValueHintDouble(5.0001) }; |
| 162 | + assertInRange(__LINE__, true, &foo, &boo, &poo, true, true); |
| 163 | + assertInRange(__LINE__, false, &foo, &poo, &boo, true, true); |
| 164 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, true); |
| 165 | + assertInRange(__LINE__, true, &foo, &foo, &poo, true, false); |
| 166 | + assertInRange(__LINE__, false, &foo, &foo, &poo, false, true); |
| 167 | + assertInRange(__LINE__, true, &foo, &boo, &foo, true, true); |
| 168 | + assertInRange(__LINE__, true, &foo, &boo, &foo, false, true); |
| 169 | + assertInRange(__LINE__, false, &foo, &boo, &foo, true, false); |
| 170 | + } |
| 171 | + // type mix |
| 172 | + { |
| 173 | + AqlValue const Int5{ AqlValueHintInt(5) }; |
| 174 | + AqlValue const NullVal{ AqlValueHintNull{} }; |
| 175 | + AqlValue const ArrayVal{ AqlValueHintEmptyArray{} }; |
| 176 | + AqlValue const ObjectVal{ AqlValueHintEmptyObject{} }; |
| 177 | + AqlValue const StringVal("foo"); |
| 178 | + assertInRange(__LINE__, true, &StringVal, &NullVal, &ObjectVal, true, true); |
| 179 | + assertInRange(__LINE__, true, &StringVal, &NullVal, &ArrayVal, true, true); |
| 180 | + assertInRange(__LINE__, false, &StringVal, &ObjectVal, &NullVal, true, true); |
| 181 | + assertInRange(__LINE__, false, &StringVal, &ArrayVal, &NullVal, true, true); |
| 182 | + assertInRange(__LINE__, false, &StringVal, &ObjectVal, &ArrayVal, true, true); |
| 183 | + assertInRange(__LINE__, false, &StringVal, &ArrayVal, &ObjectVal, true, true); |
| 184 | + assertInRange(__LINE__, false, &StringVal, &NullVal, &Int5, true, true); |
| 185 | + assertInRange(__LINE__, true, &StringVal, &NullVal, &StringVal, true, true); |
| 186 | + assertInRange(__LINE__, false, &StringVal, &StringVal, &NullVal, true, true); |
| 187 | + assertInRange(__LINE__, false, &StringVal, &StringVal, &Int5, true, true); |
| 188 | + assertInRange(__LINE__, true, &Int5, &NullVal, &StringVal, true, true); |
| 189 | + assertInRange(__LINE__, false, &Int5, &ArrayVal, &StringVal, true, true); |
| 190 | + assertInRange(__LINE__, true, &Int5, &NullVal, &ArrayVal, true, true); |
| 191 | + assertInRange(__LINE__, true, &Int5, &NullVal, &ObjectVal, true, true); |
| 192 | + assertInRange(__LINE__, false, &ArrayVal, &NullVal, &StringVal, true, true); |
| 193 | + assertInRange(__LINE__, true, &ArrayVal, &NullVal, &ObjectVal, true, true); |
| 194 | + assertInRange(__LINE__, true, &ArrayVal, &StringVal, &ObjectVal, true, true); |
| 195 | + assertInRange(__LINE__, true, &ArrayVal, &Int5, &ObjectVal, true, true); |
| 196 | + assertInRange(__LINE__, true, &ObjectVal, &Int5, &ObjectVal, true, true); |
| 197 | + assertInRange(__LINE__, false, &ObjectVal, &Int5, &ObjectVal, true, false); |
| 198 | + } |
| 199 | +} |
| 200 | + |
| 201 | +TEST_F(InRangeFunctionTest, testInvalidArgs) { |
| 202 | + const std::set<int> typeMismatchWarning{ TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH }; |
| 203 | + const std::set<int> invalidArgsCount{ TRI_ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH }; |
| 204 | + AqlValue const ValidString{ "ValidString" }; |
| 205 | + AqlValue const ValidBool{ AqlValueHintBool{true} }; |
| 206 | + assertInRangeFail(__LINE__, invalidArgsCount, &ValidString, &ValidString, &ValidString, &ValidBool, nullptr); |
| 207 | + assertInRangeFail(__LINE__, typeMismatchWarning, &ValidString, &ValidString, &ValidString, &ValidBool, &ValidString); |
| 208 | + assertInRangeFail(__LINE__, typeMismatchWarning, &ValidString, &ValidString, &ValidString, &ValidString, &ValidBool); |
| 209 | +} |
0 commit comments