From 941d1fb7132b281addb43a257a95a080d59e0b64 Mon Sep 17 00:00:00 2001 From: Christian Melchior Date: Fri, 22 Sep 2023 09:35:28 +0200 Subject: [PATCH] Cleanup resources better during Client Reset and closing Realms. (#1515) --- CHANGELOG.md | 6 + dependencies.list | 6 +- .../kotlin/internal/interop/RealmInterop.kt | 2 + .../src/main/jni/realm_api_helpers.cpp | 2 +- .../io/realm/kotlin/internal/RealmImpl.kt | 35 +++-- .../ClientResetRequiredException.kt | 5 +- .../mongodb/internal/SyncConfigurationImpl.kt | 33 +++- .../mongodb/internal/SyncSessionImpl.kt | 4 + .../mongodb/internal/SyncedRealmContext.kt | 17 ++- .../kotlin/test/platform/PlatformUtils.kt | 11 +- .../io/realm/kotlin/test/common/RealmTests.kt | 8 + packages/test-sync/build.gradle.kts | 24 +-- .../mongodb/common/AsymmetricSyncTests.kt | 13 +- .../common/FlexibleSyncIntegrationTests.kt | 24 ++- .../common/MutableSubscriptionSetTests.kt | 4 +- .../mongodb/common/ProgressListenerTests.kt | 6 +- .../kotlin/test/mongodb/common/Schema.kt | 49 ++++++ .../common/SubscriptionExtensionsTests.kt | 9 +- .../mongodb/common/SubscriptionSetTests.kt | 2 +- .../test/mongodb/common/SubscriptionTests.kt | 2 +- .../common/SyncClientResetIntegrationTests.kt | 143 ++++++++++-------- .../test/mongodb/common/SyncClientTests.kt | 8 +- .../test/mongodb/common/SyncConfigTests.kt | 28 ++-- .../test/mongodb/common/SyncSessionTests.kt | 13 +- .../test/mongodb/common/SyncedRealmTests.kt | 142 +++++------------ .../mongodb/common/nonlatin/NonLatinTests.kt | 3 +- 26 files changed, 328 insertions(+), 271 deletions(-) create mode 100644 packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/Schema.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a9db0a72..c990aa834e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,14 @@ * Realm will no longer set the JVM bytecode to 1.8 when applying the Realm plugin. ([#1513](https://github.com/realm/realm-kotlin/issues/1513)) ### Fixed +* `Realm.close()` is now idempotent. * Fix error in `RealmAny.equals` that would sometimes return `true` when comparing RealmAnys wrapping same type but different values. (Issue [#1523](https://github.com/realm/realm-kotlin/pull/1523)) * [Sync] If calling a function on App Services that resulted in a redirect, it would only redirect for GET requests. (Issue [#1517](https://github.com/realm/realm-kotlin/pull/1517)) +* [Sync] Manual client reset on Windows would not trigger correctly when run inside `onManualResetFallback`. (Issue [#1515](https://github.com/realm/realm-kotlin/pull/1515)) +* [Sync] `ClientResetRequiredException.executeClientReset()` now returns a boolean indicating if the manual reset fully succeded or not. (Issue [#1515](https://github.com/realm/realm-kotlin/pull/1515)) +* [Sync] If calling a function on App Services that resulted in a redirect, it would only redirect for +GET requests. (Issue [#1517](https://github.com/realm/realm-kotlin/pull/1517)) +* [Sync] If calling a function on App Services that resulted in a redirect, it would only redirect for GET requests. (Issue [#1517](https://github.com/realm/realm-kotlin/pull/1517)) ### Compatibility * File format: Generates Realms with file format v23. diff --git a/dependencies.list b/dependencies.list index 19abf07b21..ba8ab7b56a 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,11 +1,11 @@ # Version of MongoDB Realm used by integration tests # See https://github.com/realm/ci/packages/147854 for available versions -MONGODB_REALM_SERVER=2023-09-07 +MONGODB_REALM_SERVER=2023-09-22 # `BAAS` and `BAAS-UI` projects commit hashes matching MONGODB_REALM_SERVER image version # note that the MONGODB_REALM_SERVER image is a nightly build, find the matching commits # for that date within the following repositories: # https://github.com/10gen/baas/ # https://github.com/10gen/baas-ui/ -REALM_BAAS_GIT_HASH=fc070ea7e874f58eb5b4f7a9d344fa7cdd755817 -REALM_BAAS_UI_GIT_HASH=ca722f5bb7a7485404a68874991ba8e50787ea9a +REALM_BAAS_GIT_HASH=0b7562d0401d72c909369030dc29332542614ba3 +REALM_BAAS_UI_GIT_HASH=24baee4eb0e9736969a00a7bfac849565bca17f4 diff --git a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt index 4418168802..46facf20d2 100644 --- a/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt +++ b/packages/cinterop/src/nativeDarwin/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt @@ -2544,6 +2544,8 @@ actual object RealmInterop { } catch (e: Throwable) { println(e.message) false + } finally { + realm_wrapper.realm_close(afterRealmPtr) } }, StableRef.create(afterHandler).asCPointer(), diff --git a/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp b/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp index 392e1a7797..98e52f6890 100644 --- a/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp +++ b/packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp @@ -906,7 +906,7 @@ after_client_reset(void* userdata, realm_t* before_realm, realm_t* after_realm_ptr = realm_from_thread_safe_reference(after_realm, &scheduler); auto after_pointer = wrap_pointer(env, reinterpret_cast(after_realm_ptr), false); env->CallVoidMethod(static_cast(userdata), java_after_callback_function, before_pointer, after_pointer, did_recover); - + realm_close(after_realm_ptr); if (env->ExceptionCheck()) { std::string exception_message = get_exception_message(env); std::string message_template = "An error has occurred in the 'onAfter' callback: "; diff --git a/packages/library-base/src/commonMain/kotlin/io/realm/kotlin/internal/RealmImpl.kt b/packages/library-base/src/commonMain/kotlin/io/realm/kotlin/internal/RealmImpl.kt index 64d6ed3eaf..b96ea5cd0b 100644 --- a/packages/library-base/src/commonMain/kotlin/io/realm/kotlin/internal/RealmImpl.kt +++ b/packages/library-base/src/commonMain/kotlin/io/realm/kotlin/internal/RealmImpl.kt @@ -46,7 +46,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlin.reflect.KClass @@ -56,8 +55,6 @@ public class RealmImpl private constructor( configuration: InternalConfiguration, ) : BaseRealmImpl(configuration), Realm, InternalTypedRealm, Flowable> { - private val realmPointerMutex = Mutex() - public val notificationScheduler: LiveRealmContext = configuration.notificationDispatcherFactory.createLiveRealmContext() @@ -85,6 +82,7 @@ public class RealmImpl private constructor( private var _realmReference: AtomicRef = atomic(null) private val realmReferenceLock = SynchronizableObject() + private val isClosed = atomic(false) /** * The current Realm reference that points to the underlying frozen C++ SharedRealm. @@ -103,7 +101,8 @@ public class RealmImpl private constructor( // Injection point for synchronized Realms. This property should only be used to hold state // required by synchronized realms. See `SyncedRealmContext` for more details. - public var syncContext: AtomicRef = atomic(null) + @OptIn(ExperimentalStdlibApi::class) + public var syncContext: AtomicRef = atomic(null) init { @Suppress("TooGenericExceptionCaught") @@ -261,27 +260,39 @@ public class RealmImpl private constructor( return VersionInfo(mainVersions, notifier.versions(), writer.versions()) } + override fun isClosed(): Boolean { + // We cannot rely on `realmReference()` here. If something happens during open, this might + // not be available and will throw, so we need to track closed state separately. + return isClosed.value + } + override fun close() { // TODO Reconsider this constraint. We have the primitives to check is we are on the // writer thread and just close the realm in writer.close() writer.checkInTransaction("Cannot close the Realm while inside a transaction block") - runBlocking { - realmPointerMutex.withLock { + realmReferenceLock.withLock { + if (isClosed()) { + return + } + isClosed.value = true + runBlocking { writer.close() realmScope.cancel() notifier.close() versionTracker.close() + @OptIn(ExperimentalStdlibApi::class) + syncContext.value?.close() // The local realmReference is pointing to a realm reference managed by either the // version tracker, writer or notifier, so it is already closed super.close() } - } - if (!realmStateFlow.tryEmit(State.CLOSED)) { - log.warn("Cannot signal internal close") - } + if (!realmStateFlow.tryEmit(State.CLOSED)) { + log.warn("Cannot signal internal close") + } - notificationScheduler.close() - writeScheduler.close() + notificationScheduler.close() + writeScheduler.close() + } } internal companion object { diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ClientResetRequiredException.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ClientResetRequiredException.kt index 697100dc81..26b2daa2e4 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ClientResetRequiredException.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/exceptions/ClientResetRequiredException.kt @@ -56,9 +56,10 @@ public class ClientResetRequiredException constructor( * associated to the session in which this error is generated **must be closed**. Not doing so * might result in unexpected file system errors. * + * @return `true` if the Client Reset succeeded, `false` if not. * @throws IllegalStateException if not all instances have been closed. */ - public fun executeClientReset() { - RealmInterop.realm_sync_immediately_run_file_actions(appPointer, originalFilePath) + public fun executeClientReset(): Boolean { + return RealmInterop.realm_sync_immediately_run_file_actions(appPointer, originalFilePath) } } diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncConfigurationImpl.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncConfigurationImpl.kt index 3664d5d968..23d689a20d 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncConfigurationImpl.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncConfigurationImpl.kt @@ -36,6 +36,7 @@ import io.realm.kotlin.internal.interop.SyncErrorCallback import io.realm.kotlin.internal.interop.sync.SyncError import io.realm.kotlin.internal.interop.sync.SyncSessionResyncMode import io.realm.kotlin.internal.platform.fileExists +import io.realm.kotlin.log.RealmLog import io.realm.kotlin.mongodb.exceptions.ClientResetRequiredException import io.realm.kotlin.mongodb.exceptions.DownloadingRealmTimeOutException import io.realm.kotlin.mongodb.subscriptions @@ -52,8 +53,11 @@ import io.realm.kotlin.mongodb.sync.SyncSession import kotlinx.atomicfu.AtomicBoolean import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.atomic +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.mongodb.kbson.BsonValue @@ -194,10 +198,33 @@ internal class SyncConfigurationImpl( SyncErrorCallback { pointer: RealmSyncSessionPointer, error: SyncError -> val session = SyncSessionImpl(pointer) val syncError = convertSyncError(error) - - // Notify before/after callbacks too if error is client reset if (error.isClientResetRequested) { - initializerHelper.onSyncError(session, frozenAppPointer, error) + // If a Client Reset happened, we only get here if `onManualResetFallback` needs + // to be called. This means there is a high likelihood that users will want to + // call ClientResetRequiredException.executeClientReset() inside the callback. + // + // In order to do that, they will need to close the Realm first. + // + // On POSIX this will work fine, but on Windows this will fail as the + // C++ session still holds a DBPointer preventing the release of the file during + // the callback. + // + // So, in order to prevent errors on Windows, we are running the Kotlin callback + // on a separate worker thread. This will allow Core to finish its callback so + // when we close the Realm from the worker thread, the underlying + // session can also be fully freed. + // + // Given that we do not make any promises regarding which thread the callback + // is running on. This should be fine. + @OptIn(DelicateCoroutinesApi::class) + try { + GlobalScope.launch { + initializerHelper.onSyncError(session, frozenAppPointer, error) + } + } catch (ex: Exception) { + @Suppress("invisible_member") + RealmLog.error("Error thrown and ignored in `onManualResetFallback`: $ex") + } } else { userErrorHandler.onError(session, syncError) } diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncSessionImpl.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncSessionImpl.kt index 69acf48137..6ce5e305c6 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncSessionImpl.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncSessionImpl.kt @@ -247,6 +247,10 @@ internal open class SyncSessionImpl( } } + fun close() { + nativePointer.release() + } + internal companion object { internal fun stateFrom(coreState: CoreSyncSessionState): SyncSession.State { return when (coreState) { diff --git a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncedRealmContext.kt b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncedRealmContext.kt index 04f914e697..95c505b1f3 100644 --- a/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncedRealmContext.kt +++ b/packages/library-sync/src/commonMain/kotlin/io/realm/kotlin/mongodb/internal/SyncedRealmContext.kt @@ -32,7 +32,8 @@ import io.realm.kotlin.mongodb.sync.SyncSession * In order to work around the bootstrap problem, all public API entry points that access this * class must do so through the [executeInSyncContext] closure. */ -internal class SyncedRealmContext(realm: T) { +@OptIn(ExperimentalStdlibApi::class) +internal class SyncedRealmContext(realm: T) : AutoCloseable { // TODO For now this can only be a RealmImpl, which is required by the SyncSessionImpl // When we introduce a public DynamicRealm, this can also be a `DynamicRealmImpl` // And we probably need to modify the SyncSessionImpl to take either of these two. @@ -40,18 +41,27 @@ internal class SyncedRealmContext(realm: T) { internal val config: SyncConfiguration = baseRealm.configuration as SyncConfiguration // Note: Session and Subscriptions only need a valid dbPointer when being created, after that, they // have their own lifecycle and can be cached. - internal val session: SyncSession by lazy { + private val sessionDelegate: Lazy = lazy { SyncSessionImpl( baseRealm, RealmInterop.realm_sync_session_get(baseRealm.realmReference.dbPointer) ) } - internal val subscriptions: SubscriptionSet by lazy { + internal val session: SyncSession by sessionDelegate + + private val subscriptionsDelegate: Lazy> = lazy { SubscriptionSetImpl( realm, RealmInterop.realm_sync_get_latest_subscriptionset(baseRealm.realmReference.dbPointer) ) } + internal val subscriptions: SubscriptionSet by subscriptionsDelegate + + override fun close() { + if (sessionDelegate.isInitialized()) { + (session as SyncSessionImpl).close() + } + } } /** @@ -77,6 +87,7 @@ internal fun executeInSyncContext(realm: R, block: (context: } } +@OptIn(ExperimentalStdlibApi::class) private fun initSyncContextIfNeeded(realm: T): SyncedRealmContext { // INVARIANT: `syncContext` is only ever set once, and never to `null`. // This code works around the fact that `Mutex`'s can only be locked inside suspend functions on diff --git a/packages/test-base/src/androidMain/kotlin/io/realm/kotlin/test/platform/PlatformUtils.kt b/packages/test-base/src/androidMain/kotlin/io/realm/kotlin/test/platform/PlatformUtils.kt index 2b693a5fa9..cfc7d1f33f 100644 --- a/packages/test-base/src/androidMain/kotlin/io/realm/kotlin/test/platform/PlatformUtils.kt +++ b/packages/test-base/src/androidMain/kotlin/io/realm/kotlin/test/platform/PlatformUtils.kt @@ -21,7 +21,6 @@ import android.os.SystemClock import java.io.File import java.nio.file.Files import java.nio.file.Path -import java.nio.file.attribute.PosixFilePermission import kotlin.io.path.absolutePathString import kotlin.time.Duration @@ -30,14 +29,8 @@ actual object PlatformUtils { actual fun createTempDir(prefix: String, readOnly: Boolean): String { val dir: Path = Files.createTempDirectory("$prefix-android_tests") if (readOnly) { - Files.setPosixFilePermissions( - dir, - setOf( - PosixFilePermission.GROUP_READ, - PosixFilePermission.OTHERS_READ, - PosixFilePermission.OWNER_READ - ) - ) + // Use the File API as it works across Windows and POSIX. + dir.toFile().setReadOnly() } return dir.absolutePathString() } diff --git a/packages/test-base/src/commonTest/kotlin/io/realm/kotlin/test/common/RealmTests.kt b/packages/test-base/src/commonTest/kotlin/io/realm/kotlin/test/common/RealmTests.kt index 624353f975..a398bc409e 100644 --- a/packages/test-base/src/commonTest/kotlin/io/realm/kotlin/test/common/RealmTests.kt +++ b/packages/test-base/src/commonTest/kotlin/io/realm/kotlin/test/common/RealmTests.kt @@ -429,6 +429,14 @@ class RealmTests { } } + @Test + fun close_idempotent() { + realm.close() + assertTrue(realm.isClosed()) + realm.close() + assertTrue(realm.isClosed()) + } + @Test @Suppress("LongMethod") fun deleteRealm() { diff --git a/packages/test-sync/build.gradle.kts b/packages/test-sync/build.gradle.kts index 72ce21f2c9..d04f38661f 100644 --- a/packages/test-sync/build.gradle.kts +++ b/packages/test-sync/build.gradle.kts @@ -286,27 +286,33 @@ kotlin { // - 'syncTestUrl` defines the root URL for the App Services server. Default is `http://localhost:9090` // - 'syncTestAppNamePrefix' is added a differentiator for all apps created by tests. This makes // it possible for builds in parallel to run against the same test server. Default is `test-app`. +fun getPropertyValue(propertyName: String): String? { + if (project.hasProperty(propertyName)) { + return project.property(propertyName) as String + } + return System.getenv(propertyName) +} buildkonfig { packageName = "io.realm.kotlin.test.mongodb" objectName = "SyncServerConfig" defaultConfigs { - buildConfigField(Type.STRING, "url", properties["syncTestUrl"]!! as String) - buildConfigField(Type.STRING, "appPrefix", properties["syncTestAppNamePrefix"]!! as String) - if (properties.containsKey("syncTestLoginEmail") && properties.containsKey("syncTestLoginPassword")) { - buildConfigField(Type.STRING, "email", properties["syncTestLoginEmail"]!! as String) - buildConfigField(Type.STRING, "password", properties["syncTestLoginPassword"]!! as String) + buildConfigField(Type.STRING, "url", getPropertyValue("syncTestUrl")) + buildConfigField(Type.STRING, "appPrefix", getPropertyValue("syncTestAppNamePrefix")) + if (project.hasProperty("syncTestLoginEmail") && project.hasProperty("syncTestLoginPassword")) { + buildConfigField(Type.STRING, "email", getPropertyValue("syncTestLoginEmail")) + buildConfigField(Type.STRING, "password", getPropertyValue("syncTestLoginPassword")) } else { buildConfigField(Type.STRING, "email", "") buildConfigField(Type.STRING, "password", "") } - if (properties.containsKey("syncTestLoginPublicApiKey") && properties.containsKey("syncTestLoginPrivateApiKey")) { - buildConfigField(Type.STRING, "publicApiKey", properties["syncTestLoginPublicApiKey"]!! as String) - buildConfigField(Type.STRING, "privateApiKey", properties["syncTestLoginPrivateApiKey"]!! as String) + if (project.hasProperty("syncTestLoginPublicApiKey") && project.hasProperty("syncTestLoginPrivateApiKey")) { + buildConfigField(Type.STRING, "publicApiKey", getPropertyValue("syncTestLoginPublicApiKey")) + buildConfigField(Type.STRING, "privateApiKey", getPropertyValue("syncTestLoginPrivateApiKey")) } else { buildConfigField(Type.STRING, "publicApiKey", "") buildConfigField(Type.STRING, "privateApiKey", "") } - buildConfigField(Type.STRING, "clusterName", properties["syncTestClusterName"] as String? ?: "") + buildConfigField(Type.STRING, "clusterName", getPropertyValue("syncTestClusterName") ?: "") } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AsymmetricSyncTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AsymmetricSyncTests.kt index 7d2f05821f..642278458c 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AsymmetricSyncTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/AsymmetricSyncTests.kt @@ -104,12 +104,7 @@ class AsymmetricSyncTests { } config = SyncConfiguration.Builder( user, - schema = setOf( - Measurement::class, - Device::class, - BackupDevice::class, - DeviceParent::class - ) + schema = FLX_SYNC_SCHEMA ).initialSubscriptions { it.query().subscribe() }.build() @@ -299,11 +294,7 @@ class AsymmetricSyncTests { fun asymmetricSchema() = runBlocking { config = SyncConfiguration.Builder( app.login(Credentials.anonymous()), - schema = setOf( - AsymmetricA::class, - EmbeddedB::class, - StandardC::class, - ) + schema = FLX_SYNC_SCHEMA ).build() Realm.open(config).use { it.write { diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FlexibleSyncIntegrationTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FlexibleSyncIntegrationTests.kt index f6597162ec..f3093f2e6a 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FlexibleSyncIntegrationTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/FlexibleSyncIntegrationTests.kt @@ -22,7 +22,6 @@ import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject import io.realm.kotlin.entities.sync.flx.FlexParentObject import io.realm.kotlin.ext.query import io.realm.kotlin.internal.platform.runBlocking -import io.realm.kotlin.log.LogLevel import io.realm.kotlin.mongodb.exceptions.CompensatingWriteException import io.realm.kotlin.mongodb.exceptions.DownloadingRealmTimeOutException import io.realm.kotlin.mongodb.exceptions.SyncException @@ -59,12 +58,11 @@ import kotlin.time.Duration.Companion.seconds */ class FlexibleSyncIntegrationTests { - private val defaultSchema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class) private lateinit var app: TestApp @BeforeTest fun setup() { - app = TestApp(this::class.simpleName, appName = TEST_APP_FLEX, logLevel = LogLevel.ALL) + app = TestApp(this::class.simpleName, appName = TEST_APP_FLEX) val (email, password) = TestHelper.randomEmail() to "password1234" runBlocking { app.createUserAndLogIn(email, password) @@ -84,7 +82,7 @@ class FlexibleSyncIntegrationTests { // Upload data from user 1 val user1 = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config1 = SyncConfiguration.create(user1, defaultSchema) + val config1 = SyncConfiguration.create(user1, FLX_SYNC_SCHEMA) Realm.open(config1).use { realm1 -> val subs = realm1.subscriptions.update { add(realm1.query("section = $0", randomSection)) @@ -99,7 +97,7 @@ class FlexibleSyncIntegrationTests { // Download data from user 2 val user2 = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config2 = SyncConfiguration.Builder(user2, defaultSchema) + val config2 = SyncConfiguration.Builder(user2, FLX_SYNC_SCHEMA) .initialSubscriptions { realm -> add( realm.query( @@ -120,7 +118,7 @@ class FlexibleSyncIntegrationTests { @Test fun writeFailsIfNoSubscription() = runBlocking { val user = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config = SyncConfiguration.Builder(user, defaultSchema) + val config = SyncConfiguration.Builder(user, FLX_SYNC_SCHEMA) .build() Realm.open(config).use { realm -> @@ -138,7 +136,7 @@ class FlexibleSyncIntegrationTests { val randomSection = Random.nextInt() // Generate random section to allow replays of unit tests val user = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config = SyncConfiguration.Builder(user, defaultSchema).build() + val config = SyncConfiguration.Builder(user, FLX_SYNC_SCHEMA).build() Realm.open(config).use { realm -> realm.subscriptions.update { val query = realm.query() @@ -163,7 +161,7 @@ class FlexibleSyncIntegrationTests { @Test fun initialSubscriptions_timeOut() { - val config = SyncConfiguration.Builder(app.currentUser!!, defaultSchema) + val config = SyncConfiguration.Builder(app.currentUser!!, FLX_SYNC_SCHEMA) .initialSubscriptions { realm -> repeat(10) { add(realm.query("section = $0", it)) @@ -186,7 +184,7 @@ class FlexibleSyncIntegrationTests { // Prepare some user data val user1 = app.createUserAndLogin() - val config1 = SyncConfiguration.create(user1, defaultSchema) + val config1 = SyncConfiguration.create(user1, FLX_SYNC_SCHEMA) Realm.open(config1).use { realm -> realm.subscriptions.update { add(realm.query("section = $0", randomSection)) @@ -208,7 +206,7 @@ class FlexibleSyncIntegrationTests { // User 2 opens a Realm twice val counter = atomic(0) val user2 = app.createUserAndLogin() - val config2 = SyncConfiguration.Builder(user2, defaultSchema) + val config2 = SyncConfiguration.Builder(user2, FLX_SYNC_SCHEMA) .initialSubscriptions(rerunOnOpen = true) { realm -> add( realm.query( @@ -236,7 +234,7 @@ class FlexibleSyncIntegrationTests { // Upload data from user 1 val user1 = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config1 = SyncConfiguration.create(user1, defaultSchema) + val config1 = SyncConfiguration.create(user1, FLX_SYNC_SCHEMA) Realm.open(config1).use { realm1 -> val subs = realm1.subscriptions.update { add(realm1.query("section = $0", randomSection)) @@ -274,7 +272,7 @@ class FlexibleSyncIntegrationTests { // Download data from user 2 val user2 = app.createUserAndLogIn(TestHelper.randomEmail(), "123456") - val config2 = SyncConfiguration.Builder(user2, defaultSchema) + val config2 = SyncConfiguration.Builder(user2, FLX_SYNC_SCHEMA) .initialSubscriptions { realm -> add( realm.query( @@ -305,7 +303,7 @@ class FlexibleSyncIntegrationTests { val channel = Channel(1) - val config1 = SyncConfiguration.Builder(user1, defaultSchema) + val config1 = SyncConfiguration.Builder(user1, FLX_SYNC_SCHEMA) .errorHandler { _: SyncSession, syncException: SyncException -> runBlocking { channel.send(syncException as CompensatingWriteException) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/MutableSubscriptionSetTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/MutableSubscriptionSetTests.kt index 790e3d0141..56ef66dc9a 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/MutableSubscriptionSetTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/MutableSubscriptionSetTests.kt @@ -64,7 +64,7 @@ class MutableSubscriptionSetTests { } config = SyncConfiguration.Builder( user, - schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class) + schema = FLX_SYNC_SCHEMA ) .build() realm = Realm.open(config) @@ -284,7 +284,7 @@ class MutableSubscriptionSetTests { // Not part of schema realm.subscriptions.update { assertFailsWith { - removeAll(io.realm.kotlin.entities.sync.ParentPk::class) + removeAll(io.realm.kotlin.entities.Sample::class) } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/ProgressListenerTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/ProgressListenerTests.kt index 3b0b3dcda8..a3310c5494 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/ProgressListenerTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/ProgressListenerTests.kt @@ -61,8 +61,6 @@ import kotlin.time.Duration.Companion.seconds private const val TEST_SIZE = 500 private val TIMEOUT = 30.seconds -private val schema = setOf(SyncObjectWithAllTypes::class) - class ProgressListenerTests { private lateinit var app: TestApp @@ -242,7 +240,7 @@ class ProgressListenerTests { fun throwsOnFlexibleSync() = runBlocking { TestApp("throwsOnFlexibleSync", TEST_APP_FLEX).use { val user = app.createUserAndLogIn() - val configuration: SyncConfiguration = SyncConfiguration.create(user, schema) + val configuration: SyncConfiguration = SyncConfiguration.create(user, FLX_SYNC_SCHEMA) Realm.open(configuration).use { realm -> assertFailsWithMessage( "Progress listeners are not supported for Flexible Sync" @@ -306,7 +304,7 @@ class ProgressListenerTests { user: User, partitionValue: String = getTestPartitionValue() ): SyncConfiguration { - return SyncConfiguration.Builder(user, partitionValue, schema) + return SyncConfiguration.Builder(user, partitionValue, io.realm.kotlin.test.mongodb.common.PARTITION_SYNC_SCHEMA) .build() } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/Schema.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/Schema.kt new file mode 100644 index 0000000000..13591ef636 --- /dev/null +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/Schema.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.realm.kotlin.test.mongodb.common + +import io.realm.kotlin.entities.sync.ChildPk +import io.realm.kotlin.entities.sync.ObjectIdPk +import io.realm.kotlin.entities.sync.ParentPk +import io.realm.kotlin.entities.sync.SyncObjectWithAllTypes +import io.realm.kotlin.entities.sync.SyncPerson +import io.realm.kotlin.entities.sync.flx.FlexChildObject +import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject +import io.realm.kotlin.entities.sync.flx.FlexParentObject + +private val ASYMMETRIC_CLASSES = setOf( + AsymmetricSyncTests.AsymmetricA::class, + AsymmetricSyncTests.EmbeddedB::class, + AsymmetricSyncTests.StandardC::class, + Measurement::class, +) + +private val DEFAULT_CLASSES = setOf( + BackupDevice::class, + ChildPk::class, + Device::class, + DeviceParent::class, + FlexChildObject::class, + FlexEmbeddedObject::class, + FlexParentObject::class, + ObjectIdPk::class, + ParentPk::class, + SyncObjectWithAllTypes::class, + SyncPerson::class +) + +val FLX_SYNC_SCHEMA = DEFAULT_CLASSES + ASYMMETRIC_CLASSES +val PARTITION_SYNC_SCHEMA = DEFAULT_CLASSES diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionExtensionsTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionExtensionsTests.kt index 8a7649627f..bce3f8697a 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionExtensionsTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionExtensionsTests.kt @@ -17,8 +17,6 @@ package io.realm.kotlin.test.mongodb.common import io.realm.kotlin.Realm -import io.realm.kotlin.entities.sync.flx.FlexChildObject -import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject import io.realm.kotlin.entities.sync.flx.FlexParentObject import io.realm.kotlin.ext.query import io.realm.kotlin.internal.platform.runBlocking @@ -45,7 +43,6 @@ import kotlin.test.assertFailsWith import kotlin.test.assertNotEquals import kotlin.test.assertNull import kotlin.test.assertTrue -import kotlin.text.Typography.section import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.Duration.Companion.seconds @@ -67,7 +64,7 @@ class SubscriptionExtensionsTests { } val config = SyncConfiguration.Builder( user, - schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class) + schema = FLX_SYNC_SCHEMA ) .build() realm = Realm.open(config) @@ -136,7 +133,7 @@ class SubscriptionExtensionsTests { val user1 = app.createUserAndLogIn(email, password) val config = SyncConfiguration.Builder( user1, - schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class) + schema = FLX_SYNC_SCHEMA ).initialSubscriptions { realm: Realm -> realm.query("section = $0", section).subscribe() }.build() @@ -394,7 +391,7 @@ class SubscriptionExtensionsTests { private suspend fun uploadServerData(sectionId: Int, noOfObjects: Int) { val user = app.createUserAndLogin() - val config = SyncConfiguration.Builder(user, setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class)) + val config = SyncConfiguration.Builder(user, FLX_SYNC_SCHEMA) .initialSubscriptions { it.query().subscribe() } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionSetTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionSetTests.kt index b18352c597..73487856bb 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionSetTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionSetTests.kt @@ -64,7 +64,7 @@ class SubscriptionSetTests { } val config = SyncConfiguration.Builder( user, - schema = setOf(FlexParentObject::class, FlexChildObject::class, FlexEmbeddedObject::class) + schema = FLX_SYNC_SCHEMA ) .build() realm = Realm.open(config) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionTests.kt index 8cc13229a9..1bc7105f75 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SubscriptionTests.kt @@ -62,7 +62,7 @@ class SubscriptionTests { } val config = SyncConfiguration.Builder( user, - schema = setOf(ParentPk::class, ChildPk::class) + schema = FLX_SYNC_SCHEMA ) .build() realm = Realm.open(config) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientResetIntegrationTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientResetIntegrationTests.kt index 5e9b6bb58d..16b68768c6 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientResetIntegrationTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientResetIntegrationTests.kt @@ -21,8 +21,6 @@ import io.realm.kotlin.MutableRealm import io.realm.kotlin.Realm import io.realm.kotlin.TypedRealm import io.realm.kotlin.entities.sync.SyncPerson -import io.realm.kotlin.entities.sync.flx.FlexChildObject -import io.realm.kotlin.entities.sync.flx.FlexEmbeddedObject import io.realm.kotlin.entities.sync.flx.FlexParentObject import io.realm.kotlin.ext.query import io.realm.kotlin.internal.interop.ErrorCode @@ -161,11 +159,7 @@ class SyncClientResetIntegrationTests { configBuilderGenerator = { user -> return@TestEnvironment SyncConfiguration.Builder( user, - setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ) + FLX_SYNC_SCHEMA ).initialSubscriptions { realm -> realm.query( "section = $0 AND name = $1", @@ -220,7 +214,7 @@ class SyncClientResetIntegrationTests { return@TestEnvironment SyncConfiguration.Builder( user, TestHelper.randomPartitionValue(), - schema = setOf(SyncPerson::class) + schema = PARTITION_SYNC_SCHEMA ) }, insertElement = { realm: Realm -> @@ -621,21 +615,25 @@ class SyncClientResetIntegrationTests { @Test fun discardUnsyncedChanges_executeClientReset_pbs() = runBlocking { - performPbsTest { _, _, _, builder -> - discardUnsyncedChanges_executeClientReset(builder) + performPbsTest { syncMode, app, user, builder -> + discardUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } @Test fun discardUnsyncedChanges_executeClientReset_flx() = runBlocking { - performFlxTest { _, _, _, builder -> - discardUnsyncedChanges_executeClientReset(builder) + performFlxTest { syncMode, app, user, builder -> + discardUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } private fun discardUnsyncedChanges_executeClientReset( + syncMode: SyncMode, + app: TestApp, + user: User, builder: SyncConfiguration.Builder ) { + var testRealm: Realm? = null // Channel size is 2 because both onError and onManualResetFallback are called val channel = Channel(2) val config = builder.syncClientResetStrategy(object : DiscardUnsyncedChangesStrategy { @@ -659,12 +657,14 @@ class SyncClientResetIntegrationTests { session: SyncSession, exception: ClientResetRequiredException ) { + testRealm!!.close() + val originalFilePath = assertNotNull(exception.originalFilePath) val recoveryFilePath = assertNotNull(exception.recoveryFilePath) assertTrue(fileExists(originalFilePath)) assertFalse(fileExists(recoveryFilePath)) - exception.executeClientReset() + assertTrue(exception.executeClientReset()) // Validate that files have been moved after explicit reset assertFalse(fileExists(originalFilePath)) @@ -674,14 +674,13 @@ class SyncClientResetIntegrationTests { } }).build() - Realm.open(config).use { realm -> - runBlocking { - realm.syncSession.downloadAllServerChanges(defaultTimeout) - - with(realm.syncSession as SyncSessionImpl) { - simulateSyncError(ErrorCode.RLM_ERR_AUTO_CLIENT_RESET_FAILED) - - // TODO Twice until the deprecated method is removed + runBlocking { + Realm.open(config).use { realm -> + testRealm = realm + with(realm.syncSession) { + downloadAllServerChanges(defaultTimeout) + app.triggerClientReset(syncMode, this, user.id) + // Twice until the deprecated method is removed assertEquals(ClientResetEvents.ON_MANUAL_RESET_FALLBACK, channel.receiveOrFail()) assertEquals(ClientResetEvents.ON_MANUAL_RESET_FALLBACK, channel.receiveOrFail()) } @@ -959,19 +958,22 @@ class SyncClientResetIntegrationTests { @Test fun manuallyRecoverUnsyncedChanges_executeClientReset_pbs() = runBlocking { - performPbsTest { _, _, _, builder -> - manuallyRecoverUnsyncedChanges_executeClientReset(builder) + performPbsTest { syncMode, app, user, builder -> + manuallyRecoverUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } @Test fun manuallyRecoverUnsyncedChanges_executeClientReset_flx() = runBlocking { - performFlxTest { _, _, _, builder -> - manuallyRecoverUnsyncedChanges_executeClientReset(builder) + performFlxTest { syncMode, app, user, builder -> + manuallyRecoverUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } private fun manuallyRecoverUnsyncedChanges_executeClientReset( + syncMode: SyncMode, + app: TestApp, + user: User, builder: SyncConfiguration.Builder ) { val channel = Channel(1) @@ -987,21 +989,18 @@ class SyncClientResetIntegrationTests { } ).build() - Realm.open(config).use { realm -> - runBlocking { - realm.syncSession.downloadAllServerChanges(defaultTimeout) - - with(realm.syncSession as SyncSessionImpl) { - simulateSyncError(ErrorCode.RLM_ERR_AUTO_CLIENT_RESET_FAILED) - + runBlocking { + Realm.open(config).use { realm -> + with(realm.syncSession) { + downloadAllServerChanges(defaultTimeout) + app.triggerClientReset(syncMode, this, user.id) val exception = channel.receiveOrFail() - val originalFilePath = assertNotNull(exception.originalFilePath) val recoveryFilePath = assertNotNull(exception.recoveryFilePath) + realm.close() assertTrue(fileExists(originalFilePath)) assertFalse(fileExists(recoveryFilePath)) - - exception.executeClientReset() + assertTrue(exception.executeClientReset()) assertFalse(fileExists(originalFilePath)) assertTrue(fileExists(recoveryFilePath)) } @@ -1180,23 +1179,30 @@ class SyncClientResetIntegrationTests { @Test fun recoverUnsyncedChanges_executeClientReset_pbs() = runBlocking { - performPbsTest { _, _, _, builder -> - recoverUnsyncedChanges_executeClientReset(builder) + performPbsTest { syncMode, app, user, builder -> + recoverUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } @Test fun recoverUnsyncedChanges_executeClientReset_flx() = runBlocking { - performFlxTest { _, _, _, builder -> - recoverUnsyncedChanges_executeClientReset(builder) + performFlxTest { syncMode, app, user, builder -> + recoverUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } - private fun recoverUnsyncedChanges_executeClientReset(builder: SyncConfiguration.Builder) { + private fun recoverUnsyncedChanges_executeClientReset( + syncMode: SyncMode, + app: TestApp, + user: User, + builder: SyncConfiguration.Builder + ) { + var testRealm: Realm? = null val channel = Channel(2) val config = builder.syncClientResetStrategy(object : RecoverUnsyncedChangesStrategy { override fun onBeforeReset(realm: TypedRealm) { - fail("Should not call onBeforeReset") + @Suppress("TooGenericExceptionThrown") + throw RuntimeException("Trigger onManualResetFallback") } override fun onAfterReset(before: TypedRealm, after: MutableRealm) { @@ -1207,19 +1213,21 @@ class SyncClientResetIntegrationTests { session: SyncSession, exception: ClientResetRequiredException ) { + testRealm!!.close() + val originalFilePath = assertNotNull(exception.originalFilePath) val recoveryFilePath = assertNotNull(exception.recoveryFilePath) assertTrue(fileExists(originalFilePath)) assertFalse(fileExists(recoveryFilePath)) - exception.executeClientReset() + assertTrue(exception.executeClientReset()) // Validate that files have been moved after explicit reset assertFalse(fileExists(originalFilePath)) assertTrue(fileExists(recoveryFilePath)) assertEquals( - "[Sync][AutoClientResetFailed(1028)] Simulate Client Reset.", + "[Sync][AutoClientResetFailed(1028)] A fatal error occurred during client reset: 'User-provided callback failed'.", exception.message ) @@ -1227,14 +1235,12 @@ class SyncClientResetIntegrationTests { } }).build() - Realm.open(config).use { realm -> - runBlocking { - realm.syncSession.downloadAllServerChanges(defaultTimeout) - - with(realm.syncSession as SyncSessionImpl) { - simulateSyncError(ErrorCode.RLM_ERR_AUTO_CLIENT_RESET_FAILED) - - // TODO Twice until the deprecated method is removed + runBlocking { + Realm.open(config).use { realm -> + testRealm = realm + with(realm.syncSession) { + downloadAllServerChanges(defaultTimeout) + app.triggerClientReset(syncMode, this, user.id) assertEquals(ClientResetEvents.ON_MANUAL_RESET_FALLBACK, channel.receiveOrFail()) } } @@ -1372,25 +1378,30 @@ class SyncClientResetIntegrationTests { @Test fun recoverOrDiscardUnsyncedChanges_executeClientReset_pbs() = runBlocking { - performPbsTest { _, _, _, builder -> - recoverOrDiscardUnsyncedChanges_executeClientReset(builder) + performPbsTest { syncMode, app, user, builder -> + recoverOrDiscardUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } @Test fun recoverOrDiscardUnsyncedChanges_executeClientReset_flx() = runBlocking { - performFlxTest { _, _, _, builder -> - recoverOrDiscardUnsyncedChanges_executeClientReset(builder) + performFlxTest { syncMode, app, user, builder -> + recoverOrDiscardUnsyncedChanges_executeClientReset(syncMode, app, user, builder) } } private fun recoverOrDiscardUnsyncedChanges_executeClientReset( + syncMode: SyncMode, + app: TestApp, + user: User, builder: SyncConfiguration.Builder ) { + var testRealm: Realm? = null val channel = Channel(2) val config = builder.syncClientResetStrategy(object : RecoverOrDiscardUnsyncedChangesStrategy { override fun onBeforeReset(realm: TypedRealm) { - fail("Should not call onBeforeReset") + @Suppress("TooGenericExceptionThrown") + throw RuntimeException("Trigger onManualResetFallback") } override fun onAfterRecovery(before: TypedRealm, after: MutableRealm) { @@ -1405,19 +1416,21 @@ class SyncClientResetIntegrationTests { session: SyncSession, exception: ClientResetRequiredException ) { + testRealm!!.close() + val originalFilePath = assertNotNull(exception.originalFilePath) val recoveryFilePath = assertNotNull(exception.recoveryFilePath) assertTrue(fileExists(originalFilePath)) assertFalse(fileExists(recoveryFilePath)) - exception.executeClientReset() + assertTrue(exception.executeClientReset()) // Validate that files have been moved after explicit reset assertFalse(fileExists(originalFilePath)) assertTrue(fileExists(recoveryFilePath)) assertEquals( - "[Sync][AutoClientResetFailed(1028)] Simulate Client Reset.", + "[Sync][AutoClientResetFailed(1028)] A fatal error occurred during client reset: 'User-provided callback failed'.", exception.message ) @@ -1425,14 +1438,12 @@ class SyncClientResetIntegrationTests { } }).build() - Realm.open(config).use { realm -> - runBlocking { - realm.syncSession.downloadAllServerChanges(defaultTimeout) - - with(realm.syncSession as SyncSessionImpl) { - simulateSyncError(ErrorCode.RLM_ERR_AUTO_CLIENT_RESET_FAILED) - - // TODO Twice until the deprecated method is removed + runBlocking { + Realm.open(config).use { realm -> + testRealm = realm + with(realm.syncSession) { + downloadAllServerChanges(defaultTimeout) + app.triggerClientReset(syncMode, this, user.id) assertEquals(ClientResetEvents.ON_MANUAL_RESET_FALLBACK, channel.receiveOrFail()) } } diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientTests.kt index 1a79d7349b..5cdd58e678 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncClientTests.kt @@ -56,7 +56,7 @@ class SyncClientTests { // There is no way to test reconnect automatically, so just verify that code path does not crash. @Test fun reconnect() { - val config = SyncConfiguration.create(user, schema = setOf()) + val config = SyncConfiguration.create(user, schema = FLX_SYNC_SCHEMA) Realm.open(config).use { app.sync.reconnect() } @@ -69,7 +69,7 @@ class SyncClientTests { @Test fun hasSyncSessions() { - val config = SyncConfiguration.create(user, schema = setOf()) + val config = SyncConfiguration.create(user, schema = FLX_SYNC_SCHEMA) Realm.open(config).use { assertTrue(app.sync.hasSyncSessions) } @@ -82,8 +82,8 @@ class SyncClientTests { @Test fun waitForSessionsToTerminate() { - val config1 = SyncConfiguration.Builder(user, schema = setOf()).build() - val config2 = SyncConfiguration.Builder(user, schema = setOf()).name("other.realm").build() + val config1 = SyncConfiguration.Builder(user, schema = FLX_SYNC_SCHEMA).build() + val config2 = SyncConfiguration.Builder(user, schema = FLX_SYNC_SCHEMA).name("other.realm").build() Realm.open(config1).use { assertTrue(app.sync.hasSyncSessions) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncConfigTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncConfigTests.kt index 217516f2dd..77a80463d5 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncConfigTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncConfigTests.kt @@ -105,7 +105,7 @@ class SyncConfigTests { val logger = createDefaultSystemLogger("TEST", LogLevel.DEBUG) val customLoggers = listOf(logger) val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).also { builder -> @@ -124,7 +124,7 @@ class SyncConfigTests { } val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).also { builder -> @@ -137,7 +137,7 @@ class SyncConfigTests { fun errorHandler_default() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() @@ -150,7 +150,7 @@ class SyncConfigTests { fun compactOnLaunch_default() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() @@ -164,7 +164,7 @@ class SyncConfigTests { val user = createTestUser() val callback = CompactOnLaunchCallback { _, _ -> false } val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ) @@ -186,7 +186,7 @@ class SyncConfigTests { ) } val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = PARTITION_SYNC_SCHEMA, user = user, partitionValue = partitionValue ) @@ -249,7 +249,7 @@ class SyncConfigTests { } } val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).syncClientResetStrategy(strategy) @@ -277,7 +277,7 @@ class SyncConfigTests { } } val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).syncClientResetStrategy(strategy) @@ -309,7 +309,7 @@ class SyncConfigTests { } } val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).syncClientResetStrategy(strategy) @@ -321,7 +321,7 @@ class SyncConfigTests { fun equals_sameObject() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() @@ -374,7 +374,7 @@ class SyncConfigTests { fun equals_syncSpecificFields() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() @@ -532,7 +532,7 @@ class SyncConfigTests { fun encryption() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).also { builder -> @@ -759,7 +759,7 @@ class SyncConfigTests { fun getPartitionValue() { val user = createTestUser() val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() @@ -1243,7 +1243,7 @@ class SyncConfigTests { } SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).build() diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncSessionTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncSessionTests.kt index aac6fec160..827832ed2f 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncSessionTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncSessionTests.kt @@ -258,13 +258,13 @@ class SyncSessionTests { val config1 = SyncConfiguration.Builder( user1, partitionValue, - schema = setOf(ParentPk::class, ChildPk::class) + schema = FLX_SYNC_SCHEMA ).name("user1.realm") .build() val config2 = SyncConfiguration.Builder( user2, partitionValue, - schema = setOf(ParentPk::class, ChildPk::class) + schema = FLX_SYNC_SCHEMA ).name("user2.realm") .build() @@ -320,7 +320,7 @@ class SyncSessionTests { val user = app.createUserAndLogIn(email, password) val channel = Channel(1) val config = SyncConfiguration.Builder( - schema = setOf(ParentPk::class, ChildPk::class), + schema = PARTITION_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).errorHandler { session, _ -> @@ -382,7 +382,7 @@ class SyncSessionTests { @Test fun syncingObjectIdFromMongoDB() = runBlocking { val adminApi = app.asTestApp - val config = SyncConfiguration.Builder(user, partitionValue, schema = setOf(ObjectIdPk::class)).build() + val config = SyncConfiguration.Builder(user, partitionValue, schema = PARTITION_SYNC_SCHEMA).build() Realm.open(config).use { realm -> val json: JsonObject = adminApi.insertDocument( ObjectIdPk::class.simpleName!!, @@ -427,7 +427,7 @@ class SyncSessionTests { val config = SyncConfiguration.Builder( user, partitionValue, - schema = setOf(ObjectIdPk::class) + schema = PARTITION_SYNC_SCHEMA ) .build() Realm.open(config).use { realm -> @@ -473,6 +473,7 @@ class SyncSessionTests { } } + @Ignore // TODO Find another way to test with with developer mode v2 @Test fun getConfiguration_inErrorHandlerThrows() = runBlocking { // Open and close a realm with a schema. @@ -480,7 +481,7 @@ class SyncSessionTests { val (email, password) = TestHelper.randomEmail() to "password1234" val user = app.createUserAndLogIn(email, password) val config1 = SyncConfiguration.Builder( - schema = setOf(ChildPk::class), + schema = FLX_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).name("test1.realm").build() diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncedRealmTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncedRealmTests.kt index d8c59a2b38..e486bd9e4d 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncedRealmTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/SyncedRealmTests.kt @@ -63,7 +63,6 @@ import io.realm.kotlin.test.util.TestHelper import io.realm.kotlin.test.util.TestHelper.randomEmail import io.realm.kotlin.test.util.receiveOrFail import io.realm.kotlin.test.util.use -import io.realm.kotlin.types.BaseRealmObject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel @@ -79,7 +78,6 @@ import okio.Path.Companion.toPath import org.mongodb.kbson.ObjectId import kotlin.random.Random import kotlin.random.nextULong -import kotlin.reflect.KClass import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Ignore @@ -119,7 +117,7 @@ class SyncedRealmTests { app.createUserAndLogIn(email, password) } - syncConfiguration = createSyncConfig( + syncConfiguration = createPartitionSyncConfig( user = user, partitionValue = partitionValue, ) @@ -149,7 +147,7 @@ class SyncedRealmTests { val user = app.createUserAndLogIn(email, password) val partitionValue = Random.nextULong().toString() - val config1 = createSyncConfig( + val config1 = createPartitionSyncConfig( user = user, partitionValue = partitionValue, name = "db1", errorHandler = object : SyncSession.ErrorHandler { override fun onError(session: SyncSession, error: SyncException) { @@ -158,7 +156,7 @@ class SyncedRealmTests { } ) Realm.open(config1).use { realm1 -> - val config2 = createSyncConfig( + val config2 = createPartitionSyncConfig( user = user, partitionValue = partitionValue, name = "db2", errorHandler = object : SyncSession.ErrorHandler { override fun onError(session: SyncSession, error: SyncException) { @@ -216,11 +214,10 @@ class SyncedRealmTests { val user1 = app.createUserAndLogIn(email1, password1) val user2 = app.createUserAndLogIn(email2, password2) - val config1 = createSyncConfig( + val config1 = createPartitionSyncConfig( user = user1, name = "db1.realm", - partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) + partitionValue = partitionValue ) val realm1 = Realm.open(config1) val c = Channel>(1) @@ -233,11 +230,10 @@ class SyncedRealmTests { assertTrue(event is InitialRealm) // Write remote change - createSyncConfig( + createPartitionSyncConfig( user = user2, name = "db2.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ).let { config -> Realm.open(config).use { realm -> realm.write { @@ -274,10 +270,10 @@ class SyncedRealmTests { val partitionValue = Random.nextLong().toString() // Setup two realms that synchronizes with the backend - val config1 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db1") + val config1 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db1") val realm1 = Realm.open(config1) assertNotNull(realm1) - val config2 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db2") + val config2 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db2") val realm2 = Realm.open(config2) assertNotNull(realm2) @@ -296,7 +292,7 @@ class SyncedRealmTests { // writer and notifier) are opened before the schema is synced from the server, but // empirically it has shown not to be the case and cause trouble if opening the second or // third realm with the wrong sync-intended schema mode. - val config3 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db3") + val config3 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db3") val realm3 = Realm.open(config3) assertNotNull(realm3) @@ -620,10 +616,9 @@ class SyncedRealmTests { val id = "id-${Random.nextLong()}" val masterObject = SyncObjectWithAllTypes.createWithSampleData(id) - createSyncConfig( + createPartitionSyncConfig( user = user1, partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ).let { config -> Realm.open(config).use { realm -> realm.write { @@ -632,10 +627,9 @@ class SyncedRealmTests { realm.syncSession.uploadAllLocalChanges() } } - createSyncConfig( + createPartitionSyncConfig( user = user2, partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ).let { config -> Realm.open(config).use { realm -> val list: RealmResults = @@ -654,16 +648,17 @@ class SyncedRealmTests { // return the full on-disk schema from ObjectStore, but for typed Realms the user visible schema // should still only return classes and properties that was defined by the user. @Test + @Ignore // TODO Need to adopt this to developer mode fun onlyLocalSchemaIsVisible() = runBlocking { val (email1, password1) = randomEmail() to "password1234" val (email2, password2) = randomEmail() to "password1234" val user1 = app.createUserAndLogIn(email1, password1) val user2 = app.createUserAndLogIn(email2, password2) - createSyncConfig( + createPartitionSyncConfig( user = user1, partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class, ChildPk::class) + // schema = setOf(SyncObjectWithAllTypes::class, ChildPk::class) ).let { config -> Realm.open(config).use { realm -> realm.syncSession.uploadAllLocalChanges() @@ -676,10 +671,10 @@ class SyncedRealmTests { assertNotNull(childPkSchema["linkedFrom"]) } } - createSyncConfig( + createPartitionSyncConfig( user = user2, partitionValue = partitionValue, - schema = setOf(io.realm.kotlin.entities.sync.subset.ChildPk::class) + // schema = setOf(io.realm.kotlin.entities.sync.subset.ChildPk::class) ).let { config -> Realm.open(config).use { realm -> // Make sure that server schema changes are integrated @@ -701,27 +696,24 @@ class SyncedRealmTests { @Test fun mutableRealmInt_convergesAcrossClients() = runBlocking { // Updates and initial data upload are carried out using this config - val config0 = createSyncConfig( + val config0 = createPartitionSyncConfig( user = app.createUserAndLogIn(randomEmail(), "password1234"), partitionValue = partitionValue, name = "db1", - schema = setOf(SyncObjectWithAllTypes::class) ) // Config for update 1 - val config1 = createSyncConfig( + val config1 = createPartitionSyncConfig( user = app.createUserAndLogIn(randomEmail(), "password1234"), partitionValue = partitionValue, name = "db2", - schema = setOf(SyncObjectWithAllTypes::class) ) // Config for update 2 - val config2 = createSyncConfig( + val config2 = createPartitionSyncConfig( user = app.createUserAndLogIn(randomEmail(), "password1234"), partitionValue = partitionValue, name = "db3", - schema = setOf(SyncObjectWithAllTypes::class) ) val counterValue = Channel(1) @@ -816,17 +808,15 @@ class SyncedRealmTests { val user2 = app.createUserAndLogIn(email2, password2) val localConfig = createWriteCopyLocalConfig("local.realm") val partitionValue = TestHelper.randomPartitionValue() - val syncConfig1 = createSyncConfig( + val syncConfig1 = createPartitionSyncConfig( user = user1, name = "sync1.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ) - val syncConfig2 = createSyncConfig( + val syncConfig2 = createPartitionSyncConfig( user = user2, name = "sync2.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ) Realm.open(localConfig).use { localRealm -> localRealm.writeBlocking { @@ -876,12 +866,7 @@ class SyncedRealmTests { val user1 = flexApp.createUserAndLogIn(email1, password1) val localConfig = createWriteCopyLocalConfig("local.realm") val flexSyncConfig = createFlexibleSyncConfig( - user = user1, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ) + user = user1 ) Realm.open(localConfig).use { localRealm -> localRealm.writeBlocking { @@ -907,11 +892,10 @@ class SyncedRealmTests { val migratedLocalConfig = createWriteCopyLocalConfig("local.realm", directory = dir, schemaVersion = 1) val partitionValue = TestHelper.randomPartitionValue() - val syncConfig = createSyncConfig( + val syncConfig = createPartitionSyncConfig( user = user, name = "sync1.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ) Realm.open(syncConfig).use { syncRealm -> // Write local data @@ -960,15 +944,10 @@ class SyncedRealmTests { val (email1, password1) = randomEmail() to "password1234" val user = flexApp.createUserAndLogIn(email1, password1) val localConfig = createWriteCopyLocalConfig("local.realm") - val syncConfig = createSyncConfig( + val syncConfig = createPartitionSyncConfig( user = user, name = "sync.realm", partitionValue = partitionValue, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ) ) Realm.open(syncConfig).use { flexSyncRealm: Realm -> flexSyncRealm.writeBlocking { @@ -995,17 +974,15 @@ class SyncedRealmTests { val (email2, password2) = randomEmail() to "password1234" val user1 = app.createUserAndLogIn(email1, password1) val user2 = app.createUserAndLogIn(email2, password2) - val syncConfig1 = createSyncConfig( + val syncConfig1 = createPartitionSyncConfig( user = user1, name = "sync1.realm", partitionValue = TestHelper.randomPartitionValue(), - schema = setOf(SyncObjectWithAllTypes::class) ) - val syncConfig2 = createSyncConfig( + val syncConfig2 = createPartitionSyncConfig( user = user2, name = "sync2.realm", partitionValue = TestHelper.randomPartitionValue(), - schema = setOf(SyncObjectWithAllTypes::class) ) Realm.open(syncConfig1).use { syncRealm1 -> syncRealm1.writeBlocking { @@ -1039,17 +1016,15 @@ class SyncedRealmTests { val user1 = app.createUserAndLogIn(email1, password1) val user2 = app.createUserAndLogIn(email2, password2) val partitionValue = TestHelper.randomPartitionValue() - val syncConfig1 = createSyncConfig( + val syncConfig1 = createPartitionSyncConfig( user = user1, name = "sync1.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ) - val syncConfig2 = createSyncConfig( + val syncConfig2 = createPartitionSyncConfig( user = user2, name = "sync2.realm", partitionValue = partitionValue, - schema = setOf(SyncObjectWithAllTypes::class) ) Realm.open(syncConfig1).use { syncRealm1 -> // Write local data @@ -1098,11 +1073,6 @@ class SyncedRealmTests { errorHandler = { _, error -> fail(error.toString()) }, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ), initialSubscriptions = { realm: Realm -> realm.query("section = $0", section).subscribe(name = "parentSubscription") } @@ -1112,12 +1082,7 @@ class SyncedRealmTests { name = "sync2.realm", errorHandler = { _, error -> fail(error.toString()) - }, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ) + } ) Realm.open(syncConfig1).use { flexRealm1: Realm -> @@ -1167,17 +1132,15 @@ class SyncedRealmTests { fun writeCopyTo_dataNotUploaded_throws() = runBlocking { val (email1, password1) = randomEmail() to "password1234" val user1 = app.createUserAndLogIn(email1, password1) - val syncConfigA = createSyncConfig( + val syncConfigA = createPartitionSyncConfig( user = user1, name = "a.realm", partitionValue = TestHelper.randomPartitionValue(), - schema = setOf(SyncObjectWithAllTypes::class) ) - val syncConfigB = createSyncConfig( + val syncConfigB = createPartitionSyncConfig( user = user1, name = "b.realm", partitionValue = TestHelper.randomPartitionValue(), - schema = setOf(SyncObjectWithAllTypes::class) ) Realm.open(syncConfigA).use { realm -> realm.syncSession.pause() @@ -1312,8 +1275,8 @@ class SyncedRealmTests { partitionValue = TestHelper.randomPartitionValue() - val config1 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db1") - val config2 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db2") + val config1 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db1") + val config2 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db2") Realm.open(config1).use { realm1 -> Realm.open(config2).use { realm2 -> @@ -1356,7 +1319,7 @@ class SyncedRealmTests { copyToRealm(ParentPk().apply { _id = ObjectId().toString() }) } .build() - val config2 = createSyncConfig(user = user, partitionValue = partitionValue, name = "db1") + val config2 = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "db1") Realm.open(config1).use { assertEquals(2, it.query().find().size) it.writeCopyTo(config2) @@ -1372,7 +1335,7 @@ class SyncedRealmTests { val user = runBlocking { app.createUserAndLogIn(email, password) } - val config1 = createSyncConfig( + val config1 = createPartitionSyncConfig( user = user, partitionValue = partitionValue, name = "db1", errorHandler = object : SyncSession.ErrorHandler { override fun onError(session: SyncSession, error: SyncException) { @@ -1395,7 +1358,7 @@ class SyncedRealmTests { } } - val config2 = createSyncConfig( + val config2 = createPartitionSyncConfig( user = user, partitionValue = partitionValue, name = "db1", errorHandler = object : SyncSession.ErrorHandler { override fun onError(session: SyncSession, error: SyncException) { @@ -1442,11 +1405,6 @@ class SyncedRealmTests { errorHandler = { _, error -> fail(error.toString()) }, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ), initialSubscriptions = { realm: Realm -> realm.query() .subscribe(name = "parentSubscription") @@ -1457,12 +1415,7 @@ class SyncedRealmTests { name = "asset-fs.realm", errorHandler = { _, error -> fail(error.toString()) - }, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ) + } ) Realm.open(syncConfig1).use { flexRealm1: Realm -> @@ -1504,12 +1457,7 @@ class SyncedRealmTests { name = "sync1.realm", errorHandler = { _, error -> fail(error.toString()) - }, - schema = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ), + } ) { initialRealmFile("asset-fs.realm") initialData { @@ -1531,7 +1479,7 @@ class SyncedRealmTests { app.createUserAndLogIn(email, password) } - val local = createSyncConfig(user = user, partitionValue = partitionValue, name = "local") { + val local = createPartitionSyncConfig(user = user, partitionValue = partitionValue, name = "local") { initialRealmFile("asset-local.realm") } assertFalse(fileExists(local.path)) @@ -1898,17 +1846,16 @@ class SyncedRealmTests { // } @Suppress("LongParameterList") - private fun createSyncConfig( + private fun createPartitionSyncConfig( user: User, partitionValue: String, name: String = DEFAULT_NAME, encryptionKey: ByteArray? = null, log: LogConfiguration? = null, errorHandler: ErrorHandler? = null, - schema: Set> = setOf(ParentPk::class, ChildPk::class), block: SyncConfiguration.Builder.() -> Unit = {} ): SyncConfiguration = SyncConfiguration.Builder( - schema = schema, + schema = PARTITION_SYNC_SCHEMA, user = user, partitionValue = partitionValue ).name(name).also { builder -> @@ -1925,16 +1872,11 @@ class SyncedRealmTests { encryptionKey: ByteArray? = null, log: LogConfiguration? = null, errorHandler: ErrorHandler? = null, - schema: Set> = setOf( - FlexParentObject::class, - FlexChildObject::class, - FlexEmbeddedObject::class - ), initialSubscriptions: InitialSubscriptionsCallback? = null, block: SyncConfiguration.Builder.() -> Unit = {}, ): SyncConfiguration = SyncConfiguration.Builder( user = user, - schema = schema + schema = FLX_SYNC_SCHEMA ).name(name).also { builder -> if (encryptionKey != null) builder.encryptionKey(encryptionKey) if (errorHandler != null) builder.errorHandler(errorHandler) diff --git a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/nonlatin/NonLatinTests.kt b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/nonlatin/NonLatinTests.kt index 5d66dd280a..effa1d2db0 100644 --- a/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/nonlatin/NonLatinTests.kt +++ b/packages/test-sync/src/commonTest/kotlin/io/realm/kotlin/test/mongodb/common/nonlatin/NonLatinTests.kt @@ -8,6 +8,7 @@ import io.realm.kotlin.mongodb.User import io.realm.kotlin.mongodb.sync.SyncConfiguration import io.realm.kotlin.test.mongodb.TestApp import io.realm.kotlin.test.mongodb.asTestApp +import io.realm.kotlin.test.mongodb.common.PARTITION_SYNC_SCHEMA import io.realm.kotlin.test.mongodb.createUserAndLogIn import io.realm.kotlin.test.util.TestHelper import io.realm.kotlin.test.util.receiveOrFail @@ -54,7 +55,7 @@ class NonLatinTests { @Test fun readNullCharacterFromMongoDB() = runBlocking { val adminApi = app.asTestApp - val config = SyncConfiguration.Builder(user, partitionValue, schema = setOf(ObjectIdPk::class)).build() + val config = SyncConfiguration.Builder(user, partitionValue, schema = PARTITION_SYNC_SCHEMA).build() Realm.open(config).use { realm -> val json: JsonObject = adminApi.insertDocument( ObjectIdPk::class.simpleName!!,