//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2019 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Andrey Abramov /// @author Vasiliy Nabatchikov //////////////////////////////////////////////////////////////////////////////// #include "gtest/gtest.h" #include "velocypack/Iterator.h" #include "velocypack/Parser.h" #include "analysis/analyzers.hpp" #include "analysis/token_attributes.hpp" #include "IResearch/RestHandlerMock.h" #include "IResearch/common.h" #include "Mocks/LogLevels.h" #include "Mocks/Servers.h" #include "Mocks/StorageEngineMock.h" #include "Aql/QueryRegistry.h" #include "Basics/VelocyPackHelper.h" #include "GeneralServer/AuthenticationFeature.h" #include "IResearch/IResearchAnalyzerFeature.h" #include "IResearch/IResearchCommon.h" #include "RestHandler/RestAnalyzerHandler.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/QueryRegistryFeature.h" #include "RestServer/SystemDatabaseFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "V8Server/V8DealerFeature.h" #include "VocBase/LogicalCollection.h" #include "VocBase/Methods/Collections.h" #if USE_ENTERPRISE #include "Enterprise/Ldap/LdapFeature.h" #endif #define ASSERT_ARANGO_OK(x) \ { ASSERT_TRUE(x.ok()) << x.errorMessage(); } using namespace std::literals::string_literals; namespace { class EmptyAnalyzer : public irs::analysis::analyzer { public: DECLARE_ANALYZER_TYPE(); EmptyAnalyzer() : irs::analysis::analyzer(EmptyAnalyzer::type()) { _attrs.emplace(_attr); } virtual irs::attribute_view const& attributes() const NOEXCEPT override { return _attrs; } static ptr make(irs::string_ref const&) { PTR_NAMED(EmptyAnalyzer, ptr); return ptr; } static bool normalize(irs::string_ref const&, std::string& out) { out.resize(VPackSlice::emptyObjectSlice().byteSize()); std::memcpy(&out[0], VPackSlice::emptyObjectSlice().begin(), out.size()); return true; } virtual bool next() override { return false; } virtual bool reset(irs::string_ref const& data) override { return true; } private: irs::attribute_view _attrs; irs::frequency _attr; }; DEFINE_ANALYZER_TYPE_NAMED(EmptyAnalyzer, "rest-analyzer-empty"); REGISTER_ANALYZER_VPACK(EmptyAnalyzer, EmptyAnalyzer::make, EmptyAnalyzer::normalize); } // namespace // ----------------------------------------------------------------------------- // --SECTION-- setup / tear-down // ----------------------------------------------------------------------------- class RestAnalyzerHandlerTest : public ::testing::Test, public arangodb::tests::LogSuppressor { protected: arangodb::tests::mocks::MockAqlServer server; TRI_vocbase_t& _system_vocbase; arangodb::iresearch::IResearchAnalyzerFeature& analyzers; arangodb::DatabaseFeature& dbFeature; arangodb::AuthenticationFeature& authFeature; arangodb::auth::UserManager* userManager; struct ExecContext : public arangodb::ExecContext { ExecContext() : arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "", arangodb::auth::Level::NONE, arangodb::auth::Level::NONE) {} } execContext; arangodb::ExecContextScope execContextScope; // (&execContext); arangodb::aql::QueryRegistry queryRegistry; // required for UserManager::loadFromDB() RestAnalyzerHandlerTest() : server(), _system_vocbase(server.getSystemDatabase()), analyzers(server.getFeature()), dbFeature(server.getFeature()), authFeature(server.getFeature()), userManager(authFeature.userManager()), execContext(), execContextScope(&execContext), queryRegistry(0) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); userManager->setQueryRegistry(&queryRegistry); // TODO: This should at the very least happen in the mock // create system vocbase { std::shared_ptr unused; arangodb::methods::Collections::createSystem(server.getSystemDatabase(), arangodb::tests::AnalyzerCollectionName, false, unused); } createAnalyzers(); grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE); } // Creates the analyzers that are used in all the tests void createAnalyzers() { arangodb::Result res; std::string name; arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result; name = arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"; ASSERT_ARANGO_OK( analyzers.emplace(result, name, "identity"s, VPackParser::fromJson("{\"args\":\"abc\"}"s)->slice())); name = arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"; ASSERT_ARANGO_OK( analyzers.emplace(result, name, "identity"s, VPackParser::fromJson("{\"args\":\"abc\"}"s)->slice())); name = arangodb::StaticStrings::SystemDatabase + "::emptyAnalyzer"s; ASSERT_ARANGO_OK( analyzers.emplace(result, name, "rest-analyzer-empty"s, VPackParser::fromJson("{\"args\":\"en\"}"s)->slice(), irs::flags{irs::frequency::type()})); }; // Creates a new database void createDatabase(std::string const& name) { arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result; auto const databases = arangodb::velocypack::Parser::fromJson( std::string("[ { \"name\": \"" + name + "\" } ]")); ASSERT_EQ(TRI_ERROR_NO_ERROR, dbFeature.loadDatabases(databases->slice())); grantOnDb({{name, arangodb::auth::Level::RW}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW}}); std::shared_ptr ignored; arangodb::Result res = arangodb::methods::Collections::createSystem(*dbFeature.useDatabase(name), arangodb::tests::AnalyzerCollectionName, false, ignored); ASSERT_TRUE(res.ok()); ASSERT_ARANGO_OK(analyzers.emplace(result, name + "::testAnalyzer1", "identity"s, VPackSlice::noneSlice())); ASSERT_ARANGO_OK(analyzers.emplace(result, name + "::testAnalyzer2", "identity"s, VPackSlice::noneSlice())); } // Grant permissions on one DB // NOTE that permissions are always overwritten. void grantOnDb(std::string const& dbName, arangodb::auth::Level const& level) { arangodb::auth::UserMap userMap; // empty map, no user -> no permissions auto& user = userMap .emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP)) .first->second; // for system collections User::collectionAuthLevel(...) returns database auth::Level user.grantDatabase(dbName, level); // set user map to avoid loading configuration from system database userManager->setAuthInfo(userMap); } // Grant permissions on multiple DBs // NOTE that permissions are always overwritten. void grantOnDb(std::vector> grants) { arangodb::auth::UserMap userMap; auto& user = userMap .emplace("", arangodb::auth::User::newUser(""s, ""s, arangodb::auth::Source::LDAP)) .first->second; for (auto& g : grants) { user.grantDatabase(g.first, g.second); } userManager->setAuthInfo(userMap); } }; // ----------------------------------------------------------------------------- // --SECTION-- test suite // ----------------------------------------------------------------------------- // invalid params (non-object body) TEST_F(RestAnalyzerHandlerTest, test_create_non_object_body) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openArray(); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_no_name) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("type", arangodb::velocypack::Value("identity")); request._payload.add("properties", arangodb::velocypack::Value( arangodb::velocypack::ValueType::Null)); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_no_permission) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", VPackValue("unknownVocbase::testAnalyzer")); request._payload.add("type", VPackValue("identity")); request._payload.add("properties", VPackValue(arangodb::velocypack::ValueType::Null)); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_invalid_symbols) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", VPackValue(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer")); request._payload.add("type", VPackValue("identity")); request._payload.add("properties", VPackValue(arangodb::velocypack::ValueType::Null)); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } // TODO: is this the smae test as above? TEST_F(RestAnalyzerHandlerTest, test_create_invalid_symbols_2) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", VPackValue("::testAnalyzer")); request._payload.add("type", VPackValue("identity")); request._payload.add("properties", VPackValue(arangodb::velocypack::ValueType::Null)); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_name_collision) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", arangodb::velocypack::Value("emptyAnalyzer")); request._payload.add("type", arangodb::velocypack::Value("rest-analyzer-empty")); request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}")); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_duplicate_matching) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", VPackValue("testAnalyzer1")); request._payload.add("type", VPackValue("identity")); request._payload.add("properties", VPackSlice::noneSlice()); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1" == slice.get("name").copyString())); EXPECT_TRUE(slice.hasKey("type") && slice.get("type").isString()); EXPECT_TRUE(slice.hasKey("properties") && slice.get("properties").isObject()); EXPECT_TRUE(slice.hasKey("features") && slice.get("features").isArray()); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); EXPECT_NE(nullptr, analyzer); } TEST_F(RestAnalyzerHandlerTest, test_create_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", VPackValue("testAnalyzer2")); request._payload.add("type", VPackValue("identity")); request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}")); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_create_success) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::POST); request._payload.openObject(); request._payload.add("name", arangodb::velocypack::Value("testAnalyzer3")); request._payload.add("type", arangodb::velocypack::Value("identity")); request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}")); request._payload.close(); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::CREATED, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && arangodb::StaticStrings::SystemDatabase + "::testAnalyzer3" == slice.get("name").copyString())); EXPECT_TRUE(slice.hasKey("type") && slice.get("type").isString()); EXPECT_TRUE(slice.hasKey("properties") && slice.get("properties").isObject()); EXPECT_TRUE(slice.hasKey("features") && slice.get("features").isArray()); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); EXPECT_NE(nullptr, analyzer); } TEST_F(RestAnalyzerHandlerTest, test_get_static_known) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("identity"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && std::string("identity") == slice.get("name").copyString())); EXPECT_TRUE(slice.hasKey("type") && slice.get("type").isString()); EXPECT_TRUE(slice.hasKey("properties") && slice.get("properties").isObject()); EXPECT_TRUE(slice.hasKey("features") && slice.get("features").isArray()); } TEST_F(RestAnalyzerHandlerTest, test_get_static_unknown) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); TRI_ASSERT(requestPtr != nullptr); auto& request = *requestPtr; auto responcePtr = std::make_unique(); TRI_ASSERT(responcePtr != nullptr); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::NOT_FOUND, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_get_known) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1" == slice.get("name").copyString())); EXPECT_TRUE(slice.hasKey("type") && slice.get("type").isString()); EXPECT_TRUE(slice.hasKey("properties") && slice.get("properties").isObject()); EXPECT_TRUE(slice.hasKey("features") && slice.get("features").isArray()); } // TODO: This test needs some love (i.e. probably splitting) TEST_F(RestAnalyzerHandlerTest, test_get_custom) { createDatabase("FooDb"s); createDatabase("FooDb2"s); grantOnDb({{"FooDb"s, arangodb::auth::Level::RW}, {"FooDb2"s, arangodb::auth::Level::RW}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO}}); { TRI_vocbase_t& vocbase = *dbFeature.useDatabase("FooDb2"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("FooDb::testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); // user has access but analyzer should not be visible EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); } { TRI_vocbase_t& vocbase = *dbFeature.useDatabase("FooDb2"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); // system should be visible EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); } { TRI_vocbase_t& vocbase = *dbFeature.useDatabase("FooDb2"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("::testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); // system should be visible EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); } } TEST_F(RestAnalyzerHandlerTest, test_get_known_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_get_unknown_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::NOT_FOUND, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_get_unknown_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_get_unknown_analyzer_unknown_vocbase_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); // TODO TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, unknownDBInfo(server.server())); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::NOT_FOUND, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_get_unknown_analyzer_unknown_vocbase_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE); // TODO TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, unknownDBInfo(server.server())); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } TEST_F(RestAnalyzerHandlerTest, test_list_system_database_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2", arangodb::StaticStrings::SystemDatabase + "::emptyAnalyzer", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } TEST_F(RestAnalyzerHandlerTest, test_list_system_database_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("result") && slice.get("result").isArray() && expected.size() == slice.get("result").length())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } TEST_F(RestAnalyzerHandlerTest, test_list_non_system_database_authorized) { createDatabase("testVocbase"s); arangodb::auth::UserMap userMap; grantOnDb({{"testVocbase"s, arangodb::auth::Level::RW}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO}}); TRI_vocbase_t& vocbase = *dbFeature.useDatabase("testVocbase"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); grantOnDb({{"testVocbase"s, arangodb::auth::Level::RO}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO}}); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", "testVocbase::testAnalyzer1", "testVocbase::testAnalyzer2", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2", arangodb::StaticStrings::SystemDatabase + "::emptyAnalyzer", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("result") && slice.get("result").isArray() && expected.size() == slice.get("result").length())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } TEST_F(RestAnalyzerHandlerTest, test_list_non_system_database_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); createDatabase("testVocbase"s); TRI_vocbase_t& vocbase = *dbFeature.useDatabase("testVocbase"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); grantOnDb({{"testVocbase"s, arangodb::auth::Level::NONE}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO}}); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", arangodb::StaticStrings::SystemDatabase + "::emptyAnalyzer", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1", arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("result") && slice.get("result").isArray() && expected.size() == slice.get("result").length())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } TEST_F(RestAnalyzerHandlerTest, test_list_non_system_database_system_not_authorized) { createDatabase("testVocbase"s); TRI_vocbase_t& vocbase = *dbFeature.useDatabase("testVocbase"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); grantOnDb({{"testVocbase"s, arangodb::auth::Level::RO}, {arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE}}); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", "testVocbase::testAnalyzer1", "testVocbase::testAnalyzer2", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("result") && slice.get("result").isArray() && expected.size() == slice.get("result").length())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } TEST_F(RestAnalyzerHandlerTest, test_list_non_system_database_system_not_authorized_not_authorized) { createDatabase("testVocbase"s); TRI_vocbase_t& vocbase = *dbFeature.useDatabase("testVocbase"); auto requestPtr = std::make_unique(vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::GET); grantOnDb({{arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::NONE}, {"testVocbase"s, arangodb::auth::Level::NONE}}); std::set expected = { "identity", "text_de", "text_en", "text_es", "text_fi", "text_fr", "text_it", "text_nl", "text_no", "text_pt", "text_ru", "text_sv", "text_zh", }; auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("result") && slice.get("result").isArray() && expected.size() == slice.get("result").length())); for (arangodb::velocypack::ArrayIterator itr(slice.get("result")); itr.valid(); ++itr) { auto subSlice = *itr; EXPECT_TRUE(subSlice.isObject()); EXPECT_TRUE(subSlice.hasKey("name") && subSlice.get("name").isString()); EXPECT_TRUE(subSlice.hasKey("type") && subSlice.get("type").isString()); EXPECT_TRUE( (subSlice.hasKey("properties") && (subSlice.get("properties").isObject() || subSlice.get("properties").isNull()))); EXPECT_TRUE(subSlice.hasKey("features") && subSlice.get("features").isArray()); EXPECT_EQ(1, expected.erase(subSlice.get("name").copyString())); } EXPECT_TRUE(expected.empty()); } // invalid params (no name) TEST_F(RestAnalyzerHandlerTest, test_remove_invalid_params) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::BAD, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::BAD) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } // unknown analyzer TEST_F(RestAnalyzerHandlerTest, test_remove_unknown_analyzer) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix("unknown"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::NOT_FOUND, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); } // not authorised TEST_F(RestAnalyzerHandlerTest, test_remove_not_authorized) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix("testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::FORBIDDEN) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); // Check it's not gone auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); ASSERT_NE(analyzer, nullptr); } // still in use (fail) TEST_F(RestAnalyzerHandlerTest, test_remove_still_in_use) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix("testAnalyzer2"); request.values().emplace("force", "false"); // Hold a reference to mark analyzer in use auto inUseAnalyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"); ASSERT_NE(nullptr, inUseAnalyzer); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::CONFLICT, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::CONFLICT) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber() && TRI_ERROR_ARANGO_CONFLICT == slice.get(arangodb::StaticStrings::ErrorNum).getNumber())); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"); // Check it's not gone ASSERT_NE(analyzer, nullptr); } // still in use + force (success) TEST_F(RestAnalyzerHandlerTest, test_remove_still_in_use_force) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix("testAnalyzer2"); request.values().emplace("force", "true"); // hold ref to mark in-use auto inUseAnalyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"); ASSERT_NE(nullptr, inUseAnalyzer); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2" == slice.get("name").copyString())); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"); ASSERT_EQ(nullptr, analyzer); } // removal with db name in analyzer name TEST_F(RestAnalyzerHandlerTest, test_remove_with_db_name) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); EXPECT_EQ(nullptr, analyzer); } TEST_F(RestAnalyzerHandlerTest, test_remove_success) { grantOnDb(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RW); auto requestPtr = std::make_unique(_system_vocbase); auto& request = *requestPtr; auto responcePtr = std::make_unique(); auto& responce = *responcePtr; arangodb::iresearch::RestAnalyzerHandler handler(server.server(), requestPtr.release(), responcePtr.release()); request.setRequestType(arangodb::rest::RequestType::DELETE_REQ); request.addSuffix("testAnalyzer1"); auto status = handler.execute(); EXPECT_EQ(arangodb::RestStatus::DONE, status); EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode()); auto slice = responce._payload.slice(); EXPECT_TRUE(slice.isObject()); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber() && size_t(arangodb::rest::ResponseCode::OK) == slice.get(arangodb::StaticStrings::Code).getNumber())); EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean())); EXPECT_TRUE((slice.hasKey("name") && slice.get("name").isString() && arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1" == slice.get("name").copyString())); auto analyzer = analyzers.get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"); ASSERT_EQ(nullptr, analyzer); }