From 5f72d0c4ef4e2ddf6b79a0a7a11db5c55813f7c4 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Thu, 2 May 2024 14:09:41 -0400 Subject: [PATCH 01/10] Updated TestAppSession to remove close/reopen and take a config structure --- test/object-store/c_api/c_api.cpp | 2 +- test/object-store/realm.cpp | 26 +-- test/object-store/sync/app.cpp | 10 +- .../util/sync/flx_sync_harness.hpp | 9 +- .../util/sync/sync_test_utils.cpp | 4 +- .../util/sync/sync_test_utils.hpp | 3 +- test/object-store/util/test_file.cpp | 151 +++++++++++------- test/object-store/util/test_file.hpp | 59 ++++--- 8 files changed, 161 insertions(+), 103 deletions(-) diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 6ec5de1e803..da1d21e2bc6 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -6378,7 +6378,7 @@ TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_a auto user_data = new TestTransportUserData(); auto http_transport = realm_http_transport_new(send_request_to_server, user_data, user_data_free); auto app_session = get_runtime_app_session(); - TestAppSession session(app_session, *http_transport, DeleteApp{false}); + TestAppSession session(app_session, {*http_transport}, DeleteApp{false}); realm_app app(session.app()); SECTION("remove_user integration") { diff --git a/test/object-store/realm.cpp b/test/object-store/realm.cpp index 0ee72b3759d..1c175721799 100644 --- a/test/object-store/realm.cpp +++ b/test/object-store/realm.cpp @@ -1509,16 +1509,22 @@ TEST_CASE("Syhcnronized realm: AutoOpen", "[sync][baas][pbs][async open]") { std::mutex mutex; // Create the app session and get the logged in user identity - auto server_app_config = minimal_app_config("autoopen-realm", schema); - TestAppSession session(create_app(server_app_config), transport, DeleteApp{true}, realm::ReconnectMode::normal, - socket_provider); - auto user = session.app()->current_user(); - std::string identity = user->user_id(); - REQUIRE(user->is_logged_in()); - REQUIRE(!identity.empty()); - // Reopen the App instance and retrieve the cached user - session.reopen(false); - user = session.app()->get_existing_logged_in_user(identity); + auto app_session = create_app(minimal_app_config("autoopen-realm", schema)); + std::string identity; + TestAppSession::Config tas_config; + { + TestAppSession session(app_session, {transport, realm::ReconnectMode::normal, socket_provider, false}, + DeleteApp{false}); + auto user = session.current_user(); + REQUIRE(user); + REQUIRE(user->is_logged_in()); + identity = user->user_id(); + tas_config = session.config(); // get config with storage path and user creds populated + } + REQUIRE_FALSE(identity.empty()); + tas_config.delete_storage = true; + TestAppSession session(app_session, tas_config); + auto user = session.app()->get_existing_logged_in_user(identity); SyncTestFile config(user, partition, schema); config.sync_config->cancel_waits_on_nonfatal_error = true; diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 6326b95c105..d5af820575a 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2158,7 +2158,7 @@ TEST_CASE("app: mixed lists with object links", "[sync][pbs][app][links][baas]") Mixed{target_id}, }; { - TestAppSession test_session(app_session, nullptr, DeleteApp{false}); + TestAppSession test_session(app_session, {}, DeleteApp{false}); SyncTestFile config(test_session.app()->current_user(), partition, schema); auto realm = Realm::get_shared_realm(config); @@ -2222,7 +2222,7 @@ TEST_CASE("app: roundtrip values", "[sync][pbs][app][baas]") { Decimal128 large_significand = Decimal128(70) / Decimal128(1.09); auto obj_id = ObjectId::gen(); { - TestAppSession test_session(app_session, nullptr, DeleteApp{false}); + TestAppSession test_session(app_session, {}, DeleteApp{false}); SyncTestFile config(test_session.app()->current_user(), partition, schema); auto realm = Realm::get_shared_realm(config); @@ -2646,7 +2646,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { } auto transport = std::make_shared>(); - TestAppSession hooked_session(session.app_session(), transport, DeleteApp{false}); + TestAppSession hooked_session(session.app_session(), {transport}, DeleteApp{false}); auto app = hooked_session.app(); std::shared_ptr user = app->current_user(); REQUIRE(user); @@ -2704,7 +2704,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { } auto transport = std::make_shared>(); - TestAppSession hooked_session(session.app_session(), transport, DeleteApp{false}); + TestAppSession hooked_session(session.app_session(), {transport}, DeleteApp{false}); auto app = hooked_session.app(); std::shared_ptr user = app->current_user(); REQUIRE(user); @@ -4408,7 +4408,7 @@ TEST_CASE("app: full-text compatible with sync", "[sync][app][baas]") { auto server_app_config = minimal_app_config("full_text", schema); auto app_session = create_app(server_app_config); const auto partition = random_string(100); - TestAppSession test_session(app_session, nullptr); + TestAppSession test_session(app_session); SyncTestFile config(test_session.app()->current_user(), partition, schema); SharedRealm realm; SECTION("sync open") { diff --git a/test/object-store/util/sync/flx_sync_harness.hpp b/test/object-store/util/sync/flx_sync_harness.hpp index 7d08c861616..27c3dab6c8b 100644 --- a/test/object-store/util/sync/flx_sync_harness.hpp +++ b/test/object-store/util/sync/flx_sync_harness.hpp @@ -84,16 +84,17 @@ class FLXSyncTestHarness { }; explicit FLXSyncTestHarness(Config&& config) - : m_test_session(make_app_from_server_schema(config.test_name, config.server_schema), config.transport, true, - config.reconnect_mode, config.custom_socket_provider) + : m_test_session(make_app_from_server_schema(config.test_name, config.server_schema), + {config.transport, config.reconnect_mode, config.custom_socket_provider}, DeleteApp{true}) , m_schema(std::move(config.server_schema.schema)) { } FLXSyncTestHarness(const std::string& test_name, ServerSchema server_schema = default_server_schema(), std::shared_ptr transport = instance_of, std::shared_ptr custom_socket_provider = nullptr) - : m_test_session(make_app_from_server_schema(test_name, server_schema), std::move(transport), true, - realm::ReconnectMode::normal, custom_socket_provider) + : m_test_session(make_app_from_server_schema(test_name, server_schema), + {std::move(transport), realm::ReconnectMode::normal, custom_socket_provider}, + DeleteApp{true}) , m_schema(std::move(server_schema.schema)) { } diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index f65ea2e4302..9e67c272c01 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -224,10 +224,10 @@ std::string get_compile_time_admin_url() #endif // REALM_ENABLE_SYNC #if REALM_APP_SERVICES -AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials() +AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials(std::string_view prefix) { // emails with this prefix will pass through the baas app due to the register function - email = util::format("realm_tests_do_autoverify%1@%2.com", random_string(10), random_string(10)); + email = util::format("%1%2@%3.com", prefix, random_string(10), random_string(10)); password = random_string(10); static_cast(*this) = AppCredentials::username_password(email, password); } diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index 76333d66e41..b4e29b8a9f0 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -143,7 +143,8 @@ void subscribe_to_all_and_bootstrap(Realm& realm); #if REALM_APP_SERVICES struct AutoVerifiedEmailCredentials : app::AppCredentials { - AutoVerifiedEmailCredentials(); + // Prefix can be set to a custom string for the test (e.g. client_app_id) + AutoVerifiedEmailCredentials(std::string_view prefix = "realm_tests_do_autoverify"); std::string email; std::string password; }; diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index ef2496be9e5..b2e7ffdaca6 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -328,105 +328,136 @@ void set_app_config_defaults(app::AppConfig& app_config, #if REALM_ENABLE_AUTH_TESTS +TestAppSession::Config::Config(std::shared_ptr transport, + realm::ReconnectMode reconnect_mode, + std::shared_ptr socket_provider, bool delete_storage, + std::optional storage_path) + : storage_path(storage_path) + , delete_storage(delete_storage) + , reconnect_mode(reconnect_mode) + , transport(transport) + , socket_provider(socket_provider) +{ +} + TestAppSession::TestAppSession() - : TestAppSession(get_runtime_app_session(), nullptr, DeleteApp{false}) + // Don't delete the global runtime app session + : TestAppSession(get_runtime_app_session(), {}, DeleteApp{false}) +{ +} + +TestAppSession::TestAppSession(AppSession session) + : TestAppSession(session, {}, DeleteApp{true}) { } -TestAppSession::TestAppSession(AppSession session, - std::shared_ptr custom_transport, - DeleteApp delete_app, ReconnectMode reconnect_mode, - std::shared_ptr custom_socket_provider) +TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp delete_app) : m_app_session(std::make_unique(session)) - , m_base_file_path(util::make_temp_dir() + random_string(10)) + , m_config(config) , m_delete_app(delete_app) - , m_transport(custom_transport) { - if (!m_transport) - m_transport = instance_of; - app_config = get_config(m_transport, *m_app_session); - set_app_config_defaults(app_config, m_transport); - app_config.base_file_path = m_base_file_path; - app_config.metadata_mode = realm::app::AppConfig::MetadataMode::NoEncryption; + if (!m_config.storage_path || m_config.storage_path->empty()) { + m_config.storage_path.emplace(util::make_temp_dir() + random_string(10)); + } + REQUIRE(m_config.storage_path); + util::try_make_dir(*m_config.storage_path); - util::try_make_dir(m_base_file_path); - app_config.sync_client_config.reconnect_mode = reconnect_mode; - app_config.sync_client_config.socket_provider = custom_socket_provider; + if (!m_config.transport) { + m_config.transport = instance_of; + } + realm::app::AppConfig app_config = get_config(m_config.transport, *m_app_session); + set_app_config_defaults(app_config, m_config.transport); + // If a base URL was provided, set it in the app config + if (m_config.base_url) { + app_config.base_url = *m_config.base_url; + } + app_config.base_file_path = *m_config.storage_path; + app_config.metadata_mode = m_config.metadata_mode; + app_config.sync_client_config.reconnect_mode = m_config.reconnect_mode; // With multiplexing enabled, the linger time controls how long a // connection is kept open for reuse. In tests, we want to shut // down sync clients immediately. app_config.sync_client_config.timeouts.connection_linger_time = 0; - + app_config.sync_client_config.socket_provider = m_config.socket_provider; m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config); // initialize sync client m_app->sync_manager()->get_sync_client(); - user_creds = create_user_and_log_in(m_app); + // If no user creds are supplied, then create the user and log in + if (!m_config.user_creds) { + auto creds_user = create_user_and_log_in(); + m_config.user_creds = creds_user.first; + } + // If creds are supplied, it is up to the caller to log in separately } TestAppSession::~TestAppSession() { - if (util::File::exists(m_base_file_path)) { + if (m_app) { + m_app->sync_manager()->tear_down_for_testing(); + } + app::App::clear_cached_apps(); + // If the app session is being deleted or the config tells us to, delete the storage path + if ((m_delete_app || m_config.delete_storage) && util::File::exists(*m_config.storage_path)) { try { - m_app->sync_manager()->tear_down_for_testing(); - util::try_remove_dir_recursive(m_base_file_path); + util::try_remove_dir_recursive(*m_config.storage_path); } catch (const std::exception& ex) { - std::cerr << ex.what() << "\n"; + std::cerr << "Error tearing down TestAppSession(" << m_app_session->config.app_name << "): " << ex.what() + << "\n"; } - app::App::clear_cached_apps(); } if (m_delete_app) { + REQUIRE(m_app_session); m_app_session->admin_api.delete_app(m_app_session->server_app_id); } } -void TestAppSession::close(bool tear_down) +std::pair> TestAppSession::create_user_and_log_in() { - try { - if (tear_down) { - // If tearing down, make sure there's an app to work with - if (!m_app) { - reopen(false); + REQUIRE(m_app); + AutoVerifiedEmailCredentials creds(m_app_session->config.app_name); + auto pf = util::make_promise_future>(); + m_app->provider_client().register_email( + creds.email, creds.password, + [this, &creds, + promise = util::CopyablePromiseHolder>(std::move(pf.promise))]( + util::Optional error) mutable { + if (error) { + promise.get_promise().set_error(error->to_status()); + return; } - REALM_ASSERT(m_app); - // Clean up the app data - m_app->sync_manager()->tear_down_for_testing(); - } - else if (m_app) { - // Otherwise, make sure all the session are closed - m_app->sync_manager()->close_all_sessions(); - } - m_app.reset(); - - // If tearing down, clean up the test file directory - if (tear_down && !m_base_file_path.empty() && util::File::exists(m_base_file_path)) { - util::try_remove_dir_recursive(m_base_file_path); - m_base_file_path.clear(); - } - } - catch (const std::exception& ex) { - std::cerr << "Error tearing down TestAppSession: " << ex.what() << "\n"; + promise.get_promise().emplace_value(log_in_user(creds)); + }); + auto result = pf.future.get_no_throw(); + if (!result.is_ok()) { + FAIL(util::format("Failed to log in: %1", result.get_status())); } - // Ensure all cached apps are cleared - app::App::clear_cached_apps(); + return std::make_pair(creds, result.get_value()); } -void TestAppSession::reopen(bool log_in) +std::shared_ptr TestAppSession::log_in_user(std::optional user_creds) { - REALM_ASSERT(!m_base_file_path.empty()); - if (m_app) { - close(false); - } - m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config); - - // initialize sync client - m_app->sync_manager()->get_sync_client(); - if (log_in) { - log_in_user(m_app, user_creds); + REQUIRE(m_app); + REQUIRE((user_creds || m_config.user_creds)); + auto pf = util::make_promise_future>(); + m_app->log_in_with_credentials( + *user_creds, [promise = util::CopyablePromiseHolder>(std::move(pf.promise))]( + std::shared_ptr user, util::Optional error) mutable { + if (error) { + promise.get_promise().set_error(error->to_status()); + return; + } + promise.get_promise().emplace_value(user); + }); + auto result = pf.future.get_no_throw(); + if (!result.is_ok()) { + FAIL(util::format("Failed to log in: %1", result.get_status())); } + return result.get_value(); } + std::vector TestAppSession::get_documents(app::User& user, const std::string& object_type, size_t expected_count) const { diff --git a/test/object-store/util/test_file.hpp b/test/object-store/util/test_file.hpp index 93c80b13d91..54d4a4b3e3b 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -323,47 +323,66 @@ class OfflineAppSession { using DeleteApp = realm::util::TaggedBool; class TestAppSession { public: + struct Config { + Config(std::shared_ptr transport = nullptr, + realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal, + std::shared_ptr socket_provider = nullptr, + bool delete_storage = false, std::optional storage_path = std::nullopt); + std::optional user_creds; + std::optional storage_path; + bool delete_storage = true; + std::optional base_url; + realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal; + realm::app::AppConfig::MetadataMode metadata_mode = realm::app::AppConfig::MetadataMode::NoEncryption; + std::shared_ptr transport; + std::shared_ptr socket_provider; + }; + TestAppSession(); - TestAppSession(realm::AppSession, std::shared_ptr = nullptr, - DeleteApp = true, realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal, - std::shared_ptr custom_socket_provider = nullptr); + TestAppSession(realm::AppSession); + TestAppSession(realm::AppSession, Config, DeleteApp = true); + ~TestAppSession(); std::shared_ptr app() const noexcept { return m_app; } + const std::shared_ptr& sync_manager() const + { + REQUIRE(m_app); + return m_app->sync_manager(); + } + + std::shared_ptr current_user() const + { + REQUIRE(m_app); + return m_app->current_user(); + } + std::pair> create_user_and_log_in(); + std::shared_ptr log_in_user(std::optional user_creds = std::nullopt); + const realm::AppSession& app_session() const noexcept { return *m_app_session; } - realm::app::GenericNetworkTransport* transport() + const Config& config() const noexcept { - return m_transport.get(); + return m_config; } - const std::shared_ptr& sync_manager() const + realm::app::GenericNetworkTransport* transport() { - REALM_ASSERT(m_app); - return m_app->sync_manager(); + return m_config.transport.get(); } - // Close the app instance (or tear down the TestAppSession) - void close(bool tear_down = false); - // Re-open the app instance using app_config - void reopen(bool log_in = false); - - realm::app::AppConfig app_config; - std::vector get_documents(realm::app::User& user, const std::string& object_type, size_t expected_count) const; private: - std::shared_ptr m_app; std::unique_ptr m_app_session; - std::string m_base_file_path; - bool m_delete_app = true; - std::shared_ptr m_transport; - realm::app::AppCredentials user_creds; + Config m_config; + bool m_delete_app; + std::shared_ptr m_app; }; #endif // REALM_ENABLE_AUTH_TESTS From 3a64207deca96056ed71e005a9e6d89367ba4de9 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Thu, 2 May 2024 15:06:40 -0400 Subject: [PATCH 02/10] Updated changelog and minor tweaks --- CHANGELOG.md | 2 +- test/object-store/sync/app.cpp | 2 +- test/object-store/util/test_file.cpp | 4 +--- test/object-store/util/test_file.hpp | 4 ---- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a08001dc0b1..497764f94f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ ----------- ### Internals -* None. +* Update TestAppSession to take a config object and be restarted. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index d5af820575a..d12e36294de 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2895,7 +2895,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { promise.get_promise().emplace_value(std::move(error)); }; - auto transport = static_cast(session.transport()); + auto transport = static_cast(session.config().transport.get()); transport->block(); // don't let the token refresh happen until we're ready for it auto r = Realm::get_shared_realm(config); auto session = app->sync_manager()->get_existing_session(config.path); diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index b2e7ffdaca6..9d0e7d423f3 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -407,8 +407,7 @@ TestAppSession::~TestAppSession() << "\n"; } } - if (m_delete_app) { - REQUIRE(m_app_session); + if (m_delete_app && m_app_session) { m_app_session->admin_api.delete_app(m_app_session->server_app_id); } } @@ -457,7 +456,6 @@ std::shared_ptr TestAppSession::log_in_user(std::optional TestAppSession::get_documents(app::User& user, const std::string& object_type, size_t expected_count) const { diff --git a/test/object-store/util/test_file.hpp b/test/object-store/util/test_file.hpp index 54d4a4b3e3b..7dfb2809c81 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -370,10 +370,6 @@ class TestAppSession { { return m_config; } - realm::app::GenericNetworkTransport* transport() - { - return m_config.transport.get(); - } std::vector get_documents(realm::app::User& user, const std::string& object_type, size_t expected_count) const; From ee3cff2b9eb6f1f13c4a14986a9807aa379a0673 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Fri, 3 May 2024 09:32:32 -0400 Subject: [PATCH 03/10] AutoVerify email address matters... --- test/object-store/util/sync/sync_test_utils.cpp | 4 ++-- test/object-store/util/sync/sync_test_utils.hpp | 2 +- test/object-store/util/test_file.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index 9e67c272c01..9894782533f 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -224,10 +224,10 @@ std::string get_compile_time_admin_url() #endif // REALM_ENABLE_SYNC #if REALM_APP_SERVICES -AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials(std::string_view prefix) +AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials() { // emails with this prefix will pass through the baas app due to the register function - email = util::format("%1%2@%3.com", prefix, random_string(10), random_string(10)); + email = util::format("realm_tests_do_autoverify-%1%2@%3.com", prefix, random_string(10), random_string(10)); password = random_string(10); static_cast(*this) = AppCredentials::username_password(email, password); } diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index b4e29b8a9f0..a147732440e 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -144,7 +144,7 @@ void subscribe_to_all_and_bootstrap(Realm& realm); #if REALM_APP_SERVICES struct AutoVerifiedEmailCredentials : app::AppCredentials { // Prefix can be set to a custom string for the test (e.g. client_app_id) - AutoVerifiedEmailCredentials(std::string_view prefix = "realm_tests_do_autoverify"); + AutoVerifiedEmailCredentials(); std::string email; std::string password; }; diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index 9d0e7d423f3..ebde6e5aaad 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -415,7 +415,7 @@ TestAppSession::~TestAppSession() std::pair> TestAppSession::create_user_and_log_in() { REQUIRE(m_app); - AutoVerifiedEmailCredentials creds(m_app_session->config.app_name); + AutoVerifiedEmailCredentials creds; auto pf = util::make_promise_future>(); m_app->provider_client().register_email( creds.email, creds.password, From bffec15df2ce96ac59e5e93e6fdc402d8f27e45a Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Fri, 3 May 2024 10:01:13 -0400 Subject: [PATCH 04/10] Forgot to adjust format string --- test/object-store/util/sync/sync_test_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index 9894782533f..a70750c22da 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -227,7 +227,7 @@ std::string get_compile_time_admin_url() AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials() { // emails with this prefix will pass through the baas app due to the register function - email = util::format("realm_tests_do_autoverify-%1%2@%3.com", prefix, random_string(10), random_string(10)); + email = util::format("realm_tests_do_autoverify-%1@%2.com", random_string(10), random_string(10)); password = random_string(10); static_cast(*this) = AppCredentials::username_password(email, password); } From e5d63913fec76f541a5957e9e6a4976e67927b93 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Fri, 3 May 2024 11:21:57 -0400 Subject: [PATCH 05/10] Updated REQUIRE's in TestAppSession to REALM_ASSERTs due to thread sanitizer --- test/object-store/util/test_file.cpp | 14 ++++++++------ test/object-store/util/test_file.hpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index ebde6e5aaad..c58be141ae0 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -359,7 +359,7 @@ TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp dele if (!m_config.storage_path || m_config.storage_path->empty()) { m_config.storage_path.emplace(util::make_temp_dir() + random_string(10)); } - REQUIRE(m_config.storage_path); + REALM_ASSERT(m_config.storage_path); util::try_make_dir(*m_config.storage_path); if (!m_config.transport) { @@ -395,6 +395,7 @@ TestAppSession::~TestAppSession() { if (m_app) { m_app->sync_manager()->tear_down_for_testing(); + m_app.reset(); } app::App::clear_cached_apps(); // If the app session is being deleted or the config tells us to, delete the storage path @@ -414,7 +415,7 @@ TestAppSession::~TestAppSession() std::pair> TestAppSession::create_user_and_log_in() { - REQUIRE(m_app); + REALM_ASSERT(m_app); AutoVerifiedEmailCredentials creds; auto pf = util::make_promise_future>(); m_app->provider_client().register_email( @@ -430,15 +431,16 @@ std::pair> TestAppS }); auto result = pf.future.get_no_throw(); if (!result.is_ok()) { - FAIL(util::format("Failed to log in: %1", result.get_status())); + throw RuntimeError(result.get_status().code(), + util::format("Failed to create user: %1", result.get_status())); } return std::make_pair(creds, result.get_value()); } std::shared_ptr TestAppSession::log_in_user(std::optional user_creds) { - REQUIRE(m_app); - REQUIRE((user_creds || m_config.user_creds)); + REALM_ASSERT(m_app); + REALM_ASSERT((user_creds || m_config.user_creds)); auto pf = util::make_promise_future>(); m_app->log_in_with_credentials( *user_creds, [promise = util::CopyablePromiseHolder>(std::move(pf.promise))]( @@ -451,7 +453,7 @@ std::shared_ptr TestAppSession::log_in_user(std::optional& sync_manager() const { - REQUIRE(m_app); + REALM_ASSERT(m_app); return m_app->sync_manager(); } std::shared_ptr current_user() const { - REQUIRE(m_app); + REALM_ASSERT(m_app); return m_app->current_user(); } std::pair> create_user_and_log_in(); From 631bb043d9d0fd277634cff5b697c0ce3f3939c9 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Thu, 30 May 2024 13:33:28 -0400 Subject: [PATCH 06/10] Some minor cleanup --- test/object-store/sync/app.cpp | 2 +- .../util/sync/sync_test_utils.cpp | 2 +- .../util/sync/sync_test_utils.hpp | 1 - test/object-store/util/test_file.cpp | 33 ++++++++++--------- test/object-store/util/test_file.hpp | 31 +++++++++-------- 5 files changed, 37 insertions(+), 32 deletions(-) diff --git a/test/object-store/sync/app.cpp b/test/object-store/sync/app.cpp index 508ede629ef..d8a7929346d 100644 --- a/test/object-store/sync/app.cpp +++ b/test/object-store/sync/app.cpp @@ -2895,7 +2895,7 @@ TEST_CASE("app: sync integration", "[sync][pbs][app][baas]") { promise.get_promise().emplace_value(std::move(error)); }; - auto transport = static_cast(session.config().transport.get()); + auto transport = static_cast(session.transport()); transport->block(); // don't let the token refresh happen until we're ready for it auto r = Realm::get_shared_realm(config); auto session = app->sync_manager()->get_existing_session(config.path); diff --git a/test/object-store/util/sync/sync_test_utils.cpp b/test/object-store/util/sync/sync_test_utils.cpp index a70750c22da..f65ea2e4302 100644 --- a/test/object-store/util/sync/sync_test_utils.cpp +++ b/test/object-store/util/sync/sync_test_utils.cpp @@ -227,7 +227,7 @@ std::string get_compile_time_admin_url() AutoVerifiedEmailCredentials::AutoVerifiedEmailCredentials() { // emails with this prefix will pass through the baas app due to the register function - email = util::format("realm_tests_do_autoverify-%1@%2.com", random_string(10), random_string(10)); + email = util::format("realm_tests_do_autoverify%1@%2.com", random_string(10), random_string(10)); password = random_string(10); static_cast(*this) = AppCredentials::username_password(email, password); } diff --git a/test/object-store/util/sync/sync_test_utils.hpp b/test/object-store/util/sync/sync_test_utils.hpp index a147732440e..76333d66e41 100644 --- a/test/object-store/util/sync/sync_test_utils.hpp +++ b/test/object-store/util/sync/sync_test_utils.hpp @@ -143,7 +143,6 @@ void subscribe_to_all_and_bootstrap(Realm& realm); #if REALM_APP_SERVICES struct AutoVerifiedEmailCredentials : app::AppCredentials { - // Prefix can be set to a custom string for the test (e.g. client_app_id) AutoVerifiedEmailCredentials(); std::string email; std::string password; diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index c58be141ae0..95b4848a8b8 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -385,8 +385,9 @@ TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp dele m_app->sync_manager()->get_sync_client(); // If no user creds are supplied, then create the user and log in if (!m_config.user_creds) { - auto creds_user = create_user_and_log_in(); - m_config.user_creds = creds_user.first; + auto result = create_user_and_log_in(); + REALM_ASSERT(result.is_ok()); + m_config.user_creds = result.get_value(); } // If creds are supplied, it is up to the caller to log in separately } @@ -413,31 +414,35 @@ TestAppSession::~TestAppSession() } } -std::pair> TestAppSession::create_user_and_log_in() +StatusWith TestAppSession::create_user_and_log_in() { REALM_ASSERT(m_app); AutoVerifiedEmailCredentials creds; - auto pf = util::make_promise_future>(); + auto pf = util::make_promise_future(); m_app->provider_client().register_email( creds.email, creds.password, - [this, &creds, - promise = util::CopyablePromiseHolder>(std::move(pf.promise))]( + [this, &creds, promise = util::CopyablePromiseHolder(std::move(pf.promise))]( util::Optional error) mutable { if (error) { promise.get_promise().set_error(error->to_status()); return; } - promise.get_promise().emplace_value(log_in_user(creds)); + auto result = log_in_user(creds); + if (!result.is_ok()) { + promise.get_promise().set_error(result.get_status()); + return; + } + promise.get_promise().emplace_value(); }); auto result = pf.future.get_no_throw(); if (!result.is_ok()) { - throw RuntimeError(result.get_status().code(), - util::format("Failed to create user: %1", result.get_status())); + return result; } - return std::make_pair(creds, result.get_value()); + return creds; } -std::shared_ptr TestAppSession::log_in_user(std::optional user_creds) +StatusWith> +TestAppSession::log_in_user(std::optional user_creds) { REALM_ASSERT(m_app); REALM_ASSERT((user_creds || m_config.user_creds)); @@ -451,11 +456,7 @@ std::shared_ptr TestAppSession::log_in_user(std::optional TestAppSession::get_documents(app::User& user, const std::string& object_type, diff --git a/test/object-store/util/test_file.hpp b/test/object-store/util/test_file.hpp index c3226f837ff..70c50a581a8 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -326,8 +326,8 @@ class TestAppSession { struct Config { Config(std::shared_ptr transport = nullptr, realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal, - std::shared_ptr socket_provider = nullptr, - bool delete_storage = false, std::optional storage_path = std::nullopt); + std::shared_ptr socket_provider = nullptr, bool delete_storage = true, + std::optional storage_path = std::nullopt); std::optional user_creds; std::optional storage_path; bool delete_storage = true; @@ -344,10 +344,22 @@ class TestAppSession { ~TestAppSession(); + const Config& config() const noexcept + { + return m_config; + } std::shared_ptr app() const noexcept { return m_app; } + const realm::AppSession& app_session() const noexcept + { + return *m_app_session; + } + realm::app::GenericNetworkTransport* transport() + { + return m_config.transport.get(); + } const std::shared_ptr& sync_manager() const { REALM_ASSERT(m_app); @@ -359,17 +371,10 @@ class TestAppSession { REALM_ASSERT(m_app); return m_app->current_user(); } - std::pair> create_user_and_log_in(); - std::shared_ptr log_in_user(std::optional user_creds = std::nullopt); - - const realm::AppSession& app_session() const noexcept - { - return *m_app_session; - } - const Config& config() const noexcept - { - return m_config; - } + realm::StatusWith create_user_and_log_in(); + // The user_creds from the Config structure will be used if not provided + realm::StatusWith> + log_in_user(std::optional user_creds = std::nullopt); std::vector get_documents(realm::app::User& user, const std::string& object_type, size_t expected_count) const; From 4a2d00c7ff8f3c0daa3335c25c9462edfd4da288 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Thu, 30 May 2024 14:35:32 -0400 Subject: [PATCH 07/10] a little more cleanup --- test/object-store/realm.cpp | 6 +++--- test/object-store/util/test_file.cpp | 17 +++-------------- test/object-store/util/test_file.hpp | 19 ++++++++----------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/test/object-store/realm.cpp b/test/object-store/realm.cpp index 1c175721799..01673e58bb8 100644 --- a/test/object-store/realm.cpp +++ b/test/object-store/realm.cpp @@ -1513,8 +1513,9 @@ TEST_CASE("Syhcnronized realm: AutoOpen", "[sync][baas][pbs][async open]") { std::string identity; TestAppSession::Config tas_config; { - TestAppSession session(app_session, {transport, realm::ReconnectMode::normal, socket_provider, false}, - DeleteApp{false}); + // Keep the app and realm storage + TestAppSession session(app_session, {transport, realm::ReconnectMode::normal, socket_provider}, + DeleteApp{false}, false); auto user = session.current_user(); REQUIRE(user); REQUIRE(user->is_logged_in()); @@ -1522,7 +1523,6 @@ TEST_CASE("Syhcnronized realm: AutoOpen", "[sync][baas][pbs][async open]") { tas_config = session.config(); // get config with storage path and user creds populated } REQUIRE_FALSE(identity.empty()); - tas_config.delete_storage = true; TestAppSession session(app_session, tas_config); auto user = session.app()->get_existing_logged_in_user(identity); diff --git a/test/object-store/util/test_file.cpp b/test/object-store/util/test_file.cpp index 95b4848a8b8..91e8a356a1a 100644 --- a/test/object-store/util/test_file.cpp +++ b/test/object-store/util/test_file.cpp @@ -328,18 +328,6 @@ void set_app_config_defaults(app::AppConfig& app_config, #if REALM_ENABLE_AUTH_TESTS -TestAppSession::Config::Config(std::shared_ptr transport, - realm::ReconnectMode reconnect_mode, - std::shared_ptr socket_provider, bool delete_storage, - std::optional storage_path) - : storage_path(storage_path) - , delete_storage(delete_storage) - , reconnect_mode(reconnect_mode) - , transport(transport) - , socket_provider(socket_provider) -{ -} - TestAppSession::TestAppSession() // Don't delete the global runtime app session : TestAppSession(get_runtime_app_session(), {}, DeleteApp{false}) @@ -351,10 +339,11 @@ TestAppSession::TestAppSession(AppSession session) { } -TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp delete_app) +TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp delete_app, bool delete_storage) : m_app_session(std::make_unique(session)) , m_config(config) , m_delete_app(delete_app) + , m_delete_storage(delete_storage) { if (!m_config.storage_path || m_config.storage_path->empty()) { m_config.storage_path.emplace(util::make_temp_dir() + random_string(10)); @@ -400,7 +389,7 @@ TestAppSession::~TestAppSession() } app::App::clear_cached_apps(); // If the app session is being deleted or the config tells us to, delete the storage path - if ((m_delete_app || m_config.delete_storage) && util::File::exists(*m_config.storage_path)) { + if ((m_delete_app || m_delete_storage) && util::File::exists(*m_config.storage_path)) { try { util::try_remove_dir_recursive(*m_config.storage_path); } diff --git a/test/object-store/util/test_file.hpp b/test/object-store/util/test_file.hpp index 70c50a581a8..e516e7e3d35 100644 --- a/test/object-store/util/test_file.hpp +++ b/test/object-store/util/test_file.hpp @@ -324,23 +324,19 @@ using DeleteApp = realm::util::TaggedBool; class TestAppSession { public: struct Config { - Config(std::shared_ptr transport = nullptr, - realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal, - std::shared_ptr socket_provider = nullptr, bool delete_storage = true, - std::optional storage_path = std::nullopt); - std::optional user_creds; - std::optional storage_path; - bool delete_storage = true; - std::optional base_url; - realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal; - realm::app::AppConfig::MetadataMode metadata_mode = realm::app::AppConfig::MetadataMode::NoEncryption; std::shared_ptr transport; + realm::ReconnectMode reconnect_mode = realm::ReconnectMode::normal; std::shared_ptr socket_provider; + realm::app::AppConfig::MetadataMode metadata_mode = realm::app::AppConfig::MetadataMode::NoEncryption; + std::optional base_url; + std::optional storage_path; + // If user_creds are supplied, caller must explicitly call log_in_user() after TestAppSession creation + std::optional user_creds; }; TestAppSession(); TestAppSession(realm::AppSession); - TestAppSession(realm::AppSession, Config, DeleteApp = true); + TestAppSession(realm::AppSession, Config, DeleteApp = true, bool delete_storage = true); ~TestAppSession(); @@ -383,6 +379,7 @@ class TestAppSession { std::unique_ptr m_app_session; Config m_config; bool m_delete_app; + bool m_delete_storage; std::shared_ptr m_app; }; #endif // REALM_ENABLE_AUTH_TESTS From 03b45b6f446a49f07f04397026d44c691d1349d7 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Mon, 3 Jun 2024 09:03:41 -0400 Subject: [PATCH 08/10] Updated changelog after release --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5b22fe5dd..fd49b49e7ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ ----------- ### Internals -* None. +* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- @@ -44,7 +44,6 @@ * Refactor `sync::Session` to eliminate the bind() step of session creation ([#7609](https://github.com/realm/realm-core/pull/7609)). * Add ScopeExitFail which only calls the handler if exiting the scope via an uncaught exception ([#7609](https://github.com/realm/realm-core/pull/7609)). * Add the originating error and server requests action that caused a client reset to occur to the client reset tracking metadata storage. ([PR #7649](https://github.com/realm/realm-core/pull/7649)) -* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- From 4a0ac6c235f207524e975b82f113ebd504ca94f1 Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Fri, 14 Jun 2024 20:27:23 -0400 Subject: [PATCH 09/10] Updated changelog after release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 465ef2c3334..8934a50236a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ### Internals * Switch to building the Swift package and Cocoa binaries as C++20 ([PR #7802](https://github.com/realm/realm-core/pull/7802)). +* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- @@ -52,7 +53,6 @@ * Added back iOS simulator testing to evergreen after Jenkins went away ([PR #7758](https://github.com/realm/realm-core/pull/7758)). * `realm-trawler -c` did not work on Realm using SyncClient history ([PR #7734](https://github.com/realm/realm-core/pull/7734)). * `File::Map`'s move constructor and assignment operator left `m_fd` unchanged, which appears to have never actually resulted in problems with how it was used ([PR #7698](https://github.com/realm/realm-core/pull/7698)). -* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- From 21eb8a30c1d1adb4d71d348e0b7ed903d5ac83ec Mon Sep 17 00:00:00 2001 From: Michael Wilkerson-Barker Date: Tue, 2 Jul 2024 18:29:23 -0400 Subject: [PATCH 10/10] Updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b364f399a9..0b4a55074e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ ### Internals * Fixed `Table::remove_object_recursive` which wouldn't recursively follow links through a single `Mixed` property. This feature is exposed publicly on `Table` but no SDK currently uses it, so this is considered internal. ([#7829](https://github.com/realm/realm-core/issues/7829), likely since the introduction of Mixed) * Upload completion is now tracked in a multiprocess-compatible manner ([PR #7796](https://github.com/realm/realm-core/pull/7796)). +* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ---------------------------------------------- @@ -72,7 +73,6 @@ ### Internals * Switch to building the Swift package and Cocoa binaries as C++20 ([PR #7802](https://github.com/realm/realm-core/pull/7802)). -* Update TestAppSession to allow scope-based usage for restarting the local app resources. ([PR #7672](https://github.com/realm/realm-core/pull/7672)) ----------------------------------------------