From 42b5cfa41afcf0a6dcc6cccd8ddc847dde08bd32 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 4 Jan 2024 14:04:46 -0500 Subject: [PATCH 01/21] Start a baasaas instance to run baas integration tests against --- evergreen/config.yml | 11 +- test/object-store/test_runner.cpp | 4 + .../object-store/util/sync/baas_admin_api.cpp | 251 +++++++++++++++++- .../object-store/util/sync/baas_admin_api.hpp | 2 + .../util/sync/sync_test_utils.cpp | 22 +- .../util/sync/sync_test_utils.hpp | 4 +- 6 files changed, 272 insertions(+), 22 deletions(-) diff --git a/evergreen/config.yml b/evergreen/config.yml index 75665c8e64b..dc7453e0310 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -205,6 +205,8 @@ functions: params: working_dir: realm-core shell: bash + env: + BAASAAS_API_KEY: "${baasaas_api_key}" script: |- set -o errexit set -o verbose @@ -231,6 +233,10 @@ functions: TEST_FLAGS="--no-tests=error $TEST_FLAGS ${test_flags|}" + if [[ -z "${baas_branch}" ]]; then + unset BAASAAS_API_KEY + fi + if [[ -n "${llvm_symbolizer}" ]]; then export ASAN_SYMBOLIZER_PATH="$(./evergreen/abspath.sh ${llvm_symbolizer})" fi @@ -974,18 +980,15 @@ tasks: tags: [ "object_store_test_suite", "for_pull_requests" ] exec_timeout_secs: 3600 commands: - - func: "launch remote baas" - vars: - baas_branch: 300efb0604a88f1f36899bee0f42b34826b9b65f - func: "compile" vars: target_to_build: ObjectStoreTests - - func: "wait for remote baas to start" - func: "run tests" vars: test_label: objstore-baas test_executable_name: "realm-object-store-tests" verbose_test_output: true + baas_branch: 300efb0604a88f1f36899bee0f42b34826b9b65f - func: "check branch state" - name: baas-network-tests diff --git a/test/object-store/test_runner.cpp b/test/object-store/test_runner.cpp index dce3b7734ac..7f3c434cfaf 100644 --- a/test/object-store/test_runner.cpp +++ b/test/object-store/test_runner.cpp @@ -138,6 +138,7 @@ class EvergreenReporter : public CumulativeReporterBase { } void testCaseStarting(TestCaseInfo const& testCaseInfo) override { + std::cerr << "Starting test case \"" << testCaseInfo.name << "\"\n"; m_results.emplace(std::make_pair(testCaseInfo.name, TestResult{})); Base::testCaseStarting(testCaseInfo); } @@ -155,6 +156,7 @@ class EvergreenReporter : public CumulativeReporterBase { it->second.status = "fail"; } it->second.end_time = std::chrono::system_clock::now(); + std::cerr << "Ending test case \"" << testCaseStats.testInfo->name << "\"\n"; Base::testCaseEnded(testCaseStats); } void sectionStarting(SectionInfo const& sectionInfo) override @@ -165,6 +167,7 @@ class EvergreenReporter : public CumulativeReporterBase { else { m_pending_name += "::" + sectionInfo.name; } + std::cerr << "Starting test section \"" << m_pending_name << "\"\n"; m_pending_test = {}; Base::sectionStarting(sectionInfo); } @@ -179,6 +182,7 @@ class EvergreenReporter : public CumulativeReporterBase { } m_pending_test.end_time = std::chrono::system_clock::now(); m_results.emplace(std::make_pair(m_pending_name, m_pending_test)); + std::cerr << "Ending test section \"" << m_pending_name << "\"\n"; m_pending_name = ""; } Base::sectionEnded(sectionStats); diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index 0b8a0074bfb..f02ad0c988b 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -268,6 +268,15 @@ size_t curl_header_cb(char* buffer, size_t size, size_t nitems, std::map(total_time)); if (response_code != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed when sending request to '%s' with body '%s': %s\n", request.url.c_str(), request.body.c_str(), curl_easy_strerror(response_code)); @@ -341,6 +354,193 @@ app::Response do_http_request(const app::Request& request) }; } +class Baasaas { +public: + enum class StartMode { Default, GitHash, Branch }; + explicit Baasaas(std::string api_key, StartMode mode, std::string ref_spec) + : m_api_key(std::move(api_key)) + { + 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 { + 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); + } + 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() + { + 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); + } + + 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( + "https://us-east-1.aws.data.mongodb-api.com/app/baas-container-service-autzb/endpoint/%1", 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); + } + + std::string m_api_key; + std::string m_container_id; + 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; + } + + 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.empty()) { + throw std::runtime_error("Excepted BAASAAS_START_MODE to be \"githash\" 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}); + } + + 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 +781,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}}); } @@ -784,6 +984,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(); @@ -1183,9 +1420,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}; @@ -1200,10 +1439,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 fcf3d5e3ab4..00067196d83 100644 --- a/test/object-store/util/sync/baas_admin_api.hpp +++ b/test/object-store/util/sync/baas_admin_api.hpp @@ -283,6 +283,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 43f8bc90612..c9198942e9e 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -214,22 +214,26 @@ static std::string unquote_string(std::string_view possibly_quoted_string) } #ifdef REALM_MONGODB_ENDPOINT -std::string get_base_url() +std::string get_compile_time_base_url() { // allows configuration with or without quotes return unquote_string(REALM_QUOTE(REALM_MONGODB_ENDPOINT)); } - -std::string get_admin_url() +#else +std::string get_compile_time_base_url() +{ + return {}; +} +#endif +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 AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials() { @@ -552,9 +556,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 879c74cb563..ba5354a6b01 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -148,8 +148,8 @@ void subscribe_to_all_and_bootstrap(Realm& realm); #if REALM_ENABLE_AUTH_TESTS #ifdef REALM_MONGODB_ENDPOINT -std::string get_base_url(); -std::string get_admin_url(); +std::string get_compile_time_base_url(); +std::string get_compile_time_admin_url(); #endif From 64737cb32e20e72a6f26ac67b1a265ff9404612f Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 10 Jan 2024 11:56:30 -0500 Subject: [PATCH 02/21] Make redirect tests work against random baas urls --- test/object-store/sync/app.cpp | 313 +++++++++++++++------------------ 1 file changed, 146 insertions(+), 167 deletions(-) diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 87a7e8504df..cb671d1c13f 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2298,10 +2298,9 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { util::UniqueFunction&& completion) override { if (request_hook) { - request_hook(request); - } - if (simulated_response) { - return completion(*simulated_response); + if (auto simulated_response = request_hook(request)) { + return completion(*simulated_response); + } } SynchronousTestTransport::send_request_to_server(request, [&](const Response& response) mutable { if (response_hook) { @@ -2313,9 +2312,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { // Optional handler for the request and response before it is returned to completion std::function response_hook; // Optional handler for the request before it is sent to the server - std::function request_hook; - // Optional Response object to return immediately instead of communicating with the server - std::optional simulated_response; + std::function(const Request&)> request_hook; }; struct HookedSocketProvider : public sync::websocket::DefaultSocketProvider { @@ -2328,20 +2325,25 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { std::unique_ptr connect(std::unique_ptr observer, sync::WebSocketEndpoint&& endpoint) override { - int status_code = 101; - std::string body; - bool use_simulated_response = websocket_connect_func && websocket_connect_func(status_code, body); + auto simulated_response = websocket_connect_simulated_response_func + ? websocket_connect_simulated_response_func() + : std::nullopt; - auto websocket = DefaultSocketProvider::connect(std::move(observer), std::move(endpoint)); - if (use_simulated_response) { + if (websocket_endpoint_resolver) { + endpoint = websocket_endpoint_resolver(std::move(endpoint)); + } + std::unique_ptr websocket = + DefaultSocketProvider::connect(std::move(observer), std::move(endpoint)); + if (simulated_response) { auto default_websocket = static_cast(websocket.get()); if (default_websocket) - default_websocket->force_handshake_response_for_testing(status_code, body); + default_websocket->force_handshake_response_for_testing(*simulated_response, ""); } return websocket; } - std::function websocket_connect_func; + std::function()> websocket_connect_simulated_response_func; + std::function websocket_endpoint_resolver; }; { @@ -2363,19 +2365,19 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { SECTION("Test invalid redirect response") { int request_count = 0; - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { if (request_count == 0) { logger->trace("request.url (%1): %2", request_count, request.url); - redir_transport->simulated_response = { - 301, 0, {{"Content-Type", "application/json"}}, "Some body data"}; - request_count++; + ++request_count; + return Response{301, 0, {{"Content-Type", "application/json"}}, "Some body data"}; } else if (request_count == 1) { logger->trace("request.url (%1): %2", request_count, request.url); - redir_transport->simulated_response = { + return Response{ 301, 0, {{"Location", ""}, {"Content-Type", "application/json"}}, "Some body data"}; - request_count++; } + + return std::nullopt; }; // This will fail due to no Location header @@ -2400,11 +2402,16 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { SECTION("Test redirect response") { int request_count = 0; // redirect URL is localhost or 127.0.0.1 depending on what the initial value is - std::string original_host = "localhost:9090"; + const std::string original_url = get_base_url(); + std::string original_host = original_url.substr(original_url.find("://") + 3); + original_host = original_host.substr(0, original_host.find("/")); + std::string original_ws_host = util::format("ws://%1", original_host); std::string redirect_scheme = "http://"; - std::string redirect_host = "127.0.0.1:9090"; - std::string redirect_url = "http://127.0.0.1:9090"; - redir_transport->request_hook = [&](const Request& request) { + std::string websocket_scheme = "ws://"; + const std::string redirect_host = "fakerealm.example.com:9090"; + const std::string redirect_url = "http://fakerealm.example.com:9090"; + const std::string redirect_ws = "ws://fakerealm.example.com:9090"; + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("Received request[%1]: %2", request_count, request.url); if (request_count == 0) { // First request should be to location @@ -2412,53 +2419,39 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { if (request.url.find("https://") != std::string::npos) { redirect_scheme = "https://"; } - // using local baas - if (request.url.find("127.0.0.1:9090") != std::string::npos) { - redirect_host = "localhost:9090"; - original_host = "127.0.0.1:9090"; - } - // using baas docker - can't test redirect - else if (request.url.find("mongodb-realm:9090") != std::string::npos) { - redirect_host = "mongodb-realm:9090"; - original_host = "mongodb-realm:9090"; - } - - redirect_url = redirect_scheme + redirect_host; logger->trace("redirect_url (%1): %2", request_count, redirect_url); request_count++; } else if (request_count == 1) { logger->trace("request.url (%1): %2", request_count, request.url); REQUIRE(!request.redirect_count); - redir_transport->simulated_response = { - 301, - 0, - {{"Location", "http://somehost:9090"}, {"Content-Type", "application/json"}}, - "Some body data"}; - request_count++; + ++request_count; + return Response{301, + 0, + {{"Location", "http://somehost:9090"}, {"Content-Type", "application/json"}}, + "Some body data"}; } else if (request_count == 2) { logger->trace("request.url (%1): %2", request_count, request.url); REQUIRE(request.url.find("somehost:9090") != std::string::npos); - redir_transport->simulated_response = { + ++request_count; + return Response{ 308, 0, {{"Location", redirect_url}, {"Content-Type", "application/json"}}, "Some body data"}; - request_count++; } else if (request_count == 3) { logger->trace("request.url (%1): %2", request_count, request.url); REQUIRE(request.url.find(redirect_url) != std::string::npos); - redir_transport->simulated_response = { + ++request_count; + return Response{ 301, 0, {{"Location", redirect_scheme + original_host}, {"Content-Type", "application/json"}}, "Some body data"}; - request_count++; } else if (request_count == 4) { logger->trace("request.url (%1): %2", request_count, request.url); REQUIRE(request.url.find(redirect_scheme + original_host) != std::string::npos); // Let the init_app_metadata request go through - redir_transport->simulated_response.reset(); request_count++; } else if (request_count == 5) { @@ -2474,11 +2467,11 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { logger->trace("WS Hostname: %1", app_metadata->ws_hostname); REQUIRE(app_metadata->hostname.find(original_host) != std::string::npos); REQUIRE(request.url.find(redirect_scheme + original_host) != std::string::npos); - redir_transport->simulated_response.reset(); // Validate the retry count tracked in the original message REQUIRE(request.redirect_count == 3); request_count++; } + return std::nullopt; }; // This will be successful after a couple of retries due to the redirect response @@ -2489,15 +2482,14 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { } SECTION("Test too many redirects") { int request_count = 0; - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); REQUIRE(request_count <= 21); - redir_transport->simulated_response = { - request_count % 2 == 1 ? 308 : 301, - 0, - {{"Location", "http://somehost:9090"}, {"Content-Type", "application/json"}}, - "Some body data"}; - request_count++; + ++request_count; + return Response{request_count % 2 == 1 ? 308 : 301, + 0, + {{"Location", "http://somehost:9090"}, {"Content-Type", "application/json"}}, + "Some body data"}; }; redir_app->log_in_with_credentials( @@ -2511,12 +2503,11 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { }); } SECTION("Test server in maintenance") { - redir_transport->request_hook = [&](const Request&) { + redir_transport->request_hook = [&](const Request&) -> std::optional { nlohmann::json maintenance_error = {{"error_code", "MaintenanceInProgress"}, {"error", "This service is currently undergoing maintenance"}, {"link", "https://link.to/server_logs"}}; - redir_transport->simulated_response = { - 500, 0, {{"Content-Type", "application/json"}}, maintenance_error.dump()}; + return Response{500, 0, {{"Content-Type", "application/json"}}, maintenance_error.dump()}; }; redir_app->log_in_with_credentials( @@ -2550,55 +2541,38 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { auto redir_app = app::App::get_uncached_app(app_config, sc_config); int request_count = 0; - // redirect URL is localhost or 127.0.0.1 depending on what the initial value is - std::string original_host = "localhost:9090"; - std::string original_scheme = "http://"; - std::string websocket_url = "ws://some-websocket:9090"; - std::string original_url; - redir_transport->request_hook = [&](const Request& request) { + const std::string original_url = get_base_url(); + std::string original_host = original_url.substr(original_url.find("://") + 3); + original_host = original_host.substr(0, original_host.find("/")); + std::string original_ws_host = util::format("ws://%1", original_host); + const std::string redirect_url = "http://fakerealm.example.com:9090"; + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); - if (request_count == 0) { + if (request_count++ == 0) { // First request should be to location REQUIRE(request.url.find("/location") != std::string::npos); - if (request.url.find("https://") != std::string::npos) { - original_scheme = "https://"; - } - // using local baas - if (request.url.find("127.0.0.1:9090") != std::string::npos) { - original_host = "127.0.0.1:9090"; - } - // using baas docker - else if (request.url.find("mongodb-realm:9090") != std::string::npos) { - original_host = "mongodb-realm:9090"; - } - original_url = original_scheme + original_host; logger->trace("original_url (%1): %2", request_count, original_url); } - else if (request_count == 1) { + else if (request_count++ == 1) { REQUIRE(!request.redirect_count); - redir_transport->simulated_response = { - 308, - 0, - {{"Location", "http://somehost:9090"}, {"Content-Type", "application/json"}}, - "Some body data"}; + return Response{ + 308, 0, {{"Location", redirect_url}, {"Content-Type", "application/json"}}, "Some body data"}; } - else if (request_count == 2) { - REQUIRE(request.url.find("http://somehost:9090") != std::string::npos); + else if (request_count++ == 2) { REQUIRE(request.url.find("location") != std::string::npos); // app hostname will be updated via the metadata info - redir_transport->simulated_response = { + return Response{ static_cast(sync::HTTPStatus::Ok), 0, {{"Content-Type", "application/json"}}, util::format("{\"deployment_model\":\"GLOBAL\",\"location\":\"US-VA\",\"hostname\":\"%1\",\"ws_" "hostname\":\"%2\"}", - original_url, websocket_url)}; + original_url, original_ws_host)}; } else { REQUIRE(request.url.find(original_url) != std::string::npos); - redir_transport->simulated_response.reset(); } - request_count++; + return std::nullopt; }; // This will be successful after a couple of retries due to the redirect response @@ -2607,15 +2581,15 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { REQUIRE(!error); }); REQUIRE(!redir_app->sync_manager()->app_metadata()); // no stored app metadata - REQUIRE(redir_app->sync_manager()->sync_route().find(websocket_url) != std::string::npos); + REQUIRE(redir_app->sync_manager()->sync_route().find(original_ws_host) != std::string::npos); // Register another email address and verify location data isn't requested again request_count = 0; - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); - redir_transport->simulated_response.reset(); REQUIRE(request.url.find("location") == std::string::npos); request_count++; + return std::nullopt; }; redir_app->provider_client().register_email( @@ -2625,39 +2599,35 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { } SECTION("Test websocket redirect with existing session") { - std::string original_host = "localhost:9090"; + std::string configured_app_url = get_base_url(); + std::string original_host = configured_app_url.substr(configured_app_url.find("://") + 3); + original_host = original_host.substr(0, original_host.find("/")); + std::string original_address = original_host; + uint16_t original_port = 443; + if (auto port_pos = original_host.find(":"); port_pos != std::string::npos) { + auto original_port_str = original_host.substr(port_pos + 1); + + original_port = strtol(original_port_str.c_str(), nullptr, 10); + original_address = original_host.substr(0, port_pos); + } + std::string redirect_scheme = "http://"; std::string websocket_scheme = "ws://"; - std::string redirect_host = "127.0.0.1:9090"; - std::string redirect_url = "http://127.0.0.1:9090"; + const std::string redirect_address = "fakerealm.example.com"; + const std::string redirect_host = "fakerealm.example.com:9090"; + const std::string redirect_url = "http://fakerealm.example.com:9090"; auto redir_transport = std::make_shared(); auto redir_provider = std::make_shared(logger, ""); + redir_provider->websocket_endpoint_resolver = [&](sync::WebSocketEndpoint&& ep) { + ep.address = original_address; + ep.port = original_port; + return ep; + }; std::mutex logout_mutex; std::condition_variable logout_cv; bool logged_out = false; - // Use the transport to grab the current url so it can be converted - redir_transport->request_hook = [&](const Request& request) { - if (request.url.find("https://") != std::string::npos) { - redirect_scheme = "https://"; - websocket_scheme = "wss://"; - } - // using local baas - if (request.url.find("127.0.0.1:9090") != std::string::npos) { - redirect_host = "localhost:9090"; - original_host = "127.0.0.1:9090"; - } - // using baas docker - can't test redirect - else if (request.url.find("mongodb-realm:9090") != std::string::npos) { - redirect_host = "mongodb-realm:9090"; - original_host = "mongodb-realm:9090"; - } - - redirect_url = redirect_scheme + redirect_host; - logger->trace("redirect_url: %1", redirect_url); - }; - auto server_app_config = minimal_app_config("websocket_redirect", schema); TestAppSession test_session(create_app(server_app_config), redir_transport, DeleteApp{true}, realm::ReconnectMode::normal, redir_provider); @@ -2688,30 +2658,38 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { sync_session->pause(); int connect_count = 0; - redir_provider->websocket_connect_func = [&connect_count](int& status_code, std::string& body) { + redir_provider->websocket_connect_simulated_response_func = [&connect_count]() -> std::optional { if (connect_count++ > 0) - return false; + return std::nullopt; - status_code = static_cast(sync::HTTPStatus::PermanentRedirect); - body = ""; - return true; + return static_cast(sync::HTTPStatus::PermanentRedirect); + }; + redir_provider->websocket_endpoint_resolver = [&](sync::WebSocketEndpoint&& ep) { + if (connect_count < 2) { + return ep; + } + REQUIRE(ep.address == redirect_address); + ep.address = original_address; + ep.port = original_port; + return ep; }; int request_count = 0; - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); if (request_count++ == 0) { // First request should be a location request against the original URL REQUIRE(request.url.find(original_host) != std::string::npos); REQUIRE(request.url.find("/location") != std::string::npos); REQUIRE(request.redirect_count == 0); - redir_transport->simulated_response = { - static_cast(sync::HTTPStatus::PermanentRedirect), - 0, - {{"Location", redirect_url}, {"Content-Type", "application/json"}}, - "Some body data"}; + return Response{static_cast(sync::HTTPStatus::PermanentRedirect), + 0, + {{"Location", redirect_url}, {"Content-Type", "application/json"}}, + "Some body data"}; } else if (request.url.find("/location") != std::string::npos) { - redir_transport->simulated_response = { + REQUIRE(request.url.find(redirect_host) != std::string::npos); + ++request_count; + return Response{ static_cast(sync::HTTPStatus::Ok), 0, {{"Content-Type", "application/json"}}, @@ -2720,9 +2698,15 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { "hostname\":\"%3%1\"}", redirect_host, redirect_scheme, websocket_scheme)}; } - else { - redir_transport->simulated_response.reset(); + else if (request.url.find(redirect_host) != std::string::npos) { + auto new_req = request; + new_req.url = util::format("%1%2", configured_app_url, request.url.substr(redirect_url.size())); + logger->trace("Proxying request from %1 to %2", request.url, new_req.url); + auto resp = do_http_request(new_req); + logger->trace("Response: \"%1\"", resp.body); + return resp; } + return std::nullopt; }; SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(*sync_manager); @@ -2741,30 +2725,27 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { sync_session->pause(); int connect_count = 0; - redir_provider->websocket_connect_func = [&connect_count](int& status_code, std::string& body) { + redir_provider->websocket_connect_simulated_response_func = [&connect_count]() -> std::optional { if (connect_count++ > 0) - return false; + return std::nullopt; - status_code = static_cast(sync::HTTPStatus::MovedPermanently); - body = ""; - return true; + return static_cast(sync::HTTPStatus::MovedPermanently); }; int request_count = 0; - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); if (request_count++ == 0) { // First request should be a location request against the original URL REQUIRE(request.url.find(original_host) != std::string::npos); REQUIRE(request.url.find("/location") != std::string::npos); REQUIRE(request.redirect_count == 0); - redir_transport->simulated_response = { - static_cast(sync::HTTPStatus::MovedPermanently), - 0, - {{"Location", redirect_url}, {"Content-Type", "application/json"}}, - "Some body data"}; + return Response{static_cast(sync::HTTPStatus::MovedPermanently), + 0, + {{"Location", redirect_url}, {"Content-Type", "application/json"}}, + "Some body data"}; } else if (request.url.find("/location") != std::string::npos) { - redir_transport->simulated_response = { + return Response{ static_cast(sync::HTTPStatus::Ok), 0, {{"Content-Type", "application/json"}}, @@ -2774,14 +2755,12 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { redirect_host, redirect_scheme, websocket_scheme)}; } else if (request.url.find("auth/session") != std::string::npos) { - redir_transport->simulated_response = {static_cast(sync::HTTPStatus::Unauthorized), - 0, - {{"Content-Type", "application/json"}}, - ""}; - } - else { - redir_transport->simulated_response.reset(); + return Response{static_cast(sync::HTTPStatus::Unauthorized), + 0, + {{"Content-Type", "application/json"}}, + ""}; } + return std::nullopt; }; SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(*sync_manager); @@ -2800,17 +2779,15 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { sync_session->pause(); int connect_count = 0; - redir_provider->websocket_connect_func = [&connect_count](int& status_code, std::string& body) { + redir_provider->websocket_connect_simulated_response_func = [&connect_count]() -> std::optional { if (connect_count++ > 0) - return false; + return std::nullopt; - status_code = static_cast(sync::HTTPStatus::MovedPermanently); - body = ""; - return true; + return static_cast(sync::HTTPStatus::MovedPermanently); }; int request_count = 0; const int max_http_redirects = 20; // from app.cpp in object-store - redir_transport->request_hook = [&](const Request& request) { + redir_transport->request_hook = [&](const Request& request) -> std::optional { logger->trace("request.url (%1): %2", request_count, request.url); if (request_count++ == 0) { // First request should be a location request against the original URL @@ -2821,16 +2798,15 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { if (request.url.find("/location") != std::string::npos) { // Keep returning the redirected response REQUIRE(request.redirect_count < max_http_redirects); - redir_transport->simulated_response = { - static_cast(sync::HTTPStatus::MovedPermanently), - 0, - {{"Location", redirect_url}, {"Content-Type", "application/json"}}, - "Some body data"}; + return Response{static_cast(sync::HTTPStatus::MovedPermanently), + 0, + {{"Location", redirect_url}, {"Content-Type", "application/json"}}, + "Some body data"}; } else { - // should not get any other types of requests during the test - the log out is local - REQUIRE(false); + FAIL("should not get any other types of requests during the test - the log out is local"); } + return std::nullopt; }; SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(*sync_manager); @@ -2869,7 +2845,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { // This assumes that we make an http request for the new token while // already in the WaitingForAccessToken state. bool seen_waiting_for_access_token = false; - transport->request_hook = [&](const Request&) { + transport->request_hook = [&](const Request&) -> std::optional { auto user = app->current_user(); REQUIRE(user); for (auto& session : user->all_sessions()) { @@ -2880,7 +2856,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { seen_waiting_for_access_token = true; } } - return true; + return std::nullopt; }; SyncTestFile config(app, partition, schema); auto r = Realm::get_shared_realm(config); @@ -2928,7 +2904,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { // This assumes that we make an http request for the new token while // already in the WaitingForAccessToken state. bool seen_waiting_for_access_token = false; - transport->request_hook = [&](const Request&) { + transport->request_hook = [&](const Request&) -> std::optional { auto user = app->current_user(); REQUIRE(user); for (auto& session : user->all_sessions()) { @@ -2937,6 +2913,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { seen_waiting_for_access_token = true; } } + return std::nullopt; }; SyncTestFile config(app, partition, schema); auto r = Realm::get_shared_realm(config); @@ -2977,8 +2954,9 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { SECTION("User is left logged out if logged out while the refresh is in progress") { REQUIRE(user->is_logged_in()); - transport->request_hook = [&](const Request&) { + transport->request_hook = [&](const Request&) -> std::optional { user->log_out(); + return std::nullopt; }; SyncTestFile config(app, partition, schema); auto r = Realm::get_shared_realm(config); @@ -3003,10 +2981,11 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { response_ref.http_status_code = 500; } }; - transport->request_hook = [&](const Request& request) { + transport->request_hook = [&](const Request& request) -> std::optional { if (!did_receive_valid_token.load() && request.url.find("/session") != std::string::npos) { response_times.push_back(steady_clock::now()); } + return std::nullopt; }; SyncTestFile config(app, partition, schema); auto r = Realm::get_shared_realm(config); From 74cec934fdbe8ebf9b0b2d688c4cdfa7a1349f73 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Mon, 22 Jan 2024 14:26:06 -0500 Subject: [PATCH 03/21] fix merge --- test/object-store/sync/app.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 3ad4b366cdc..05f45c52745 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -3011,7 +3011,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { REQUIRE(!error); }); REQUIRE(redir_app->sync_manager()->sync_route()); - REQUIRE(redir_app->sync_manager()->sync_route()->find(websocket_url) != std::string::npos); + REQUIRE(redir_app->sync_manager()->sync_route()->find(original_ws_host) != std::string::npos); // Register another email address and verify location data isn't requested again request_count = 0; From e41a531a8b2355ed27fd96c552e166d318af85dc Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 24 Jan 2024 10:06:45 -0500 Subject: [PATCH 04/21] log baas coid for admin api requests --- test/object-store/util/sync/baas_admin_api.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index d659a4d8218..ddc5d371e39 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -338,8 +338,17 @@ app::Response do_http_request(const app::Request& request) auto start_time = std::chrono::steady_clock::now(); auto response_code = curl_easy_perform(curl); auto total_time = std::chrono::steady_clock::now() - start_time; - util::format(std::cerr, "Baas API %1 request to %2 took %3\n", app::httpmethod_to_string(request.method), - request.url, std::chrono::duration_cast(total_time)); + + 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); + }(); + + util::format(std::cerr, "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); if (response_code != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed when sending request to '%s' with body '%s': %s\n", request.url.c_str(), request.body.c_str(), curl_easy_strerror(response_code)); From c28d816ce33b45107aec126f756487f6735be719 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Thu, 25 Jan 2024 17:29:43 -0500 Subject: [PATCH 05/21] fix schema migration tests --- .../sync/flx_schema_migration.cpp | 52 ++++++++----------- .../object-store/util/sync/baas_admin_api.cpp | 14 +++++ .../object-store/util/sync/baas_admin_api.hpp | 4 ++ 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/test/object-store/sync/flx_schema_migration.cpp b/test/object-store/sync/flx_schema_migration.cpp index af7049d0e7a..e9412e2136d 100644 --- a/test/object-store/sync/flx_schema_migration.cpp +++ b/test/object-store/sync/flx_schema_migration.cpp @@ -43,30 +43,20 @@ namespace realm::app { namespace { -void create_schema(const AppSession& app_session, std::shared_ptr user, Schema target_schema, - int64_t target_schema_version) +void create_schema(const AppSession& app_session, Schema target_schema, int64_t target_schema_version) { auto create_config = app_session.config; create_config.schema = target_schema; app_session.admin_api.create_schema(app_session.server_app_id, create_config); - auto remote_client = user->mongo_client("BackingDB"); - auto db = remote_client.db("app"); - auto settings = db["schema_history"]; - timed_sleeping_wait_for( [&] { - bson::BsonDocument filter_doc{{"app_id", ObjectId(app_session.server_app_id)}, - {"version_major", target_schema_version}}; - bool found = false; - settings.find_one(filter_doc, - [&](util::Optional document, util::Optional error) { - REQUIRE_FALSE(error); - found = document.has_value(); - }); - return found; + auto versions = app_session.admin_api.get_schema_versions(app_session.server_app_id); + return std::any_of(versions.begin(), versions.end(), [&](const AdminAPISession::SchemaVersionInfo& info) { + return info.version_major == target_schema_version; + }); }, - std::chrono::minutes(5), std::chrono::milliseconds(500)); + std::chrono::minutes(5), std::chrono::seconds(1)); // FIXME: There is a delay on the server between the schema being created and actually ready to use. This is due // to resource pool key cache keys using second precision (BAAS-18361). So we wait for a couple of seconds so the @@ -275,7 +265,7 @@ TEST_CASE("Sync schema migrations don't work with sync open", "[sync][flx][flx s schema_v1[0].persisted_properties.back() = {"non_queryable_field2", PropertyType::String | PropertyType::Nullable}; config.schema = schema_v1; - create_schema(app_session, harness.app()->current_user(), *config.schema, config.schema_version); + create_schema(app_session, *config.schema, config.schema_version); REQUIRE_THROWS_AS(Realm::get_shared_realm(config), InvalidAdditiveSchemaChangeException); check_realm_schema(config.path, schema_v0, 0); @@ -285,7 +275,7 @@ TEST_CASE("Sync schema migrations don't work with sync open", "[sync][flx][flx s // Remove table 'TopLevel2'. schema_v1.pop_back(); config.schema = schema_v1; - create_schema(app_session, harness.app()->current_user(), *config.schema, config.schema_version); + create_schema(app_session, *config.schema, config.schema_version); config.sync_config->on_sync_client_event_hook = [&](std::weak_ptr, const SyncClientHookData& data) mutable { @@ -331,7 +321,7 @@ TEST_CASE("Cannot migrate schema to unknown version", "[sync][flx][flx schema mi } SECTION("Schema versions") { - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); } } @@ -360,7 +350,7 @@ TEST_CASE("Cannot migrate schema to unknown version", "[sync][flx][flx schema mi } SECTION(util::format("Schema versions | Realm schema: %1", schema_version)) { - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); } } @@ -396,7 +386,7 @@ TEST_CASE("Schema version mismatch between client and server", "[sync][flx][flx const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); { auto realm = Realm::get_shared_realm(config); @@ -459,7 +449,7 @@ TEST_CASE("Fresh realm does not require schema migration", "[sync][flx][flx sche const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); config.schema_version = 1; config.schema = schema_v1; @@ -548,9 +538,9 @@ TEST_CASE("Upgrade schema version (with recovery) then downgrade", "[sync][flx][ const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); auto schema_v2 = get_schema_v2(); - create_schema(app_session, harness.app()->current_user(), schema_v2, 2); + create_schema(app_session, schema_v2, 2); // First schema upgrade. { @@ -668,7 +658,7 @@ TEST_CASE("An interrupted schema migration can recover on the next session", const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); config.schema_version = 1; config.schema = schema_v1; @@ -731,7 +721,7 @@ TEST_CASE("Migrate to new schema version with a schema subset", "[sync][flx][flx const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); config.schema_version = 1; auto schema_subset = schema_v1; @@ -777,7 +767,7 @@ TEST_CASE("Client reset during schema migration", "[sync][flx][flx schema migrat const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); config.schema_version = 1; config.schema = schema_v1; @@ -867,9 +857,9 @@ TEST_CASE("Migrate to new schema version after migration to intermediate version const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); auto schema_v2 = get_schema_v2(); - create_schema(app_session, harness.app()->current_user(), schema_v2, 2); + create_schema(app_session, schema_v2, 2); config.schema_version = 1; config.schema = schema_v1; @@ -933,7 +923,7 @@ TEST_CASE("Send schema version zero if no schema is used to open the realm", const AppSession& app_session = harness.session().app_session(); auto schema_v1 = get_schema_v1(); - create_schema(app_session, harness.app()->current_user(), schema_v1, 1); + create_schema(app_session, schema_v1, 1); config.schema = {}; config.schema_version = -1; // override the schema version set by SyncTestFile constructor @@ -987,4 +977,4 @@ TEST_CASE("Allow resetting the schema version to zero after bad schema version e } // namespace realm::app #endif // REALM_ENABLE_AUTH_TESTS -#endif // REALM_ENABLE_SYNC \ No newline at end of file +#endif // REALM_ENABLE_SYNC diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index ddc5d371e39..bc74a4790f4 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -944,6 +944,20 @@ AdminAPISession::ServiceConfig AdminAPISession::set_disable_recovery_to(const st return sync_config; } +std::vector AdminAPISession::get_schema_versions(const std::string& app_id) const +{ + std::vector ret; + auto endpoint = apps()[app_id]["sync"]["schemas"]["versions"]; + auto res = endpoint.get_json(); + for (auto&& version : res["versions"].get>()) { + SchemaVersionInfo info; + info.version_major = version["version_major"]; + ret.push_back(std::move(info)); + } + + return ret; +} + AdminAPISession::ServiceConfig AdminAPISession::get_config(const std::string& app_id, const AdminAPISession::Service& service) const { diff --git a/test/object-store/util/sync/baas_admin_api.hpp b/test/object-store/util/sync/baas_admin_api.hpp index cb0c3c4dc73..faa6cd51623 100644 --- a/test/object-store/util/sync/baas_admin_api.hpp +++ b/test/object-store/util/sync/baas_admin_api.hpp @@ -124,6 +124,10 @@ class AdminAPISession { ServiceConfig sync_config) const; ServiceConfig set_disable_recovery_to(const std::string& app_id, const std::string& service_id, ServiceConfig sync_config, bool disable) const; + struct SchemaVersionInfo { + int64_t version_major; + }; + std::vector get_schema_versions(const std::string& app_id) const; bool is_sync_enabled(const std::string& app_id) const; bool is_sync_terminated(const std::string& app_id) const; bool is_initial_sync_complete(const std::string& app_id) const; From a5381d7319900bfae82313d56eeccdc85d72f863 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 26 Jan 2024 16:04:06 -0500 Subject: [PATCH 06/21] allow starting with a patchid --- test/object-store/util/sync/baas_admin_api.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index bc74a4790f4..d7a272ee321 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -365,7 +365,7 @@ app::Response do_http_request(const app::Request& request) class Baasaas { public: - enum class StartMode { Default, GitHash, Branch }; + 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)) { @@ -379,6 +379,10 @@ class Baasaas { 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"); } @@ -528,9 +532,15 @@ class BaasaasLauncher : public Catch::EventListenerBase { } 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\" or \"branch\""); + throw std::runtime_error("Excepted BAASAAS_START_MODE to be \"githash\", \"patchid\", or \"branch\""); } ref_spec = {}; } From b8b2c240c5228e7b5b3b791a96d21e135a753b0c Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 26 Jan 2024 18:47:28 -0500 Subject: [PATCH 07/21] fix merge --- test/object-store/sync/app.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 05f45c52745..22857c21b30 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2897,7 +2897,6 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { REQUIRE(redir_app->get_host_url().find(original_host) != std::string::npos); REQUIRE(request.url.find(redirect_scheme + original_host) != std::string::npos); // Validate the retry count tracked in the original message - REQUIRE(request.redirect_count == 3); request_count++; } return std::nullopt; From af8424b7f8c704cdf7f724eb8743185745a8d6a3 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 14 Feb 2024 09:14:49 -0500 Subject: [PATCH 08/21] make dependencies.list YAML --- Jenkinsfile | 2 +- dependencies.list | 12 ++++----- evergreen/config.yml | 12 ++++++--- evergreen/install_baas.sh | 4 +-- test/object-store/CMakeLists.txt | 13 +++++----- .../util/sync/sync_test_utils.cpp | 25 +++---------------- .../util/sync/sync_test_utils.hpp | 3 --- tools/cmake/GetVersion.cmake | 10 +++++--- tools/generate-version-numbers-for-soong.sh | 5 ++-- tools/release-init.sh | 2 +- tools/release-tag.sh | 2 +- 11 files changed, 39 insertions(+), 51 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a450d2b919f..b18322916c7 100755 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,7 +28,7 @@ jobWrapper { getSourceArchive() stash includes: '**', name: 'core-source', useDefaultExcludes: false - dependencies = readProperties file: 'dependencies.list' + dependencies = readYaml file: 'dependencies.list' echo "Version in dependencies.list: ${dependencies.VERSION}" gitTag = readGitTag() gitSha = sh(returnStdout: true, script: 'git rev-parse HEAD').trim().take(8) diff --git a/dependencies.list b/dependencies.list index 2ada9057022..f17e392952c 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,7 +1,7 @@ -PACKAGE_NAME=realm-core -VERSION=13.26.0 -OPENSSL_VERSION=3.2.0 -ZLIB_VERSION=1.2.13 +PACKAGE_NAME: realm-core +VERSION: 13.26.0 +OPENSSL_VERSION: 3.2.0 +ZLIB_VERSION: 1.2.13 # https://github.com/10gen/baas/commits -# 5087f is 2024 Jan 13 -BAAS_VERSION=5087ffd5a0e4975e625f0fbcceed23107f611055 +# 81e19 is 2024 Feb 13 +BAAS_VERSION: 81e19dd5cda8888ee9f9a6cee126b76cbc9dd4ed diff --git a/evergreen/config.yml b/evergreen/config.yml index 29a89788f38..5660e7bf100 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 @@ -201,12 +200,17 @@ functions: file: './realm-core/benchmark_results/results.latest.json' "run tests": + - command: expansions.update + params: + file: realm-core/dependencies.list - 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 @@ -235,8 +239,10 @@ functions: TEST_FLAGS="--no-tests=error $TEST_FLAGS ${test_flags|}" - if [[ -z "${baas_branch}" ]]; then + 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 @@ -971,6 +977,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 @@ -986,7 +993,6 @@ tasks: test_label: objstore-baas test_executable_name: "realm-object-store-tests" verbose_test_output: true - baas_branch: 300efb0604a88f1f36899bee0f42b34826b9b65f - func: "check branch state" - name: baas-network-tests diff --git a/evergreen/install_baas.sh b/evergreen/install_baas.sh index 7e636cdfa31..8e51390a4cd 100755 --- a/evergreen/install_baas.sh +++ b/evergreen/install_baas.sh @@ -370,11 +370,11 @@ if [[ -z "${BAAS_VERSION}" ]]; then test_path2="${BASE_PATH}/${dep_file}" if [[ -f "${test_path1}" ]]; then # if this was run locally then check up a directory - get_var_from_file BAAS_VERSION "${test_path1}" + BAAS_VERSION=$(sed -rn 's/^BAAS_VERSION: (.*)/\1/p' < "${test_path1}") elif [[ -f "${test_path2}" ]]; then # if this is run from an evergreen remote host # then the dependencies.list file has been copied over - get_var_from_file BAAS_VERSION "${test_path2}" + BAAS_VERSION=$(sed -rn 's/^BAAS_VERSION: (.*)/\1/p' < "${test_path2}") else echo "could not find '${test_path1}' or '${test_path2}'" ls "${BASE_PATH}/.." diff --git a/test/object-store/CMakeLists.txt b/test/object-store/CMakeLists.txt index aac511f7428..e2732acba95 100644 --- a/test/object-store/CMakeLists.txt +++ b/test/object-store/CMakeLists.txt @@ -130,16 +130,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/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index 43250fe3175..e73fb79705f 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -205,35 +205,16 @@ 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) -{ - 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_compile_time_base_url() { +#ifdef REALM_MONGODB_ENDPOINT // allows configuration with or without quotes return unquote_string(REALM_QUOTE(REALM_MONGODB_ENDPOINT)); -} #else -std::string get_compile_time_base_url() -{ return {}; -} #endif +} + std::string get_compile_time_admin_url() { #ifdef REALM_ADMIN_ENDPOINT diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index 0241d643cfe..40ed0a11b26 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -149,12 +149,9 @@ void subscribe_to_all_and_bootstrap(Realm& realm); void wait_for_sessions_to_close(const TestAppSession& test_app_session); -#ifdef REALM_MONGODB_ENDPOINT std::string get_compile_time_base_url(); std::string get_compile_time_admin_url(); -#endif - struct AutoVerifiedEmailCredentials : app::AppCredentials { AutoVerifiedEmailCredentials(); std::string email; diff --git a/tools/cmake/GetVersion.cmake b/tools/cmake/GetVersion.cmake index e0c5649f5f6..279c5f58b03 100644 --- a/tools/cmake/GetVersion.cmake +++ b/tools/cmake/GetVersion.cmake @@ -1,13 +1,17 @@ file(STRINGS "${RealmCore_SOURCE_DIR}/dependencies.list" DEPENDENCIES) set(VALID_DEPENDENCIES "") foreach(LINE IN LISTS DEPENDENCIES) - string(REGEX MATCHALL "([^=]+)" KEY_VALUE ${LINE}) + string(REGEX MATCHALL "([^:]+)" KEY_VALUE ${LINE}) list(LENGTH KEY_VALUE MATCH_SIZE) if (MATCH_SIZE GREATER_EQUAL 2) list(GET KEY_VALUE 0 KEY) + if ("${KEY}" MATCHES "^#") + continue() + endif() list(GET KEY_VALUE 1 VALUE) - set(DEP_${KEY} ${VALUE}) - set(VALID_DEPENDENCIES "${VALID_DEPENDENCIES} ${LINE}") + string(STRIP "${VALUE}" STRIPPED_VALUE) + set(DEP_${KEY} ${STRIPPED_VALUE}) + set(VALID_DEPENDENCIES "${VALID_DEPENDENCIES} ${KEY}=\"${STRIPPED_VALUE}\"") endif() endforeach() diff --git a/tools/generate-version-numbers-for-soong.sh b/tools/generate-version-numbers-for-soong.sh index 2f24d3ff228..1d53bccb1be 100755 --- a/tools/generate-version-numbers-for-soong.sh +++ b/tools/generate-version-numbers-for-soong.sh @@ -1,8 +1,7 @@ #!/bin/bash -source $1 - -version_and_extra=( ${VERSION//-/ } ) +realm_version=$(sed -rn 's/^VERSION: (.*)/\1/p' < "$1") +version_and_extra=( ${$realm_version//-/ } ) version_only=${version_and_extra[0]} extra=${version_and_extra[1]} diff --git a/tools/release-init.sh b/tools/release-init.sh index d4628678ec2..bb5c348863f 100755 --- a/tools/release-init.sh +++ b/tools/release-init.sh @@ -18,7 +18,7 @@ git push -u origin release/${realm_version} git checkout -b prepare-$realm_version # update dependencies.list -sed -i.bak -e "s/^VERSION.*/VERSION=${realm_version}/" "${project_dir}/dependencies.list" +sed -i.bak -e "s/^VERSION.*/VERSION: ${realm_version}/" "${project_dir}/dependencies.list" rm "${project_dir}/dependencies.list.bak" || exit 1 # update Package.swift diff --git a/tools/release-tag.sh b/tools/release-tag.sh index a76611b451c..1cbc8168be7 100755 --- a/tools/release-tag.sh +++ b/tools/release-tag.sh @@ -11,7 +11,7 @@ if [ $# != 1 ]; then fi project_dir=$(git rev-parse --show-toplevel) -realm_version=$(grep ^VERSION "${project_dir}/dependencies.list" | cut -f 2 -d=) +realm_version=$(sed -rn 's/^VERSION: (.*)/\1/p' < "${project_dir}/dependencies.list") tag=v${realm_version} git tag -m \""$1"\" "${tag}" git push origin "${tag}" From 4a658359a7187c4293edc46160722bf84db3e455 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 14 Feb 2024 17:50:51 -0500 Subject: [PATCH 09/21] remove some debugging lines --- test/object-store/test_runner.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/object-store/test_runner.cpp b/test/object-store/test_runner.cpp index 7f3c434cfaf..dce3b7734ac 100644 --- a/test/object-store/test_runner.cpp +++ b/test/object-store/test_runner.cpp @@ -138,7 +138,6 @@ class EvergreenReporter : public CumulativeReporterBase { } void testCaseStarting(TestCaseInfo const& testCaseInfo) override { - std::cerr << "Starting test case \"" << testCaseInfo.name << "\"\n"; m_results.emplace(std::make_pair(testCaseInfo.name, TestResult{})); Base::testCaseStarting(testCaseInfo); } @@ -156,7 +155,6 @@ class EvergreenReporter : public CumulativeReporterBase { it->second.status = "fail"; } it->second.end_time = std::chrono::system_clock::now(); - std::cerr << "Ending test case \"" << testCaseStats.testInfo->name << "\"\n"; Base::testCaseEnded(testCaseStats); } void sectionStarting(SectionInfo const& sectionInfo) override @@ -167,7 +165,6 @@ class EvergreenReporter : public CumulativeReporterBase { else { m_pending_name += "::" + sectionInfo.name; } - std::cerr << "Starting test section \"" << m_pending_name << "\"\n"; m_pending_test = {}; Base::sectionStarting(sectionInfo); } @@ -182,7 +179,6 @@ class EvergreenReporter : public CumulativeReporterBase { } m_pending_test.end_time = std::chrono::system_clock::now(); m_results.emplace(std::make_pair(m_pending_name, m_pending_test)); - std::cerr << "Ending test section \"" << m_pending_name << "\"\n"; m_pending_name = ""; } Base::sectionEnded(sectionStats); From 2c048cdd04f9af8d88d17b0fa7162237eff2a1a1 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 21 Feb 2024 13:47:38 -0500 Subject: [PATCH 10/21] debugging rhel7 crash --- .../object-store/util/sync/baas_admin_api.cpp | 16 ++++++++-------- .../util/sync/sync_test_utils.cpp | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index d7a272ee321..f9703a8032f 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); } - 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 ... diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index 4b7c91a533a..3d79cef34ff 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -205,6 +205,25 @@ void wait_for_sessions_to_close(const TestAppSession& test_app_session) std::chrono::minutes(5), std::chrono::milliseconds(100)); } +#ifdef REALM_MONGODB_ENDPOINT +static 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}; +} +#endif + std::string get_compile_time_base_url() { #ifdef REALM_MONGODB_ENDPOINT From abd1c15e26e5fd019a759f307e203d951c4754e1 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 21 Feb 2024 20:25:55 -0500 Subject: [PATCH 11/21] temporarily download ca certs package --- evergreen/config.yml | 3 +++ test/object-store/CMakeLists.txt | 6 +++++ .../object-store/util/sync/baas_admin_api.cpp | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/evergreen/config.yml b/evergreen/config.yml index 5660e7bf100..aec1d3972af 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -81,6 +81,9 @@ functions: if [ -n "${baas_admin_port|}" ]; then set_cmake_var baas_vars REALM_ADMIN_ENDPOINT STRING "$scheme://localhost:${baas_admin_port}" fi + + curl -LO https://curl.se/ca/cacert.pem + set_cmake_var baas_vars REALM_CURL_CACERTS PATH $(./evergreen/abspath.sh ./cacert.pem) fi if [ -n "${enable_asan|}" ]; then diff --git a/test/object-store/CMakeLists.txt b/test/object-store/CMakeLists.txt index e2732acba95..7e33297c5ff 100644 --- a/test/object-store/CMakeLists.txt +++ b/test/object-store/CMakeLists.txt @@ -91,6 +91,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) diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index f9703a8032f..62ff74804b6 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -277,6 +277,23 @@ std::string_view getenv_sv(const char* name) noexcept return {}; } +static 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}; +} + } // namespace app::Response do_http_request(const app::Request& request) @@ -335,6 +352,11 @@ app::Response do_http_request(const app::Request& request) curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_cb); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_headers); +#ifdef REALM_CURL_CACERTS + auto ca_info = unquote_string(REALM_QUOTE(REALM_CURL_CACERTS)); + curl_easy_setopt(curl, CURLOPT_CAINFO, ca_info.c_str()); +#endif + auto start_time = std::chrono::steady_clock::now(); auto response_code = curl_easy_perform(curl); auto total_time = std::chrono::steady_clock::now() - start_time; From c8b3b1f62f9a82dc690e59d1cbac403f47263fd5 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Fri, 23 Feb 2024 16:43:24 -0500 Subject: [PATCH 12/21] fix some test failures that were my fault --- dependencies.list | 4 +-- test/object-store/sync/app.cpp | 1 - test/object-store/test_runner.cpp | 14 +++++++++ .../object-store/util/sync/baas_admin_api.cpp | 30 +++++++++++-------- test/object-store/util/test_file.cpp | 5 ---- test/object-store/util/test_file.hpp | 13 -------- 6 files changed, 33 insertions(+), 34 deletions(-) diff --git a/dependencies.list b/dependencies.list index f17e392952c..ccbdd19c202 100644 --- a/dependencies.list +++ b/dependencies.list @@ -3,5 +3,5 @@ VERSION: 13.26.0 OPENSSL_VERSION: 3.2.0 ZLIB_VERSION: 1.2.13 # https://github.com/10gen/baas/commits -# 81e19 is 2024 Feb 13 -BAAS_VERSION: 81e19dd5cda8888ee9f9a6cee126b76cbc9dd4ed +# dd016 is 2024 Feb 22 +BAAS_VERSION: dd01629d83b86292af9c59ebe2a28673c2e559cf diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 098cf23cf67..8989803a9e1 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -3922,7 +3922,6 @@ TEST_CASE("app: base_url", "[sync][app][base_url]") { std::unique_ptr app_session; auto redir_transport = std::make_shared(); AutoVerifiedEmailCredentials creds; - util::Logger::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 dce3b7734ac..2d29fd3a3df 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,17 @@ #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 int run_object_store_tests(int argc, const char** argv); @@ -91,6 +103,8 @@ int run_object_store_tests(int argc, const char** argv) }); #endif + realm::util::Logger::set_default_level_threshold(realm::util::Logger::Level::TEST_LOGGING_LEVEL); + 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 62ff74804b6..f0b637f44da 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -256,7 +256,7 @@ size_t curl_header_cb(char* buffer, size_t size, size_t nitems, std::mapinsert({std::string{key}, std::string{value}}); } @@ -361,20 +361,24 @@ app::Response do_http_request(const app::Request& request) auto response_code = curl_easy_perform(curl); auto total_time = std::chrono::steady_clock::now() - start_time; - 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); - }(); - - util::format(std::cerr, "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); + auto logger = util::Logger::get_default_logger(); if (response_code != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed when sending request to '%s' with body '%s': %s\n", - request.url.c_str(), request.body.c_str(), curl_easy_strerror(response_code)); + logger->error("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 { diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index 3402204ee72..f5a91de395b 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -70,7 +70,6 @@ TestFile::TestFile() disable_sync_to_disk(); m_temp_dir = util::make_temp_dir(); path = (fs::path(m_temp_dir) / "realm.XXXXXX").string(); - util::Logger::set_default_level_threshold(realm::util::Logger::Level::TEST_LOGGING_LEVEL); if (const char* crypt_key = test_util::crypt_key()) { encryption_key = std::vector(crypt_key, crypt_key + 64); } @@ -345,7 +344,6 @@ TestAppSession::TestAppSession(AppSession session, if (!m_transport) m_transport = instance_of; auto app_config = get_config(m_transport, *m_app_session); - util::Logger::set_default_level_threshold(realm::util::Logger::Level::TEST_LOGGING_LEVEL); set_app_config_defaults(app_config, m_transport); util::try_make_dir(m_base_file_path); @@ -434,7 +432,6 @@ TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& , m_should_teardown_test_directory(config.should_teardown_test_directory) { util::try_make_dir(m_base_file_path); - util::Logger::set_default_level_threshold(config.log_level); SyncClientConfig sc_config; sc_config.base_file_path = m_base_file_path; sc_config.metadata_mode = config.metadata_mode; @@ -498,8 +495,6 @@ OfflineAppSession::OfflineAppSession(OfflineAppSession::Config config) sc_config.metadata_mode = config.metadata_mode; sc_config.socket_provider = config.socket_provider; - util::Logger::set_default_level_threshold(realm::util::Logger::Level::TEST_LOGGING_LEVEL); - 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 1c7870559cf..1a802e8ee10 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -98,18 +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 - #if REALM_ENABLE_SYNC using StartImmediately = realm::util::TaggedBool; @@ -190,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; }; From 95de793b694e68be28cd319876905b1313bbdf7a Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 6 Mar 2024 12:20:11 -0500 Subject: [PATCH 13/21] update baas --- dependencies.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.list b/dependencies.list index 5a9e0a7753f..edd0ab37d44 100644 --- a/dependencies.list +++ b/dependencies.list @@ -3,5 +3,5 @@ VERSION=14.1.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 +# dd016 is 2024 Mar 6 +BAAS_VERSION: 678123fcadb2efa1fb6b488a3b5b9d334a9ca75d From 208f5658a82998a6b8ffd56c1bde1cbeeb336463 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 6 Mar 2024 14:32:44 -0500 Subject: [PATCH 14/21] fix bad merge --- evergreen/config.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/evergreen/config.yml b/evergreen/config.yml index 548b1fe4767..1e6c48fee40 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -81,9 +81,6 @@ functions: if [ -n "${baas_admin_port|}" ]; then set_cmake_var baas_vars REALM_ADMIN_ENDPOINT STRING "$scheme://localhost:${baas_admin_port}" fi - - curl -LO https://curl.se/ca/cacert.pem - set_cmake_var baas_vars REALM_CURL_CACERTS PATH $(./evergreen/abspath.sh ./cacert.pem) fi if [ -n "${enable_asan|}" ]; then @@ -212,7 +209,7 @@ functions: "run tests": - command: expansions.update params: - file: realm-core/dependencies.list + file: realm-core/dependencies.yml - command: shell.exec params: working_dir: realm-core From 2e96fc3ae31d06c6a7ab5bd1d7fed11dea15b6bf Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 6 Mar 2024 14:38:19 -0500 Subject: [PATCH 15/21] lint --- test/object-store/test_runner.cpp | 1 - test/object-store/util/test_file.cpp | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test/object-store/test_runner.cpp b/test/object-store/test_runner.cpp index c71636fb63d..59f90dcf7f4 100644 --- a/test/object-store/test_runner.cpp +++ b/test/object-store/test_runner.cpp @@ -110,7 +110,6 @@ static void set_default_level_thresholds() } - int run_object_store_tests(int argc, const char** argv); int run_object_store_tests(int argc, const char** argv) diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index 156fa279f83..048130cf40a 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -419,9 +419,7 @@ std::vector TestAppSession::get_documents(SyncUser& user, co // MARK: - TestSyncManager -TestSyncManager::Config::Config() -{ -} +TestSyncManager::Config::Config() {} TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& sync_server_config) : m_sync_server(sync_server_config) From faae632878351091e14399192cd06454ced7ae83 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Wed, 6 Mar 2024 15:02:55 -0500 Subject: [PATCH 16/21] use correct path for ca bundle --- evergreen/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evergreen/config.yml b/evergreen/config.yml index 1e6c48fee40..2b4831daf87 100644 --- a/evergreen/config.yml +++ b/evergreen/config.yml @@ -155,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} From 2dfe1db4b352dac5fcb77510bf3a6086fe73c183 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Tue, 12 Mar 2024 11:36:15 -0400 Subject: [PATCH 17/21] fixes from PR --- .../object-store/util/sync/baas_admin_api.cpp | 68 +++++++++++++------ .../util/sync/sync_test_utils.cpp | 36 +++++----- .../util/sync/sync_test_utils.hpp | 4 ++ 3 files changed, 70 insertions(+), 38 deletions(-) diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index f0b637f44da..9a41b6586e1 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -277,23 +277,6 @@ std::string_view getenv_sv(const char* name) noexcept return {}; } -static 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}; -} - } // namespace app::Response do_http_request(const app::Request& request) @@ -394,6 +377,8 @@ class Baasaas { 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"; @@ -416,7 +401,20 @@ class Baasaas { 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; @@ -476,6 +474,9 @@ class Baasaas { void stop() { + if (m_externally_managed_instance) { + return; + } auto container_id = std::move(m_container_id); if (container_id.empty()) { return; @@ -484,6 +485,10 @@ class Baasaas { 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() @@ -502,8 +507,8 @@ class Baasaas { nlohmann::json do_request(std::string api_path, app::HttpMethod method) { app::Request request; - request.url = util::format( - "https://us-east-1.aws.data.mongodb-api.com/app/baas-container-service-autzb/endpoint/%1", api_path); + + 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"); @@ -514,8 +519,24 @@ class Baasaas { 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; }; @@ -543,6 +564,15 @@ class BaasaasLauncher : public Catch::EventListenerBase { 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; diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index eda5c0e9a7c..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,25 +222,6 @@ void wait_for_sessions_to_close(const TestAppSession& test_app_session) std::chrono::minutes(5), std::chrono::milliseconds(100)); } -#ifdef REALM_MONGODB_ENDPOINT -static 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}; -} -#endif - std::string get_compile_time_base_url() { #ifdef REALM_MONGODB_ENDPOINT diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index ec40f5f17c2..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 From 0785694f9589e87e6b3aaefc06a89c9ed7c6699b Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Tue, 12 Mar 2024 15:42:53 -0400 Subject: [PATCH 18/21] changelog and bump baas version --- CHANGELOG.md | 3 ++- dependencies.yml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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 From 7f55bcfb2e03aeab3b495b7542bf98a20ab47211 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Tue, 12 Mar 2024 16:12:19 -0400 Subject: [PATCH 19/21] update docs and force create an app that lives forever --- how-to-build.md | 44 +++++++++---------- .../object-store/util/sync/baas_admin_api.cpp | 2 + 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/how-to-build.md b/how-to-build.md index f394c7ddb1e..214bb9f19bb 100644 --- a/how-to-build.md +++ b/how-to-build.md @@ -145,36 +145,36 @@ 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 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. ``` -AWS_ACCESS_KEY_ID = -AWS_SECRET_ACCESS_KEY = +export BAASAAS_API_KEY= +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 ``` +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. Ommitting +these will use the latest available commit from the main branch of baas. -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 +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. -``` -./evergreen/install_baas.sh -w baas -``` +## Running [app] tests against a local BAAS instance -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. +If you already have a baas instance running, you can specify that directly via the +`BAAS_BASE_URL` environment variable. ``` +export BAAS_BASE_URL=http://localhost:9090 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 ``` diff --git a/test/object-store/util/sync/baas_admin_api.cpp b/test/object-store/util/sync/baas_admin_api.cpp index 9a41b6586e1..41dd4337f8d 100644 --- a/test/object-store/util/sync/baas_admin_api.cpp +++ b/test/object-store/util/sync/baas_admin_api.cpp @@ -604,6 +604,8 @@ class BaasaasLauncher : public Catch::EventListenerBase { 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 From a0d375b9cecc8499780a1d0e6f8ab9c0fdc7052e Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Tue, 12 Mar 2024 16:52:58 -0400 Subject: [PATCH 20/21] update docs --- how-to-build.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/how-to-build.md b/how-to-build.md index 214bb9f19bb..124a6811e2c 100644 --- a/how-to-build.md +++ b/how-to-build.md @@ -145,6 +145,19 @@ 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 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). +``` +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 +``` + ## Running [app] tests against an on-demand BAASAAS container Due to MongoDB security policies, running baas requires company issued credentials. @@ -167,17 +180,6 @@ 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. -## 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. -``` -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 -``` ### Developing inside a container From f7aeff057e2145d966a2a5ac66a448d6002d34b7 Mon Sep 17 00:00:00 2001 From: Jonathan Reams Date: Tue, 12 Mar 2024 16:53:55 -0400 Subject: [PATCH 21/21] typo --- how-to-build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to-build.md b/how-to-build.md index 124a6811e2c..92bd693cee7 100644 --- a/how-to-build.md +++ b/how-to-build.md @@ -174,7 +174,7 @@ cmake --build build.sync.ninja --target realm-object-store-tests 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. Ommitting +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