diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa1374eb4b..aec0a598992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ ----------- ### Internals -* None. +* The CMake option `REALM_MONGODB_ENDPOINT` for running the object-store-tests against baas has been deprecated in favor of an environment variable of the same name ([PR #7423](https://github.com/realm/realm-core/pull/7423)). +* The object-store-tests test suite can now launch baas containers on its own by specifying a `BAASAAS_API_KEY` in the environment ([PR #7423](https://github.com/realm/realm-core/pull/7423)). ---------------------------------------------- diff --git a/dependencies.yml b/dependencies.yml index 04130370b31..8037f32918f 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -3,5 +3,5 @@ VERSION: 14.2.0 OPENSSL_VERSION: 3.2.0 ZLIB_VERSION: 1.2.13 # https://github.com/10gen/baas/commits -# dd016 is 2024 Feb 22 -BAAS_VERSION: dd01629d83b86292af9c59ebe2a28673c2e559cf +# acb71d0 is 2024 Mar 12 +BAAS_VERSION: acb71d0183b33eb304bb496390567efcfb8a6e60 diff --git a/evergreen/config.yml b/evergreen/config.yml index 49b952002d4..2b4831daf87 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -78,7 +78,6 @@ functions: if [ -z "${disable_tests_against_baas|}" ]; then scheme="http" set_cmake_var baas_vars REALM_ENABLE_AUTH_TESTS BOOL On - set_cmake_var baas_vars REALM_MONGODB_ENDPOINT STRING "$scheme://localhost:9090" if [ -n "${baas_admin_port|}" ]; then set_cmake_var baas_vars REALM_ADMIN_ENDPOINT STRING "$scheme://localhost:${baas_admin_port}" fi @@ -156,7 +155,7 @@ functions: if [ -n "${curl_base|}" ]; then set_cmake_var curl_vars CURL_LIBRARY PATH "$(./evergreen/abspath.sh ${curl_base}/lib/libcurl.dll.a)" set_cmake_var curl_vars CURL_INCLUDE_DIR PATH "$(./evergreen/abspath.sh ${curl_base}/include)" - set_cmake_var baas_vars REALM_CURL_CACERTS PATH "$(./evergreen/abspath.sh "${curl_base}/bin/cacert.pem")" + set_cmake_var baas_vars REALM_CURL_CACERTS PATH "$(./evergreen/abspath.sh "${curl_base}/bin/curl-ca-bundle.crt")" fi set_cmake_var realm_vars REALM_NO_TESTS BOOL ${no_tests|Off} @@ -208,10 +207,17 @@ functions: file: './realm-core/benchmark_results/results.latest.json' "run tests": + - command: expansions.update + params: + file: realm-core/dependencies.yml - command: shell.exec params: working_dir: realm-core shell: bash + env: + BAASAAS_API_KEY: "${baasaas_api_key}" + BAASAAS_REF_SPEC: "${BAAS_VERSION}" + BAASAAS_START_MODE: "githash" script: |- set -o errexit set -o verbose @@ -244,6 +250,12 @@ functions: TEST_FLAGS="--no-tests=error $TEST_FLAGS ${test_flags|}" + if [[ -n "${disable_tests_against_baas|}" ]]; then + unset BAASAAS_API_KEY + unset BAASAAS_REF_SPEC + unset BAASAAS_START_MODE + fi + if [[ -n "${llvm_symbolizer}" ]]; then export ASAN_SYMBOLIZER_PATH="$(./evergreen/abspath.sh ${llvm_symbolizer})" fi @@ -977,6 +989,7 @@ tasks: test_label: objstore-local test_executable_name: "realm-object-store-tests" verbose_test_output: true + disable_tests_against_baas: true - func: "check branch state" # These are baas object store tests that run against baas running on a remote host @@ -984,11 +997,9 @@ tasks: tags: [ "test_suite", "for_pull_requests", "requires_baas" ] exec_timeout_secs: 3600 commands: - - func: "launch remote baas" - func: "compile" vars: target_to_build: ObjectStoreTests - - func: "wait for remote baas to start" - func: "run tests" vars: test_label: objstore-baas diff --git a/how-to-build.md b/how-to-build.md index f394c7ddb1e..92bd693cee7 100644 --- a/how-to-build.md +++ b/how-to-build.md @@ -145,39 +145,41 @@ These are the available variables: testing process as soon as a check fails or an unexpected exception is thrown in a test. -## Running [app] tests against a local MongoDB BAAS - -Due to MongoDB security policies, running baas requires company issued AWS account credentials. -These are for MongoDB employees only, if you do not have these, reach out to #realm-core. -Once you have them, they need to be set in the shell environment. - -First, log in to aws using their command line tool. On mac this requries `brew install awscli`. -Then login using `aws configure` and input your access key and secret acess key. The other -configuration options can be left as none. This creates a correctly formatted file locally at -`~/.aws/credentials` which we will use later. - -If you do not want to install the aws command line tools, you can also create the aws file -manually in the correct location (`~/.aws/credentials`) with the following contents: +## Running [app] tests against a local BAAS instance +If you already have a baas instance running, you can specify that directly via the +`BAAS_BASE_URL` environment variable. You can run baas in a local docker container using +instructions from [the wiki](https://wiki.corp.mongodb.com/display/10GEN/%28Device+Sync%29+Using+Docker+to+run+a+BAAS+server+instance). ``` -AWS_ACCESS_KEY_ID = -AWS_SECRET_ACCESS_KEY = +export BAAS_BASE_URL=http://localhost:9090 +mkdir build.sync.ninja +cmake -B build.sync.ninja -G Ninja -DREALM_ENABLE_AUTH_TESTS=1 +cmake --build build.sync.ninja --target realm-object-store-tests +./build.sync.ninja/test/object-store/realm-object-store-tests -d=1 ``` -We use a script to fetch the dependencies for and run baas locally. Use the `-b sha` to use a particular version from https://github.com/10gen/baas/ -The script uses the configuration from https://github.com/10gen/baas/blob/master/etc/configs/test_rcore_config.json +## Running [app] tests against an on-demand BAASAAS container +Due to MongoDB security policies, running baas requires company issued credentials. +These are for MongoDB employees only, if you do not have these, reach out to +#appx-device-sync-internal. Once you have a baasaas API key, it needs to be set +in the shell environment. ``` -./evergreen/install_baas.sh -w baas -``` - -To run the [app] tests against the local baas, you need to configure a build with some cmake options to tell the tests where to point to. -``` +export BAASAAS_API_KEY= mkdir build.sync.ninja -cmake -B build.sync.ninja -G Ninja -DREALM_ENABLE_AUTH_TESTS=1 -DREALM_MONGODB_ENDPOINT=http://localhost:9090 +cmake -B build.sync.ninja -G Ninja -DREALM_ENABLE_AUTH_TESTS=1 cmake --build build.sync.ninja --target realm-object-store-tests ./build.sync.ninja/test/object-store/realm-object-store-tests -d=1 ``` +You can tell the object-store tests to use a specific version of baas with the +`BAASAAS_START_MODE` environment variable, which can either be `githash`, `patchid`, +or `branch`. If you specify a start mode, you need to tell it which githash or +branch name to start with via the `BAASAAS_REF_SPEC` environment variable. Omitting +these will use the latest available commit from the main branch of baas. + +If you've started a baasaas container already via the baasaas CLI, you can tell +the object-store tests to use that with the `BAASAAS_INSTANCE_ID` environment variable. + ### Developing inside a container diff --git a/test/object-store/CMakeLists.txt b/test/object-store/CMakeLists.txt index b23946ec353..406f018f2aa 100644 --- a/test/object-store/CMakeLists.txt +++ b/test/object-store/CMakeLists.txt @@ -92,6 +92,12 @@ endif() target_link_libraries(ObjectStoreTestLib Catch2::Catch2 ObjectStore RealmFFIStatic TestUtil) enable_stdfilesystem(ObjectStoreTestLib) +if(REALM_CURL_CACERTS) + target_compile_definitions(ObjectStoreTestLib PRIVATE + REALM_CURL_CACERTS="${REALM_CURL_CACERTS}" + ) +endif() + add_executable(ObjectStoreTests main.cpp ${RESOURCES}) set_target_properties(ObjectStoreTests PROPERTIES OUTPUT_NAME realm-object-store-tests) target_link_libraries(ObjectStoreTests ObjectStoreTestLib TestUtil) @@ -131,16 +137,17 @@ if(REALM_ENABLE_SYNC) target_link_libraries(ObjectStoreTestLib SyncServer) option(REALM_ENABLE_AUTH_TESTS "" OFF) if(REALM_ENABLE_AUTH_TESTS) - if(NOT REALM_MONGODB_ENDPOINT) - message(FATAL_ERROR "REALM_MONGODB_ENDPOINT must be set when specifying REALM_ENABLE_AUTH_TESTS.") - endif() - - message(STATUS "Auth tests enabled: ${REALM_MONGODB_ENDPOINT}") target_compile_definitions(ObjectStoreTestLib PRIVATE REALM_ENABLE_AUTH_TESTS=1 - REALM_MONGODB_ENDPOINT="${REALM_MONGODB_ENDPOINT}" ) + if(REALM_MONGODB_ENDPOINT) + message(STATUS "Auth tests enabled: ${REALM_MONGODB_ENDPOINT}") + target_compile_definitions(ObjectStoreTestLib PRIVATE + REALM_MONGODB_ENDPOINT="${REALM_MONGODB_ENDPOINT}" + ) + endif() + if(REALM_ADMIN_ENDPOINT) message(STATUS "BAAS admin endpoint: ${REALM_ADMIN_ENDPOINT}") target_compile_definitions(ObjectStoreTests PRIVATE diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 181ddb15858..92e13b89a2d 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -3924,7 +3924,6 @@ TEST_CASE("app: base_url", "[sync][app][base_url]") { std::unique_ptr app_session; auto redir_transport = std::make_shared(); AutoVerifiedEmailCredentials creds; - util::LogCategory::realm.set_default_level_threshold(realm::util::Logger::Level::TEST_LOGGING_LEVEL); auto logger = util::Logger::get_default_logger(); App::Config app_config = {"fake-app-id"}; diff --git a/test/object-store/test_runner.cpp b/test/object-store/test_runner.cpp index e434e574423..448e428ba3e 100644 --- a/test/object-store/test_runner.cpp +++ b/test/object-store/test_runner.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #if TEST_SCHEDULER_UV @@ -38,6 +39,76 @@ #include #include +#ifndef TEST_ENABLE_LOGGING +#define TEST_ENABLE_LOGGING 0 // change to 1 to enable trace-level logging +#endif + +#ifndef TEST_LOGGING_LEVEL +#if TEST_ENABLE_LOGGING +#define TEST_LOGGING_LEVEL all +#else +#define TEST_LOGGING_LEVEL off +#endif // TEST_ENABLE_LOGGING +#endif // TEST_LOGGING_LEVEL + +#define TEST_LOGGING_LEVEL_STORAGE off +#define TEST_LOGGING_LEVEL_SERVER off +/* +#define TEST_LOGGING_LEVEL_SYNC off +#define TEST_LOGGING_LEVEL_RESET trace +#define TEST_LOGGING_LEVEL_APP off +*/ + +static std::vector> default_log_levels = { + {"Realm", realm::util::Logger::Level::TEST_LOGGING_LEVEL}, +#ifdef TEST_LOGGING_LEVEL_STORAGE + {"Realm.Storage", realm::util::Logger::Level::TEST_LOGGING_LEVEL_STORAGE}, +#endif +#ifdef TEST_LOGGING_LEVEL_TRANSACTION + {"Realm.Storage.Transaction", realm::util::Logger::Level::TEST_LOGGING_LEVEL_TRANSACTION}, +#endif +#ifdef TEST_LOGGING_LEVEL_QUERY + {"Realm.Storage.Query", realm::util::Logger::Level::TEST_LOGGING_LEVEL_QUERY}, +#endif +#ifdef TEST_LOGGING_LEVEL_OBJECT + {"Realm.Storage.Object", realm::util::Logger::Level::TEST_LOGGING_LEVEL_OBJECT}, +#endif +#ifdef TEST_LOGGING_LEVEL_NOTIFICATION + {"Realm.Storage.Notification", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NOTIFICATION}, +#endif +#ifdef TEST_LOGGING_LEVEL_SYNC + {"Realm.Sync", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SYNC}, +#endif +#ifdef TEST_LOGGING_LEVEL_CLIENT + {"Realm.Sync.Client", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CLIENT}, +#endif +#ifdef TEST_LOGGING_LEVEL_SESSION + {"Realm.Sync.Client.Session", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SESSION}, +#endif +#ifdef TEST_LOGGING_LEVEL_CHANGESET + {"Realm.Sync.Client.Changeset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CHANGESET}, +#endif +#ifdef TEST_LOGGING_LEVEL_NETWORK + {"Realm.Sync.Client.Network", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NETWORK}, +#endif +#ifdef TEST_LOGGING_LEVEL_RESET + {"Realm.Sync.Client.Reset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_RESET}, +#endif +#ifdef TEST_LOGGING_LEVEL_SERVER + {"Realm.Sync.Server", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SERVER}, +#endif +#ifdef TEST_LOGGING_LEVEL_APP + {"Realm.App", realm::util::Logger::Level::TEST_LOGGING_LEVEL_APP}, +#endif +}; + +static void set_default_level_thresholds() +{ + for (auto [cat, level] : default_log_levels) { + realm::util::LogCategory::get_category(cat).set_default_level_threshold(level); + } +} + int run_object_store_tests(int argc, const char** argv); @@ -91,6 +162,8 @@ int run_object_store_tests(int argc, const char** argv) }); #endif + set_default_level_thresholds(); + Catch::Session session; session.useConfigData(config); int result = session.run(argc, argv); diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index eaf27468612..41dd4337f8d 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -248,17 +248,17 @@ size_t curl_write_cb(char* ptr, size_t size, size_t nmemb, std::string* response size_t curl_header_cb(char* buffer, size_t size, size_t nitems, std::map* response_headers) { REALM_ASSERT(response_headers); - std::string combined(buffer, size * nitems); + std::string_view combined(buffer, size * nitems); if (auto pos = combined.find(':'); pos != std::string::npos) { - std::string key = combined.substr(0, pos); - std::string value = combined.substr(pos + 1); - while (value.size() > 0 && value[0] == ' ') { - value = value.substr(1); + std::string_view key = combined.substr(0, pos); + std::string_view value = combined.substr(pos + 1); + if (auto first_not_space = value.find_first_not_of(' '); first_not_space != std::string::npos) { + value = value.substr(first_not_space); } - while (value.size() > 0 && (value[value.size() - 1] == '\r' || value[value.size() - 1] == '\n')) { - value = value.substr(0, value.size() - 1); + if (auto last_not_nl = value.find_last_not_of("\r\n"); last_not_nl != std::string::npos) { + value = value.substr(0, last_not_nl + 1); } - response_headers->insert({key, value}); + response_headers->insert({std::string{key}, std::string{value}}); } else { if (combined.size() > 5 && combined.substr(0, 5) != "HTTP/") { // ignore for now HTTP/1.1 ... @@ -268,6 +268,15 @@ size_t curl_header_cb(char* buffer, size_t size, size_t nitems, std::maperror("curl_easy_perform() failed when sending request to '%1' with body '%2': %3", request.url, + request.body, curl_easy_strerror(response_code)); } + if (logger->would_log(util::Logger::Level::trace)) { + std::string coid = [&] { + auto coid_header = response_headers.find("X-Appservices-Request-Id"); + if (coid_header == response_headers.end()) { + return std::string{}; + } + return util::format("BaaS Coid: \"%1\"", coid_header->second); + }(); + + logger->trace("Baas API %1 request to %2 took %3 %4\n", app::httpmethod_to_string(request.method), + request.url, std::chrono::duration_cast(total_time), coid); + } + int http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); return { @@ -341,6 +372,252 @@ app::Response do_http_request(const app::Request& request) }; } +class Baasaas { +public: + enum class StartMode { Default, GitHash, Branch, PatchId }; + explicit Baasaas(std::string api_key, StartMode mode, std::string ref_spec) + : m_api_key(std::move(api_key)) + , m_base_url(get_baasaas_base_url()) + , m_externally_managed_instance(false) + { + auto logger = util::Logger::get_default_logger(); + std::string url_path = "startContainer"; + if (mode == StartMode::GitHash) { + url_path = util::format("startContainer?githash=%1", ref_spec); + logger->info("Starting baasaas container with githash of %1", ref_spec); + } + else if (mode == StartMode::Branch) { + url_path = util::format("startContainer?branch=%1", ref_spec); + logger->info("Starting baasaas container on branch %1", ref_spec); + } + else if (mode == StartMode::PatchId) { + url_path = util::format("startContainer?patchId=%1", ref_spec); + logger->info("Starting baasaas container for patch id %1", ref_spec); + } + else { + logger->info("Starting baasaas container"); + } + + auto resp = do_request(std::move(url_path), app::HttpMethod::post); + m_container_id = resp["id"].get(); + logger->info("Baasaas container started with id \"%1\"", m_container_id); + auto lock_file = util::File(std::string{s_baasaas_lock_file_name}, util::File::mode_Write); + lock_file.write(m_container_id); + } + + explicit Baasaas(std::string api_key, std::string baasaas_instance_id) + : m_api_key(std::move(api_key)) + , m_base_url(get_baasaas_base_url()) + , m_container_id(std::move(baasaas_instance_id)) + , m_externally_managed_instance(true) + { + auto logger = util::Logger::get_default_logger(); + logger->info("Using externally managed baasaas instance \"%1\"", m_container_id); + } + + Baasaas(const Baasaas&) = delete; + Baasaas(Baasaas&&) = delete; + Baasaas& operator=(const Baasaas&) = delete; + Baasaas& operator=(Baasaas&&) = delete; + + ~Baasaas() + { + stop(); + } + + void poll() + { + if (!m_http_endpoint.empty() || m_container_id.empty()) { + return; + } + + auto logger = util::Logger::get_default_logger(); + auto poll_start_at = std::chrono::system_clock::now(); + std::string http_endpoint; + std::string mongo_endpoint; + bool logged = false; + while (std::chrono::system_clock::now() - poll_start_at < std::chrono::minutes(2) && + m_http_endpoint.empty()) { + if (http_endpoint.empty()) { + auto status_obj = + do_request(util::format("containerStatus?id=%1", m_container_id), app::HttpMethod::get); + if (!status_obj["httpUrl"].is_null()) { + http_endpoint = status_obj["httpUrl"].get(); + mongo_endpoint = status_obj["mongoUrl"].get(); + } + } + else { + app::Request baas_req; + baas_req.url = util::format("%1/api/private/v1.0/version", http_endpoint); + baas_req.method = app::HttpMethod::get; + baas_req.headers.insert_or_assign("Content-Type", "application/json"); + auto baas_resp = do_http_request(baas_req); + if (baas_resp.http_status_code >= 200 && baas_resp.http_status_code < 300) { + m_http_endpoint = http_endpoint; + m_mongo_endpoint = mongo_endpoint; + break; + } + } + + if (!logged) { + logger->info("Waiting for baasaas container \"%1\" to be ready", m_container_id); + logged = true; + } + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + + if (m_http_endpoint.empty()) { + throw std::runtime_error( + util::format("Failed to launch baasaas container %1 within 2 minutes", m_container_id)); + } + } + + void stop() + { + if (m_externally_managed_instance) { + return; + } + auto container_id = std::move(m_container_id); + if (container_id.empty()) { + return; + } + + auto logger = util::Logger::get_default_logger(); + logger->info("Stopping baasaas container with id \"%1\"", container_id); + do_request(util::format("stopContainer?id=%1", container_id), app::HttpMethod::post); + auto lock_file = util::File(std::string{s_baasaas_lock_file_name}, util::File::mode_Write); + lock_file.resize(0); + lock_file.close(); + util::File::remove(lock_file.get_path()); + } + + const std::string& http_endpoint() + { + poll(); + return m_http_endpoint; + } + + const std::string& mongo_endpoint() + { + poll(); + return m_mongo_endpoint; + } + +private: + nlohmann::json do_request(std::string api_path, app::HttpMethod method) + { + app::Request request; + + request.url = util::format("%1/%2", m_base_url, api_path); + request.method = method; + request.headers.insert_or_assign("apiKey", m_api_key); + request.headers.insert_or_assign("Content-Type", "application/json"); + auto response = do_http_request(request); + REALM_ASSERT_EX(response.http_status_code >= 200 && response.http_status_code < 300, + util::format("Baasaas api response code: %1 Response body: %2", response.http_status_code, + response.body)); + return nlohmann::json::parse(response.body); + } + + static std::string get_baasaas_base_url() + { + auto env_value = getenv_sv("BAASAAS_BASE_URL"); + if (env_value.empty()) { + // This is the current default endpoint for baasaas maintained by the sync team. + // You can reach out for help in #appx-device-sync-internal if there are problems. + return "https://us-east-1.aws.data.mongodb-api.com/app/baas-container-service-autzb/endpoint"; + } + + return unquote_string(env_value); + } + + constexpr static std::string_view s_baasaas_lock_file_name = "baasaas_instance.lock"; + + std::string m_api_key; + std::string m_base_url; + std::string m_container_id; + bool m_externally_managed_instance; + std::string m_http_endpoint; + std::string m_mongo_endpoint; +}; + +class BaasaasLauncher : public Catch::EventListenerBase { +public: + static std::optional& get_baasaas_holder() + { + static std::optional global_baasaas = std::nullopt; + return global_baasaas; + } + + using Catch::EventListenerBase::EventListenerBase; + + void testRunStarting(Catch::TestRunInfo const&) override + { + std::string_view api_key(getenv_sv("BAASAAS_API_KEY")); + if (api_key.empty()) { + return; + } + + // Allow overriding the baas base url at runtime via an environment variable, even if BAASAAS_API_KEY + // is also specified. + if (!getenv_sv("BAAS_BASE_URL").empty()) { + return; + } + + // If we've started a baasaas container outside of running the tests, then use that instead of + // figuring out how to start our own. + if (auto baasaas_instance = getenv_sv("BAASAAS_INSTANCE_ID"); !baasaas_instance.empty()) { + auto& baasaas_holder = get_baasaas_holder(); + REALM_ASSERT(!baasaas_holder); + baasaas_holder.emplace(std::string{api_key}, std::string{baasaas_instance}); + return; + } + + std::string_view ref_spec(getenv_sv("BAASAAS_REF_SPEC")); + std::string_view mode_spec(getenv_sv("BAASAAS_START_MODE")); + Baasaas::StartMode mode = Baasaas::StartMode::Default; + if (mode_spec == "branch") { + if (ref_spec.empty()) { + throw std::runtime_error("Expected branch name in BAASAAS_REF_SPEC env variable, but it was empty"); + } + mode = Baasaas::StartMode::Branch; + } + else if (mode_spec == "githash") { + if (ref_spec.empty()) { + throw std::runtime_error("Expected git hash in BAASAAS_REF_SPEC env variable, but it was empty"); + } + mode = Baasaas::StartMode::GitHash; + } + else if (mode_spec == "patchid") { + if (ref_spec.empty()) { + throw std::runtime_error("Expected patch id in BAASAAS_REF_SPEC env variable, but it was empty"); + } + mode = Baasaas::StartMode::PatchId; + } + else { + if (!mode_spec.empty()) { + throw std::runtime_error("Excepted BAASAAS_START_MODE to be \"githash\", \"patchid\", or \"branch\""); + } + ref_spec = {}; + } + + auto& baasaas_holder = get_baasaas_holder(); + REALM_ASSERT(!baasaas_holder); + baasaas_holder.emplace(std::string{api_key}, mode, std::string{ref_spec}); + + get_runtime_app_session(); + } + + void testRunEnded(Catch::TestRunStats const&) override + { + if (auto& baasaas_holder = get_baasaas_holder(); baasaas_holder.has_value()) { + baasaas_holder->stop(); + } + } +}; + +CATCH_REGISTER_LISTENER(BaasaasLauncher) + AdminAPIEndpoint AdminAPIEndpoint::operator[](StringData name) const { return AdminAPIEndpoint(util::format("%1/%2", m_url, name), m_access_token); @@ -581,7 +858,7 @@ AdminAPISession::Service AdminAPISession::get_sync_service(const std::string& ap void AdminAPISession::trigger_client_reset(const std::string& app_id, int64_t file_ident) const { - auto endpoint = apps(APIFamily::Private)[app_id]["sync"]["force_reset"]; + auto endpoint = apps(APIFamily::Admin)[app_id]["sync"]["force_reset"]; endpoint.put_json(nlohmann::json{{"file_ident", file_ident}}); } @@ -874,6 +1151,43 @@ realm::Schema get_default_schema() return realm::Schema({dog_schema, cat_schema, person_schema}); } +std::string get_base_url() +{ + if (auto baas_url = getenv_sv("BAAS_BASE_URL"); !baas_url.empty()) { + return std::string{baas_url}; + } + if (auto& baasaas_holder = BaasaasLauncher::get_baasaas_holder(); baasaas_holder.has_value()) { + return baasaas_holder->http_endpoint(); + } + + return get_compile_time_base_url(); +} + +std::string get_admin_url() +{ + if (auto baas_admin_url = getenv_sv("BAAS_ADMIN_URL"); !baas_admin_url.empty()) { + return std::string{baas_admin_url}; + } + if (auto compile_url = get_compile_time_admin_url(); !compile_url.empty()) { + return compile_url; + } + + return get_base_url(); +} + +std::string get_mongodb_server() +{ + if (auto baas_url = getenv_sv("BAAS_MONGO_URL"); !baas_url.empty()) { + return std::string{baas_url}; + } + + if (auto& baasaas_holder = BaasaasLauncher::get_baasaas_holder(); baasaas_holder.has_value()) { + return baasaas_holder->mongo_endpoint(); + } + return "mongodb://localhost:26000"; +} + + AppCreateConfig default_app_config() { ObjectId id = ObjectId::gen(); @@ -1252,9 +1566,11 @@ AppSession create_app(const AppCreateConfig& config) return object_schema.table_type == ObjectSchema::ObjectType::TopLevel; }); if (any_sync_types) { - timed_sleeping_wait_for([&] { - return session.is_initial_sync_complete(app_id); - }); + timed_sleeping_wait_for( + [&] { + return session.is_initial_sync_complete(app_id); + }, + std::chrono::seconds(30), std::chrono::seconds(1)); } return {client_app_id, app_id, session, config}; @@ -1269,10 +1585,6 @@ AppSession get_runtime_app_session() return cached_app_session; } -std::string get_mongodb_server() -{ - return "mongodb://localhost:26000"; -} #ifdef REALM_MONGODB_ENDPOINT TEST_CASE("app: baas admin api", "[sync][app][admin api][baas]") { diff --git a/test/object-store/util/sync/baas_admin_api.hpp b/test/object-store/util/sync/baas_admin_api.hpp index ce85d8ecc22..faa6cd51623 100644 --- a/test/object-store/util/sync/baas_admin_api.hpp +++ b/test/object-store/util/sync/baas_admin_api.hpp @@ -289,6 +289,8 @@ class SynchronousTestTransport : public app::GenericNetworkTransport { AppSession get_runtime_app_session(); std::string get_mongodb_server(); +std::string get_base_url(); +std::string get_admin_url(); template inline app::App::Config get_config(Factory factory, const AppSession& app_session) diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index 52fd33d6026..ddcea52d4bd 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -175,6 +175,23 @@ ExpectedRealmPaths::ExpectedRealmPaths(const std::string& base_path, const std:: legacy_sync_path = (dir_builder / cleaned_partition).string(); } +std::string unquote_string(std::string_view possibly_quoted_string) +{ + if (possibly_quoted_string.size() > 0) { + auto check_char = possibly_quoted_string.front(); + if (check_char == '"' || check_char == '\'') { + possibly_quoted_string.remove_prefix(1); + } + } + if (possibly_quoted_string.size() > 0) { + auto check_char = possibly_quoted_string.back(); + if (check_char == '"' || check_char == '\'') { + possibly_quoted_string.remove_suffix(1); + } + } + return std::string{possibly_quoted_string}; +} + #if REALM_ENABLE_SYNC void subscribe_to_all_and_bootstrap(Realm& realm) @@ -205,37 +222,23 @@ void wait_for_sessions_to_close(const TestAppSession& test_app_session) std::chrono::minutes(5), std::chrono::milliseconds(100)); } -static std::string unquote_string(std::string_view possibly_quoted_string) +std::string get_compile_time_base_url() { - if (possibly_quoted_string.size() > 0) { - auto check_char = possibly_quoted_string.front(); - if (check_char == '"' || check_char == '\'') { - possibly_quoted_string.remove_prefix(1); - } - } - if (possibly_quoted_string.size() > 0) { - auto check_char = possibly_quoted_string.back(); - if (check_char == '"' || check_char == '\'') { - possibly_quoted_string.remove_suffix(1); - } - } - return std::string{possibly_quoted_string}; -} - #ifdef REALM_MONGODB_ENDPOINT -std::string get_base_url() -{ // allows configuration with or without quotes return unquote_string(REALM_QUOTE(REALM_MONGODB_ENDPOINT)); +#else + return {}; +#endif } -std::string get_admin_url() +std::string get_compile_time_admin_url() { #ifdef REALM_ADMIN_ENDPOINT // allows configuration with or without quotes return unquote_string(REALM_QUOTE(REALM_ADMIN_ENDPOINT)); #else - return get_base_url(); + return {}; #endif } #endif // REALM_MONGODB_ENDPOINT @@ -316,8 +319,6 @@ void async_open_realm(const Realm::Config& config, finish(std::move(tsr), err); } -#endif // REALM_ENABLE_SYNC - class TestHelper { public: static DBRef& get_db(SharedRealm const& shared_realm) @@ -561,9 +562,11 @@ struct BaasClientReset : public TestClientReset { // // So just don't try to do anything until initial sync is done and we're sure the server is in a stable // state. - timed_sleeping_wait_for([&] { - return app_session.admin_api.is_initial_sync_complete(app_session.server_app_id); - }); + timed_sleeping_wait_for( + [&] { + return app_session.admin_api.is_initial_sync_complete(app_session.server_app_id); + }, + std::chrono::seconds(30), std::chrono::seconds(1)); auto realm = Realm::get_shared_realm(m_local_config); auto session = sync_manager->get_existing_session(realm->config().path); diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index 9649f7f3517..c58c58de52d 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -128,6 +128,10 @@ struct ExpectedRealmPaths { std::vector legacy_sync_directories_to_make; }; +// Takes a string_view of a possibly quoted string (i.e. the string begins with '"' and ends with '"') +// and returns an owned string without the quotes. +std::string unquote_string(std::string_view possibly_quoted_string); + #if REALM_ENABLE_SYNC template @@ -140,10 +144,8 @@ void subscribe_to_all_and_bootstrap(Realm& realm); #if REALM_ENABLE_AUTH_TESTS void wait_for_sessions_to_close(const TestAppSession& test_app_session); -#ifdef REALM_MONGODB_ENDPOINT -std::string get_base_url(); -std::string get_admin_url(); -#endif +std::string get_compile_time_base_url(); +std::string get_compile_time_admin_url(); #endif // REALM_ENABLE_AUTH_TESTS struct AutoVerifiedEmailCredentials : app::AppCredentials { diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index 5c4983678a5..048130cf40a 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -65,62 +65,11 @@ inline static int mkstemp(char* _template) using namespace realm; -static std::vector> default_log_levels = { - {"Realm", realm::util::Logger::Level::TEST_LOGGING_LEVEL}, -#ifdef TEST_LOGGING_LEVEL_STORAGE - {"Realm.Storage", realm::util::Logger::Level::TEST_LOGGING_LEVEL_STORAGE}, -#endif -#ifdef TEST_LOGGING_LEVEL_TRANSACTION - {"Realm.Storage.Transaction", realm::util::Logger::Level::TEST_LOGGING_LEVEL_TRANSACTION}, -#endif -#ifdef TEST_LOGGING_LEVEL_QUERY - {"Realm.Storage.Query", realm::util::Logger::Level::TEST_LOGGING_LEVEL_QUERY}, -#endif -#ifdef TEST_LOGGING_LEVEL_OBJECT - {"Realm.Storage.Object", realm::util::Logger::Level::TEST_LOGGING_LEVEL_OBJECT}, -#endif -#ifdef TEST_LOGGING_LEVEL_NOTIFICATION - {"Realm.Storage.Notification", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NOTIFICATION}, -#endif -#ifdef TEST_LOGGING_LEVEL_SYNC - {"Realm.Sync", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SYNC}, -#endif -#ifdef TEST_LOGGING_LEVEL_CLIENT - {"Realm.Sync.Client", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CLIENT}, -#endif -#ifdef TEST_LOGGING_LEVEL_SESSION - {"Realm.Sync.Client.Session", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SESSION}, -#endif -#ifdef TEST_LOGGING_LEVEL_CHANGESET - {"Realm.Sync.Client.Changeset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CHANGESET}, -#endif -#ifdef TEST_LOGGING_LEVEL_NETWORK - {"Realm.Sync.Client.Network", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NETWORK}, -#endif -#ifdef TEST_LOGGING_LEVEL_RESET - {"Realm.Sync.Client.Reset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_RESET}, -#endif -#ifdef TEST_LOGGING_LEVEL_SERVER - {"Realm.Sync.Server", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SERVER}, -#endif -#ifdef TEST_LOGGING_LEVEL_APP - {"Realm.App", realm::util::Logger::Level::TEST_LOGGING_LEVEL_APP}, -#endif -}; - -static void set_default_level_thresholds() -{ - for (auto [cat, level] : default_log_levels) { - realm::util::LogCategory::get_category(cat).set_default_level_threshold(level); - } -} - TestFile::TestFile() { disable_sync_to_disk(); m_temp_dir = util::make_temp_dir(); path = (fs::path(m_temp_dir) / "realm.XXXXXX").string(); - set_default_level_thresholds(); if (const char* crypt_key = test_util::crypt_key()) { encryption_key = std::vector(crypt_key, crypt_key + 64); } @@ -167,7 +116,6 @@ InMemoryTestFile::InMemoryTestFile() in_memory = true; schema_version = 0; encryption_key = std::vector(); - set_default_level_thresholds(); } DBOptions InMemoryTestFile::options() const @@ -390,7 +338,6 @@ TestAppSession::TestAppSession(AppSession session, if (!m_transport) m_transport = instance_of; auto app_config = get_config(m_transport, *m_app_session); - set_default_level_thresholds(); set_app_config_defaults(app_config, m_transport); util::try_make_dir(m_base_file_path); @@ -472,10 +419,7 @@ std::vector TestAppSession::get_documents(SyncUser& user, co // MARK: - TestSyncManager -TestSyncManager::Config::Config() -{ - set_default_level_thresholds(); -} +TestSyncManager::Config::Config() {} TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& sync_server_config) : m_sync_server(sync_server_config) @@ -546,8 +490,6 @@ OfflineAppSession::OfflineAppSession(OfflineAppSession::Config config) sc_config.metadata_mode = config.metadata_mode; sc_config.socket_provider = config.socket_provider; - set_default_level_thresholds(); - m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config, sc_config); } diff --git a/test/object-store/util/test_file.hpp b/test/object-store/util/test_file.hpp index 4d3e01c2891..d3b50438e25 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -98,26 +98,6 @@ struct InMemoryTestFile : realm::Realm::Config { void advance_and_notify(realm::Realm& realm); void on_change_but_no_notify(realm::Realm& realm); -#ifndef TEST_ENABLE_LOGGING -#define TEST_ENABLE_LOGGING 0 // change to 1 to enable trace-level logging -#endif - -#ifndef TEST_LOGGING_LEVEL -#if TEST_ENABLE_LOGGING -#define TEST_LOGGING_LEVEL all -#else -#define TEST_LOGGING_LEVEL off -#endif // TEST_ENABLE_LOGGING -#endif // TEST_LOGGING_LEVEL - -#define TEST_LOGGING_LEVEL_STORAGE off -#define TEST_LOGGING_LEVEL_SERVER off -/* -#define TEST_LOGGING_LEVEL_SYNC off -#define TEST_LOGGING_LEVEL_RESET trace -#define TEST_LOGGING_LEVEL_APP off -*/ - #if REALM_ENABLE_SYNC using StartImmediately = realm::util::TaggedBool; @@ -198,7 +178,6 @@ class TestSyncManager { std::string base_path; realm::SyncManager::MetadataMode metadata_mode = realm::SyncManager::MetadataMode::NoMetadata; bool should_teardown_test_directory = true; - realm::util::Logger::Level log_level = realm::util::Logger::Level::TEST_LOGGING_LEVEL; bool start_sync_client = true; };