diff --git a/.github/workflows/pr-coverage.yml b/.github/workflows/pr-coverage.yml index 810c7ad0de..3d89393e6d 100644 --- a/.github/workflows/pr-coverage.yml +++ b/.github/workflows/pr-coverage.yml @@ -62,7 +62,7 @@ jobs: timeout-minutes: 60 run: npm run ci:coverage --workspace @realm/integration-tests -- --reporter mocha-github-actions-reporter --timeout ${{ env.MOCHA_TIMEOUT }} env: - CONTEXT: syncLogLevel=warn,longTimeout=${{ env.LONG_TIMEOUT }},realmBaseUrl=${{ steps.baas.outputs.baas-url }} + CONTEXT: syncLogLevel=warn,longTimeout=${{ env.LONG_TIMEOUT }},baseUrl=${{ steps.baas.outputs.baas-url }} - name: Coveralls uses: coverallsapp/github-action@v2 diff --git a/.github/workflows/pr-realm-js.yml b/.github/workflows/pr-realm-js.yml index 820867d596..44a34a6e5a 100644 --- a/.github/workflows/pr-realm-js.yml +++ b/.github/workflows/pr-realm-js.yml @@ -383,7 +383,7 @@ jobs: - name: Create Mocha Remote Context id: mocha-env - run: echo "context=syncLogLevel=warn,longTimeout=${{ env.LONG_TIMEOUT }},realmBaseUrl=${{ steps.baas.outputs.baas-url }}" >> $GITHUB_OUTPUT + run: echo "context=syncLogLevel=warn,longTimeout=${{ env.LONG_TIMEOUT }},baseUrl=${{ steps.baas.outputs.baas-url }}" >> $GITHUB_OUTPUT - name: Run ${{matrix.variant.target}} (${{ matrix.variant.os}} / ${{ matrix.variant.environment }}) if: ${{ matrix.variant.os != 'android' && matrix.variant.os != 'ios' }} diff --git a/integration-tests/README.md b/integration-tests/README.md index 98677fb3bd..77b944f0f3 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -81,7 +81,7 @@ Examples of context variables used: - `defaultLogLevel=all`: Set the default log level to help debugging realm core issues. - `syncLogLevel=all`: Set the sync client log level to help debugging sync client issues. - `reuseApp=true`: Instructs the app importer to reuse and reconfigure a single app. Defaults to `false`. -- `realmBaseUrl=https://localhost:9090`: Set the base URL used when connecting the the server. +- `baseUrl=https://localhost:9090`: Set the base URL used when connecting the the server. - `mongodbClusterName=Cluster0`: Set the name of the cluster, used when setting up the "mongodb-atlas" service on imported apps. - `mongodbServiceType`: Set the type of mongodb service, used when importing. Defaults to `mongodb` or `mongodb-atlas` if `mongodbClusterName` is set. diff --git a/integration-tests/environments/react-native/src/App.js b/integration-tests/environments/react-native/src/App.js index 34f2041712..411c2d9a73 100644 --- a/integration-tests/environments/react-native/src/App.js +++ b/integration-tests/environments/react-native/src/App.js @@ -144,7 +144,7 @@ export class App extends Component { global.path = require("path-browserify"); global.environment = { // Default to the host machine when running on Android - realmBaseUrl: Platform.OS === "android" ? "http://10.0.2.2:9090" : undefined, + baseUrl: Platform.OS === "android" ? "http://10.0.2.2:9090" : undefined, ...context, reactNative: Platform.OS, android: Platform.OS === "android", diff --git a/integration-tests/tests/src/hooks/import-app-before.ts b/integration-tests/tests/src/hooks/import-app-before.ts index 8dd6c38c98..94621b886c 100644 --- a/integration-tests/tests/src/hooks/import-app-before.ts +++ b/integration-tests/tests/src/hooks/import-app-before.ts @@ -16,15 +16,27 @@ // //////////////////////////////////////////////////////////////////////////// -import Realm, { AppConfiguration } from "realm"; +import Realm from "realm"; -import { importApp } from "../utils/import-app"; -import { AppConfig } from "@realm/app-importer"; +import { AppConfig, AppImporter, Credentials } from "@realm/app-importer"; import { mongodbServiceType } from "../utils/ExtendedAppConfigBuilder"; const REALM_LOG_LEVELS = ["all", "trace", "debug", "detail", "info", "warn", "error", "fatal", "off"]; -const { syncLogLevel = "warn" } = environment; +const { + syncLogLevel = "warn", + baseUrl = "http://localhost:9090", + reuseApp = false, + username = "unique_user@domain.com", + password = "password", + publicKey, + privateKey, + missingServer, +} = environment; + +export { baseUrl }; + +const allowSkippingServerTests = typeof environment.baseUrl === "undefined" && missingServer !== false; export type AppConfigurationRelaxed = { id?: string; @@ -34,6 +46,69 @@ export type AppConfigurationRelaxed = { baseFilePath?: string; }; +function getCredentials(): Credentials { + if (typeof publicKey === "string" && typeof privateKey === "string") { + return { + kind: "api-key", + publicKey, + privateKey, + }; + } else { + return { + kind: "username-password", + username, + password, + }; + } +} + +const credentials = getCredentials(); + +const importer = new AppImporter({ + baseUrl, + credentials, + reuseApp, +}); + +function isConnectionRefused(err: unknown) { + return ( + err instanceof Error && + err.cause instanceof AggregateError && + "code" in err.cause && + err.cause.code === "ECONNREFUSED" + ); +} + +function printWarningBox(...lines: string[]) { + const contentWidth = Math.max(...lines.map((line) => line.length)); + const bar = "━".repeat(contentWidth + 2); + console.warn(`┏${bar}┓`); + for (const line of lines) { + const padding = " ".repeat(contentWidth - line.length); + console.warn(`┃ ${line}${padding} ┃`); + } + console.warn(`┗${bar}┛`); +} + +/** Ensure we'll only ever install a single after hook with this warning */ +let skippedAppImportAfterHookInstalled = false; +function ensureSkippedAppImportAfterHook() { + if (!skippedAppImportAfterHookInstalled) { + skippedAppImportAfterHookInstalled = true; + after(function (this: Mocha.Context) { + printWarningBox( + "Connection got refused while importing an app - skipping tests", + "Run this with `MOCHA_REMOTE_CONTEXT=missingServer` to silence this warning", + ); + }); + } +} + +/** + * Imports an app before the suite runs and stores an `App` instance on the test context (accessible via `this.app` of a test). + * If the `missingServer` context is set the suite will be skipped. + * If the import fails due to a connection refusal, the suite will be skipped and a warning printed at the end of the test run. + */ export function importAppBefore(config: AppConfig | { config: AppConfig }, sdkConfig?: AppConfigurationRelaxed): void { // Unwrap when passed a builder directly if ("config" in config) { @@ -41,13 +116,25 @@ export function importAppBefore(config: AppConfig | { config: AppConfig }, sdkCo } before(importAppBefore.name, async function (this: AppContext & Mocha.Context) { + if (missingServer) { + this.skip(); + } // Importing an app might take up to 5 minutes when the app has a MongoDB Atlas service enabled. this.longTimeout(); if (this.app) { throw new Error("Unexpected app on context, use only one importAppBefore per test"); } else { - const { appId, baseUrl } = await importApp(config); - this.app = new Realm.App({ id: appId, baseUrl, ...sdkConfig }); + try { + const { appId } = await importer.importApp(config); + this.app = new Realm.App({ id: appId, baseUrl, ...sdkConfig }); + } catch (err) { + if (isConnectionRefused(err) && allowSkippingServerTests) { + ensureSkippedAppImportAfterHook(); + this.skip(); + } else { + throw err; + } + } // Extract the sync database name from the config const databaseNames: (string | undefined)[] = config.services diff --git a/integration-tests/tests/src/hooks/open-realm-before.ts b/integration-tests/tests/src/hooks/open-realm-before.ts index 40c3bebcb9..4bc9627db7 100644 --- a/integration-tests/tests/src/hooks/open-realm-before.ts +++ b/integration-tests/tests/src/hooks/open-realm-before.ts @@ -34,9 +34,6 @@ export function openRealmHook(config: OpenRealmConfiguration = {}) { if (this.realm) { throw new Error("Unexpected realm on context, use only one openRealmBefore per test"); } else { - this.closeRealm = async () => { - console.warn("🤷 Skipped closing a Realm that failed to open"); - }; const { realm, config: actualConfig } = await openRealm(config, this.user as unknown as User); this.realm = realm; this.closeRealm = async ({ @@ -70,7 +67,9 @@ export function openRealmHook(config: OpenRealmConfiguration = {}) { * @param this Mocha `this` context */ export function closeThisRealm(this: RealmContext & Mocha.Context): void { - this.closeRealm({ clearTestState: true, deleteFile: true }); + if (this.closeRealm) { + this.closeRealm({ clearTestState: true, deleteFile: true }); + } // Clearing the test state to ensure the sync session gets completely reset and nothing is cached between tests Realm.clearTestState(); } diff --git a/integration-tests/tests/src/index.ts b/integration-tests/tests/src/index.ts index 5f248946c0..40fae83792 100644 --- a/integration-tests/tests/src/index.ts +++ b/integration-tests/tests/src/index.ts @@ -34,7 +34,6 @@ afterEach(() => { } }); -import "./utils/import-app.test.ts"; import "./utils/chai-plugin.test.ts"; import "./utils/listener-stub.test.ts"; import "./utils/promise-handle.test.ts"; diff --git a/integration-tests/tests/src/node/node-fetch.ts b/integration-tests/tests/src/node/node-fetch.ts index b0527d1af9..67d64e4c8a 100644 --- a/integration-tests/tests/src/node/node-fetch.ts +++ b/integration-tests/tests/src/node/node-fetch.ts @@ -16,15 +16,14 @@ // //////////////////////////////////////////////////////////////////////////// -import { expect } from "chai"; import nodeFetch from "node-fetch"; import Realm from "realm"; import { importAppBefore } from "../hooks"; import { buildAppConfig } from "../utils/build-app-config"; -import { baseUrl } from "../utils/import-app"; +import { baseUrl } from "../hooks/import-app-before"; -describe.skipIf(environment.missingServer, "passing node-fetch to AppConfiguration", () => { +describe("passing node-fetch to AppConfiguration", () => { importAppBefore(buildAppConfig().anonAuth()); it("is supported", async function (this: AppContext) { diff --git a/integration-tests/tests/src/node/path.ts b/integration-tests/tests/src/node/path.ts index cc78de4f33..ad7ab5ecbb 100644 --- a/integration-tests/tests/src/node/path.ts +++ b/integration-tests/tests/src/node/path.ts @@ -59,7 +59,7 @@ describe("path configuration (local)", function () { }); }); -describe.skipIf(environment.missingServer, `app configuration of root directory (flexible sync)`, async function () { +describe("app configuration of root directory (flexible sync)", async function () { // describe.only(`app configuration of root directory (flexible sync)`, async function () { this.timeout(60_000); const tmpdir = getAbsolutePath(); @@ -85,7 +85,7 @@ describe.skipIf(environment.missingServer, `app configuration of root directory }); }); -describe.skipIf(environment.missingServer, "path configuration (partition based sync)", function () { +describe("path configuration (partition based sync)", function () { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); authenticateUserBefore(); @@ -123,46 +123,42 @@ describe.skipIf(environment.missingServer, "path configuration (partition based }); }); -describe.skipIf( - environment.skipFlexibleSync || environment.missingServer, - "path configuration (flexible sync)", - function () { - importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); - authenticateUserBefore(); +describe.skipIf(environment.skipFlexibleSync, "path configuration (flexible sync)", function () { + importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); + authenticateUserBefore(); - it("absolute path", async function () { - this.longTimeout(); - const filename = getAbsolutePath(); - const realm = await Realm.open({ - path: filename, - schema: [schema], - sync: { - flexible: true, - user: this.user, - }, - }); - expect(realm.path).to.equal(filename); - expect(Realm.exists({ path: filename })).to.be.true; - realm.close(); - Realm.deleteFile({ path: filename }); + it("absolute path", async function () { + this.longTimeout(); + const filename = getAbsolutePath(); + const realm = await Realm.open({ + path: filename, + schema: [schema], + sync: { + flexible: true, + user: this.user, + }, }); + expect(realm.path).to.equal(filename); + expect(Realm.exists({ path: filename })).to.be.true; + realm.close(); + Realm.deleteFile({ path: filename }); + }); - it("relative path", async function () { - this.longTimeout(); - const filename = getRelativePath(); - const realm = await Realm.open({ - path: filename, - schema: [schema], - sync: { - flexible: true, - user: this.user, - }, - }); - // Realm Core will add a ".realm" suffix and url encode the path, if path is relative and sync is configured - const realmPath = realm.path; - expect(Realm.exists({ path: realmPath })).to.be.true; - realm.close(); - Realm.deleteFile({ path: realmPath }); + it("relative path", async function () { + this.longTimeout(); + const filename = getRelativePath(); + const realm = await Realm.open({ + path: filename, + schema: [schema], + sync: { + flexible: true, + user: this.user, + }, }); - }, -); + // Realm Core will add a ".realm" suffix and url encode the path, if path is relative and sync is configured + const realmPath = realm.path; + expect(Realm.exists({ path: realmPath })).to.be.true; + realm.close(); + Realm.deleteFile({ path: realmPath }); + }); +}); diff --git a/integration-tests/tests/src/node/ssl.ts b/integration-tests/tests/src/node/ssl.ts index 6a494e02a1..1756caf71f 100644 --- a/integration-tests/tests/src/node/ssl.ts +++ b/integration-tests/tests/src/node/ssl.ts @@ -39,13 +39,13 @@ import { buildAppConfig } from "../utils/build-app-config"; import { closeRealm } from "../utils/close-realm"; import { createPromiseHandle } from "../utils/promise-handle"; import { importAppBefore } from "../hooks"; -import { baseUrl } from "../utils/import-app"; +import { baseUrl } from "../hooks/import-app-before"; // IMPORTANT: // * Can only run on non-Apple machines, otherwise tests will await forever. // * Can only run if the baseUrl points to a server using TLS / HTTPs. const missingTLS = baseUrl.startsWith("http://"); -describe.skipIf(platform() === "darwin" || environment.missingServer || missingTLS, "SSL Configuration", function () { +describe.skipIf(platform() === "darwin" || missingTLS, "SSL Configuration", function () { this.longTimeout(); importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); diff --git a/integration-tests/tests/src/node/sync-proxy.ts b/integration-tests/tests/src/node/sync-proxy.ts index 66ad7388df..6d98d0f91c 100644 --- a/integration-tests/tests/src/node/sync-proxy.ts +++ b/integration-tests/tests/src/node/sync-proxy.ts @@ -58,7 +58,7 @@ function getSyncConfiguration(user: Realm.User, partition: string, addProxyConfi return realmConfig; } -describe.skipIf(environment.missingServer, "Proxy support", function () { +describe("Proxy support", function () { let proxyServer: http.Server; let nCalls = 0; diff --git a/integration-tests/tests/src/tests.ts b/integration-tests/tests/src/tests.ts index 289ae5af7a..7a7096dcc5 100644 --- a/integration-tests/tests/src/tests.ts +++ b/integration-tests/tests/src/tests.ts @@ -29,6 +29,7 @@ import "./tests/sync/dictionary"; import "./tests/sync/encryption"; import "./tests/sync/flexible"; import "./tests/sync/geospatial"; +import "./tests/sync/logging"; import "./tests/sync/mixed"; import "./tests/sync/mongo-db-client"; import "./tests/sync/open-behavior"; diff --git a/integration-tests/tests/src/tests/credentials/anonymous.ts b/integration-tests/tests/src/tests/credentials/anonymous.ts index a2b49894bb..1e6305d1e4 100644 --- a/integration-tests/tests/src/tests/credentials/anonymous.ts +++ b/integration-tests/tests/src/tests/credentials/anonymous.ts @@ -21,7 +21,7 @@ import { Credentials, User } from "realm"; import { importAppBefore } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "anonymous credentials", () => { +describe("anonymous credentials", () => { importAppBefore(buildAppConfig("with-anon").anonAuth()); it("authenticates", async function (this: AppContext) { diff --git a/integration-tests/tests/src/tests/credentials/api-key.ts b/integration-tests/tests/src/tests/credentials/api-key.ts index aaafc0301c..62b301af07 100644 --- a/integration-tests/tests/src/tests/credentials/api-key.ts +++ b/integration-tests/tests/src/tests/credentials/api-key.ts @@ -22,7 +22,7 @@ import { importAppBefore } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; //These tests are adopted from api-key-auth.test.ts in the realm-web-integration-tests directory. -describe.skipIf(environment.missingServer, "api-key credentials", () => { +describe("api-key credentials", () => { importAppBefore(buildAppConfig("with-api-key").apiKeyAuth().emailPasswordAuth()); it("lists, creates, gets, enables, authenticates, disables and deletes api keys", async function (this: AppContext) { diff --git a/integration-tests/tests/src/tests/credentials/email-password.ts b/integration-tests/tests/src/tests/credentials/email-password.ts index 7abdbc95f3..2996a5713b 100644 --- a/integration-tests/tests/src/tests/credentials/email-password.ts +++ b/integration-tests/tests/src/tests/credentials/email-password.ts @@ -22,7 +22,7 @@ import { importAppBefore } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; //These tests are adopted from email-password-auth.test.ts in the realm-web-integration-tests directory. -describe.skipIf(environment.missingServer, "email-password credentials", () => { +describe("email-password credentials", () => { importAppBefore(buildAppConfig("with-email-password").emailPasswordAuth()); it("authenticates", async function (this: AppContext) { diff --git a/integration-tests/tests/src/tests/credentials/jwt.ts b/integration-tests/tests/src/tests/credentials/jwt.ts index 37c90cb198..87ae9a8f1c 100644 --- a/integration-tests/tests/src/tests/credentials/jwt.ts +++ b/integration-tests/tests/src/tests/credentials/jwt.ts @@ -26,7 +26,7 @@ import { buildAppConfig } from "../../utils/build-app-config"; const privateKey = "2k66QfKeTRk3MdZ5vpDYgZCu2k66QfKeTRk3MdZ5vpDYgZCu"; -describe.skipIf(environment.missingServer, "jwt credentials", () => { +describe("jwt credentials", () => { importAppBefore( buildAppConfig("with-custom-token").customTokenAuth({ privateKey, diff --git a/integration-tests/tests/src/tests/sync/app.ts b/integration-tests/tests/src/tests/sync/app.ts index 57dfd30786..47ea6a7686 100644 --- a/integration-tests/tests/src/tests/sync/app.ts +++ b/integration-tests/tests/src/tests/sync/app.ts @@ -21,7 +21,7 @@ import Realm, { AppConfiguration, BSON, MetadataMode } from "realm"; import { importAppBefore } from "../../hooks"; import { generatePartition } from "../../utils/generators"; -import { baseUrl } from "../../utils/import-app"; +import { baseUrl } from "../../hooks/import-app-before"; import { select } from "../../utils/select"; import { buildAppConfig } from "../../utils/build-app-config"; @@ -74,12 +74,13 @@ interface IDogForSyncSchema { realm_id: string | undefined; } +const missingAppConfig = { id: "smurf", baseUrl }; + describe("App", () => { describe("instantiation", function () { afterEach(async () => { Realm.clearTestState(); }); - const missingAppConfig = { id: "smurf", baseUrl }; it("from config", () => { //even if "id" is not an existing app we can still instantiate a new Realm. @@ -125,12 +126,6 @@ describe("App", () => { ); }); - it.skipIf(environment.missingServer, "logging in throws on non existing app", async function () { - const app = new Realm.App(missingAppConfig); - const credentials = Realm.Credentials.anonymous(); - await expect(app.logIn(credentials)).to.be.rejectedWith("cannot find app using Client App ID 'smurf'"); - }); - it("get returns cached app", () => { const app = Realm.App.get(missingAppConfig.id); const cachedApp = Realm.App.get(missingAppConfig.id); @@ -178,8 +173,15 @@ describe("App", () => { }); }); - describe.skipIf(environment.missingServer, "with valid app", async () => { - importAppBefore(buildAppConfig("with-anon").anonAuth()); + describe("with valid app", async () => { + importAppBefore(buildAppConfig().anonAuth()); + + it("logging in throws on non existing app", async function () { + // This test is moved here to ensure the server is available before connecting with a non-exisiting app id + const app = new Realm.App(missingAppConfig); + const credentials = Realm.Credentials.anonymous(); + await expect(app.logIn(credentials)).to.be.rejectedWith("cannot find app using Client App ID 'smurf'"); + }); it("logins successfully ", async function (this: Mocha.Context & AppContext & RealmContext) { let user; diff --git a/integration-tests/tests/src/tests/sync/asymmetric.ts b/integration-tests/tests/src/tests/sync/asymmetric.ts index 8e99ce4bc7..9e1cfe6d33 100644 --- a/integration-tests/tests/src/tests/sync/asymmetric.ts +++ b/integration-tests/tests/src/tests/sync/asymmetric.ts @@ -22,7 +22,7 @@ import Realm, { BSON } from "realm"; import { authenticateUserBefore, importAppBefore, openRealmBeforeEach } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Asymmetric sync", function () { +describe("Asymmetric sync", function () { [true, false].forEach((embeddedAndAsymmetric) => { describe(`Configuration and schema (embedded = ${embeddedAndAsymmetric})`, function () { this.longTimeout(); diff --git a/integration-tests/tests/src/tests/sync/client-reset.ts b/integration-tests/tests/src/tests/sync/client-reset.ts index ed191858e7..8a3a4bf850 100644 --- a/integration-tests/tests/src/tests/sync/client-reset.ts +++ b/integration-tests/tests/src/tests/sync/client-reset.ts @@ -296,21 +296,73 @@ function getSchema(useFlexibleSync: boolean) { // FIXME: ngrok reports "Syntax Error" when tiggerClientResetFunction() is used. // Once ngrok behaves nicely, the skipped tests can be enabled. [false /*, true*/].forEach((useFlexibleSync) => { - describe.skipIf( - environment.missingServer, - `client reset handling (${getPartialTestTitle(useFlexibleSync)} sync)`, - function () { - this.longTimeout(); // client reset with flexible sync can take quite some time - importAppBefore( - useFlexibleSync - ? buildAppConfig("with-flx").anonAuth().flexibleSync() /* .triggerClientResetFunction() */ - : buildAppConfig("with-pbs").anonAuth().partitionBasedSync() /* .triggerClientResetFunction() */, + describe(`client reset handling (${getPartialTestTitle(useFlexibleSync)} sync)`, function () { + this.longTimeout(); // client reset with flexible sync can take quite some time + importAppBefore( + useFlexibleSync + ? buildAppConfig("with-flx").anonAuth().flexibleSync() /* .triggerClientResetFunction() */ + : buildAppConfig("with-pbs").anonAuth().partitionBasedSync() /* .triggerClientResetFunction() */, + ); + authenticateUserBefore(); + + it(`manual client reset requires either error handler, client reset callback or both (${getPartialTestTitle( + useFlexibleSync, + )} sync)`, async function (this: RealmContext) { + const config: ConfigurationWithSync = { + schema: getSchema(useFlexibleSync), + sync: { + // @ts-expect-error this setting is not for users to consume + _sessionStopPolicy: SessionStopPolicy.Immediately, + ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), + user: this.user, + clientReset: { + mode: ClientResetMode.Manual, + }, + }, + }; + + expect(() => new Realm(config)).throws(); + }); + + it(`handles manual simulated client resets with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + const config: ConfigurationWithSync = { + schema: getSchema(useFlexibleSync), + sync: { + //@ts-expect-error Internal field + _sessionStopPolicy: SessionStopPolicy.Immediately, + ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), + user: this.user, + clientReset: { + mode: ClientResetMode.Manual, + }, + }, + }; + await expectClientResetError( + config, + this.user, + (realm) => { + if (useFlexibleSync) { + addSubscriptions(realm); + } + const session = realm.syncSession; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore calling undocumented method _simulateError + session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", false); // 211 -> diverging histories + }, + (error: SyncError) => { + expect(error.name).to.equal("ClientReset"); + expect(error.message).to.equal("Simulate Client Reset"); + expect(error.code).to.equal(1032); // diverging client will cause a client reset (error code 1032) + }, ); - authenticateUserBefore(); + }); - it(`manual client reset requires either error handler, client reset callback or both (${getPartialTestTitle( - useFlexibleSync, - )} sync)`, async function (this: RealmContext) { + it(`handles manual simulated client resets by callback with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + return new Promise((resolve) => { const config: ConfigurationWithSync = { schema: getSchema(useFlexibleSync), sync: { @@ -320,251 +372,195 @@ function getSchema(useFlexibleSync: boolean) { user: this.user, clientReset: { mode: ClientResetMode.Manual, + onManual: (session, path) => { + expect(session).to.be.not.null; + expect(path).to.not.empty; + resolve(); + }, }, }, }; - expect(() => new Realm(config)).throws(); + const realm = new Realm(config); + if (useFlexibleSync) { + addSubscriptions(realm); + } + const session = realm.syncSession; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore calling undocumented method _simulateError + session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 211 -> diverging histories }); + }); - it(`handles manual simulated client resets with ${getPartialTestTitle( - useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { + it(`handles manual simulated client resets by callback from error handler with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + return new Promise((resolve, reject) => { const config: ConfigurationWithSync = { schema: getSchema(useFlexibleSync), sync: { - //@ts-expect-error Internal field + // @ts-expect-error this setting is not for users to consume _sessionStopPolicy: SessionStopPolicy.Immediately, ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), user: this.user, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onError: (_) => { + reject(); + }, clientReset: { mode: ClientResetMode.Manual, + onManual: (session, path) => { + expect(session).to.be.not.null; + expect(path).to.not.empty; + resolve(); + }, }, }, }; - await expectClientResetError( - config, - this.user, - (realm) => { - if (useFlexibleSync) { - addSubscriptions(realm); - } - const session = realm.syncSession; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore calling undocumented method _simulateError - session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", false); // 211 -> diverging histories - }, - (error: SyncError) => { - expect(error.name).to.equal("ClientReset"); - expect(error.message).to.equal("Simulate Client Reset"); - expect(error.code).to.equal(1032); // diverging client will cause a client reset (error code 1032) - }, - ); - }); - - it(`handles manual simulated client resets by callback with ${getPartialTestTitle( - useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - return new Promise((resolve) => { - const config: ConfigurationWithSync = { - schema: getSchema(useFlexibleSync), - sync: { - // @ts-expect-error this setting is not for users to consume - _sessionStopPolicy: SessionStopPolicy.Immediately, - ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), - user: this.user, - clientReset: { - mode: ClientResetMode.Manual, - onManual: (session, path) => { - expect(session).to.be.not.null; - expect(path).to.not.empty; - resolve(); - }, - }, - }, - }; - const realm = new Realm(config); - if (useFlexibleSync) { - addSubscriptions(realm); - } - const session = realm.syncSession; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore calling undocumented method _simulateError - session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 211 -> diverging histories - }); + const realm = new Realm(config); + const session = realm.syncSession; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore calling undocumented method _simulateError + session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 211 -> diverging histories }); + }); - it(`handles manual simulated client resets by callback from error handler with ${getPartialTestTitle( - useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - return new Promise((resolve, reject) => { - const config: ConfigurationWithSync = { - schema: getSchema(useFlexibleSync), - sync: { - // @ts-expect-error this setting is not for users to consume - _sessionStopPolicy: SessionStopPolicy.Immediately, - ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), - user: this.user, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onError: (_) => { - reject(); - }, - clientReset: { - mode: ClientResetMode.Manual, - onManual: (session, path) => { - expect(session).to.be.not.null; - expect(path).to.not.empty; - resolve(); - }, - }, - }, - }; + it(`client reset fails, the error handler is called (${getPartialTestTitle( + useFlexibleSync, + )})`, async function (this: RealmContext) { + // if client reset fails, the error handler is called + // and the two before/after handlers are not called + // we simulate the failure by error code 132") - const realm = new Realm(config); - const session = realm.syncSession; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore calling undocumented method _simulateError - session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 211 -> diverging histories - }); - }); - - it(`client reset fails, the error handler is called (${getPartialTestTitle( - useFlexibleSync, - )})`, async function (this: RealmContext) { - // if client reset fails, the error handler is called - // and the two before/after handlers are not called - // we simulate the failure by error code 132") - - return new Promise((resolve, reject) => { - const config: Configuration = { - schema: getSchema(useFlexibleSync), - sync: { - user: this.user, - ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), - onError: () => { - resolve(); + return new Promise((resolve, reject) => { + const config: Configuration = { + schema: getSchema(useFlexibleSync), + sync: { + user: this.user, + ...(useFlexibleSync ? { flexible: true } : { partitionValue: getPartitionValue() }), + onError: () => { + resolve(); + }, + clientReset: { + mode: ClientResetMode.DiscardUnsyncedChanges, + onBefore: () => { + reject(); }, - clientReset: { - mode: ClientResetMode.DiscardUnsyncedChanges, - onBefore: () => { - reject(); - }, - onAfter: () => { - reject(); - }, + onAfter: () => { + reject(); }, }, - }; - - const realm = new Realm(config); - if (useFlexibleSync) { - addSubscriptions(realm); - } - const session = realm.syncSession; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore calling undocumented method _simulateError - session._simulateError(132, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 132 -> automatic client reset failed - }); - }); - - it(`handles discard local simulated client reset with ${getPartialTestTitle( - useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - // (i) using a client reset in "DiscardUnsyncedChanges" mode, a fresh copy - // of the Realm will be downloaded (resync) - // (ii) two callback will be called, while the sync error handler is not - // (iii) after the reset, the Realm can be used as before - - const clientResetBefore = (realm: Realm) => { - expect(realm.schema.length).to.equal(2); - }; - const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { - expect(beforeRealm.schema.length).to.equal(2); - expect(afterRealm.schema.length).to.equal(2); + }, }; - await waitSimulatedClientResetDiscardUnsyncedChangesCallbacks( - useFlexibleSync, - getSchema(useFlexibleSync), - this.user, - clientResetBefore, - clientResetAfter, - ); + const realm = new Realm(config); + if (useFlexibleSync) { + addSubscriptions(realm); + } + const session = realm.syncSession; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore calling undocumented method _simulateError + session._simulateError(132, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 132 -> automatic client reset failed }); - - it(`handles simulated client reset with recovery with ${getPartialTestTitle( + }); + + it(`handles discard local simulated client reset with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + // (i) using a client reset in "DiscardUnsyncedChanges" mode, a fresh copy + // of the Realm will be downloaded (resync) + // (ii) two callback will be called, while the sync error handler is not + // (iii) after the reset, the Realm can be used as before + + const clientResetBefore = (realm: Realm) => { + expect(realm.schema.length).to.equal(2); + }; + const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { + expect(beforeRealm.schema.length).to.equal(2); + expect(afterRealm.schema.length).to.equal(2); + }; + + await waitSimulatedClientResetDiscardUnsyncedChangesCallbacks( useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - const clientResetBefore = (realm: Realm): void => { - expect(realm.schema.length).to.equal(2); - }; - const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { - expect(beforeRealm.schema.length).to.equal(2); - expect(afterRealm.schema.length).to.equal(2); - }; - - await waitSimulatedClientResetRecoverCallbacks( - useFlexibleSync, - getSchema(useFlexibleSync), - this.user, - clientResetBefore, - clientResetAfter, - ); - }); - - it.skip(`handles discard local client reset with ${getPartialTestTitle( + getSchema(useFlexibleSync), + this.user, + clientResetBefore, + clientResetAfter, + ); + }); + + it(`handles simulated client reset with recovery with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + const clientResetBefore = (realm: Realm): void => { + expect(realm.schema.length).to.equal(2); + }; + const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { + expect(beforeRealm.schema.length).to.equal(2); + expect(afterRealm.schema.length).to.equal(2); + }; + + await waitSimulatedClientResetRecoverCallbacks( useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - // (i) using a client reset in "DiscardUnsyncedChanges" mode, a fresh copy - // of the Realm will be downloaded (resync) - // (ii) two callback will be called, while the sync error handler is not - // (iii) after the reset, the Realm can be used as before - - const clientResetBefore = (realm: Realm) => { - expect(realm.schema.length).to.equal(2); - }; - const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { - expect(beforeRealm.schema.length).to.equal(2); - expect(afterRealm.schema.length).to.equal(2); - }; - - await waitServerSideClientResetDiscardUnsyncedChangesCallbacks( - useFlexibleSync, - getSchema(useFlexibleSync), - this.app, - this.user, - clientResetBefore, - clientResetAfter, - ); - }); - - it.skip(`handles recovery client reset with ${getPartialTestTitle( + getSchema(useFlexibleSync), + this.user, + clientResetBefore, + clientResetAfter, + ); + }); + + it.skip(`handles discard local client reset with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + // (i) using a client reset in "DiscardUnsyncedChanges" mode, a fresh copy + // of the Realm will be downloaded (resync) + // (ii) two callback will be called, while the sync error handler is not + // (iii) after the reset, the Realm can be used as before + + const clientResetBefore = (realm: Realm) => { + expect(realm.schema.length).to.equal(2); + }; + const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { + expect(beforeRealm.schema.length).to.equal(2); + expect(afterRealm.schema.length).to.equal(2); + }; + + await waitServerSideClientResetDiscardUnsyncedChangesCallbacks( useFlexibleSync, - )} sync enabled`, async function (this: RealmContext) { - // (i) using a client reset in "Recovery" mode, a fresh copy - // of the Realm will be downloaded (resync) - // (ii) two callback will be called, while the sync error handler is not - // (iii) after the reset, the Realm can be used as before - this.timeout(5 * 60 * 1000); - this.retries(3); - const clientResetBefore = (realm: Realm) => { - expect(realm.schema.length).to.equal(2); - }; - const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { - expect(beforeRealm.schema.length).to.equal(2); - expect(afterRealm.schema.length).to.equal(2); - }; - - await waitServerSideClientResetRecoveryCallbacks( - useFlexibleSync, - getSchema(useFlexibleSync), - this.app, - this.user, - clientResetBefore, - clientResetAfter, - ); - }); - }, - ); + getSchema(useFlexibleSync), + this.app, + this.user, + clientResetBefore, + clientResetAfter, + ); + }); + + it.skip(`handles recovery client reset with ${getPartialTestTitle( + useFlexibleSync, + )} sync enabled`, async function (this: RealmContext) { + // (i) using a client reset in "Recovery" mode, a fresh copy + // of the Realm will be downloaded (resync) + // (ii) two callback will be called, while the sync error handler is not + // (iii) after the reset, the Realm can be used as before + this.timeout(5 * 60 * 1000); + this.retries(3); + const clientResetBefore = (realm: Realm) => { + expect(realm.schema.length).to.equal(2); + }; + const clientResetAfter = (beforeRealm: Realm, afterRealm: Realm) => { + expect(beforeRealm.schema.length).to.equal(2); + expect(afterRealm.schema.length).to.equal(2); + }; + + await waitServerSideClientResetRecoveryCallbacks( + useFlexibleSync, + getSchema(useFlexibleSync), + this.app, + this.user, + clientResetBefore, + clientResetAfter, + ); + }); + }); }); diff --git a/integration-tests/tests/src/tests/sync/dictionary.ts b/integration-tests/tests/src/tests/sync/dictionary.ts index 0ad30ad1e2..e2ac89e545 100644 --- a/integration-tests/tests/src/tests/sync/dictionary.ts +++ b/integration-tests/tests/src/tests/sync/dictionary.ts @@ -22,7 +22,7 @@ import { expectDecimalEqual } from "../../utils/comparisons"; import { itUploadsDeletesAndDownloads } from "./upload-delete-download"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Type roundtrip of Dictionary object", () => { +describe("Type roundtrip of Dictionary object", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/encryption.ts b/integration-tests/tests/src/tests/sync/encryption.ts index d1530c04f1..1b225ad850 100644 --- a/integration-tests/tests/src/tests/sync/encryption.ts +++ b/integration-tests/tests/src/tests/sync/encryption.ts @@ -83,7 +83,7 @@ describe("Encryption", () => { }); }); - describe.skipIf(environment.missingServer, "with sync", () => { + describe("with sync", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); it("can set property in config", async function (this: AppContext) { diff --git a/integration-tests/tests/src/tests/sync/flexible.ts b/integration-tests/tests/src/tests/sync/flexible.ts index fe62d99185..fe97d1b20a 100644 --- a/integration-tests/tests/src/tests/sync/flexible.ts +++ b/integration-tests/tests/src/tests/sync/flexible.ts @@ -172,7 +172,7 @@ async function addSubscriptionAndSync>( return { subs, sub, query }; } -describe.skipIf(environment.missingServer, "Flexible sync", function () { +describe("Flexible sync", function () { this.timeout(60_000); // TODO: Temporarily hardcoded until envs are set up. importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/geospatial.ts b/integration-tests/tests/src/tests/sync/geospatial.ts index 8089e1e432..d46577ca31 100644 --- a/integration-tests/tests/src/tests/sync/geospatial.ts +++ b/integration-tests/tests/src/tests/sync/geospatial.ts @@ -156,7 +156,7 @@ const expectQueryResultValues = ( ); }; -describe.skipIf(environment.missingServer, `GeoSpatial`, () => { +describe(`GeoSpatial`, () => { importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/logging.ts b/integration-tests/tests/src/tests/sync/logging.ts new file mode 100644 index 0000000000..cf11a5cffa --- /dev/null +++ b/integration-tests/tests/src/tests/sync/logging.ts @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 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. +// +//////////////////////////////////////////////////////////////////////////// + +import Realm from "realm"; +import { importAppBefore } from "../../hooks"; +import { buildAppConfig } from "../../utils/build-app-config"; + +describe("Logging", () => { + importAppBefore(buildAppConfig("with-pbs").anonAuth().flexibleSync()); + afterEach(() => Realm.clearTestState()); + // Skipped because reusing a single app across tests break this + it("can set custom logging function", async function (this: AppContext) { + const credentials = Realm.Credentials.anonymous(); + + const logLevelStr = "info"; // "all", "trace", "debug", "detail", "info", "warn", "error", "fatal", "off" + const logLevelNum = 4; // == "info", see index.d.ts, logger.hpp for definitions + + const promisedLog = new Promise((resolve) => { + Realm.App.Sync.setLogLevel(this.app, logLevelStr); + Realm.App.Sync.setLogger(this.app, (level, message) => { + if (level == logLevelNum && message.includes("Connection") && message.includes("Session")) { + // we should, at some point, receive a log message that looks like + // Connection[1]: Session[1]: client_reset_config = false, Realm exists = true, client reset = false + resolve(true); + } + }); + }); + + const user = await this.app.logIn(credentials); + await Realm.open({ sync: { user, flexible: true } }); + await promisedLog; + }); +}); diff --git a/integration-tests/tests/src/tests/sync/mixed.ts b/integration-tests/tests/src/tests/sync/mixed.ts index 7bdf4c591f..1e38a2c85a 100644 --- a/integration-tests/tests/src/tests/sync/mixed.ts +++ b/integration-tests/tests/src/tests/sync/mixed.ts @@ -229,7 +229,7 @@ function describeTypes(flexibleSync: boolean) { }); } -describe.skipIf(environment.missingServer, "mixed", () => { +describe("mixed", () => { describe("partition-based sync roundtrip", function () { this.longTimeout(); importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); diff --git a/integration-tests/tests/src/tests/sync/mongo-db-client.ts b/integration-tests/tests/src/tests/sync/mongo-db-client.ts index dc47f5a45c..9fafd020f0 100644 --- a/integration-tests/tests/src/tests/sync/mongo-db-client.ts +++ b/integration-tests/tests/src/tests/sync/mongo-db-client.ts @@ -41,7 +41,7 @@ type TestContext = CollectionContext & AppContext & UserContext & Mocha.Context; const serviceName = "mongodb"; const collectionName = "test-collection"; -describe.skipIf(environment.missingServer, "MongoDB Client", function () { +describe("MongoDB Client", function () { this.timeout(60_000); // TODO: Temporarily hardcoded until envs are set up. importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/open-behavior.ts b/integration-tests/tests/src/tests/sync/open-behavior.ts index 062e55bb20..27352b3f79 100644 --- a/integration-tests/tests/src/tests/sync/open-behavior.ts +++ b/integration-tests/tests/src/tests/sync/open-behavior.ts @@ -46,7 +46,7 @@ async function getRegisteredEmailPassCredentials(app: Realm.App) { return Realm.Credentials.emailPassword(email, password); } -describe.skipIf(environment.missingServer, "OpenBehaviour", function () { +describe("OpenBehaviour", function () { this.longTimeout(); importAppBefore(buildAppConfig("with-pbs").anonAuth().emailPasswordAuth().partitionBasedSync()); afterEach(() => Realm.clearTestState()); diff --git a/integration-tests/tests/src/tests/sync/open.ts b/integration-tests/tests/src/tests/sync/open.ts index 8b721695dc..bdb8f78fd5 100644 --- a/integration-tests/tests/src/tests/sync/open.ts +++ b/integration-tests/tests/src/tests/sync/open.ts @@ -22,7 +22,7 @@ import Realm from "realm"; import { authenticateUserBefore, importAppBefore } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Realm.open on a sync Realm", () => { +describe("Realm.open on a sync Realm", () => { importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/partition-values.ts b/integration-tests/tests/src/tests/sync/partition-values.ts index 00d3eaa157..14d3d69f7d 100644 --- a/integration-tests/tests/src/tests/sync/partition-values.ts +++ b/integration-tests/tests/src/tests/sync/partition-values.ts @@ -76,7 +76,7 @@ const createConfig = (schema: Realm.ObjectSchema, user: Realm.User, partitionVal }, }); -describe.skipIf(environment.missingServer, "Partition-values", () => { +describe("Partition-values", () => { describe("setting partition value on config", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); afterEach(() => Realm.clearTestState()); diff --git a/integration-tests/tests/src/tests/sync/realm.ts b/integration-tests/tests/src/tests/sync/realm.ts index 725e7d2f13..40d697aa5e 100644 --- a/integration-tests/tests/src/tests/sync/realm.ts +++ b/integration-tests/tests/src/tests/sync/realm.ts @@ -1361,7 +1361,7 @@ describe("Realmtest", () => { }); }); - describe.skipIf(environment.missingServer, "schemaVersion", () => { + describe("schemaVersion", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); [true, false].forEach((encryption) => { @@ -1380,7 +1380,7 @@ describe("Realmtest", () => { }); }); - describe.skipIf(environment.missingServer, "exists", () => { + describe("exists", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); it("yields correct value on a local realm", () => { @@ -1391,7 +1391,7 @@ describe("Realmtest", () => { expect(Realm.exists(config)).to.be.true; }); - it.skipIf(environment.missingServerm, "yields correct value on a synced realm", function (this: AppContext) { + it("yields correct value on a synced realm", function (this: AppContext) { const credentials = Realm.Credentials.anonymous(); return this.app.logIn(credentials).then((user) => { @@ -2108,7 +2108,7 @@ describe("Realmtest", () => { }); }); - describe.skipIf(environment.missingServer, "with sync", () => { + describe("with sync", () => { importAppBefore(buildAppConfig("with-anon").anonAuth().partitionBasedSync()); it("data is deleted on realm with custom path", function (this: RealmContext & AppContext) { diff --git a/integration-tests/tests/src/tests/sync/set.ts b/integration-tests/tests/src/tests/sync/set.ts index 423fd84866..0167468783 100644 --- a/integration-tests/tests/src/tests/sync/set.ts +++ b/integration-tests/tests/src/tests/sync/set.ts @@ -21,7 +21,7 @@ import { authenticateUserBefore, importAppBefore, openRealmBefore } from "../../ import { itUploadsDeletesAndDownloads } from "./upload-delete-download"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Type roundtrip of set object", () => { +describe("Type roundtrip of set object", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/sync-as-local.ts b/integration-tests/tests/src/tests/sync/sync-as-local.ts index 92a9b45ac9..56bfae8303 100644 --- a/integration-tests/tests/src/tests/sync/sync-as-local.ts +++ b/integration-tests/tests/src/tests/sync/sync-as-local.ts @@ -23,7 +23,7 @@ import { PersonSchema, IPerson } from "../../schemas/person-and-dog-with-object- import { authenticateUserBefore, importAppBefore, openRealmBefore } from "../../hooks"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Synced Realm as local", function () { +describe("Synced Realm as local", function () { this.timeout(60_000); // TODO: Temporarily hardcoded until envs are set up. importAppBefore(buildAppConfig("with-flx").anonAuth().flexibleSync()); authenticateUserBefore(); diff --git a/integration-tests/tests/src/tests/sync/sync-session.ts b/integration-tests/tests/src/tests/sync/sync-session.ts index b0301741b0..690cbde2f5 100644 --- a/integration-tests/tests/src/tests/sync/sync-session.ts +++ b/integration-tests/tests/src/tests/sync/sync-session.ts @@ -17,12 +17,11 @@ //////////////////////////////////////////////////////////////////////////// import { expect } from "chai"; -import Realm, { ConnectionState, ObjectSchema, BSON, User, SyncConfiguration } from "realm"; +import Realm, { ConnectionState, ObjectSchema, BSON, SyncConfiguration } from "realm"; import { importAppBefore } from "../../hooks"; import { DogSchema } from "../../schemas/person-and-dog-with-object-ids"; import { getRegisteredEmailPassCredentials } from "../../utils/credentials"; import { generatePartition } from "../../utils/generators"; -import { importApp } from "../../utils/import-app"; import { sleep, throwAfterTimeout } from "../../utils/sleep"; import { buildAppConfig } from "../../utils/build-app-config"; @@ -124,7 +123,7 @@ async function seedDataWithExternalUser(app: Realm.App, partition: string) { user.logOut(); } -describe.skipIf(environment.missingServer, "SessionTest", () => { +describe("SessionTest", () => { importAppBefore(buildAppConfig("with-pbs").emailPasswordAuth().partitionBasedSync({ required: true })); describe("invalid syncsessions", () => { @@ -345,39 +344,6 @@ describe.skipIf(environment.missingServer, "SessionTest", () => { }); }); - describe("Logging", () => { - afterEach(() => Realm.clearTestState()); - // Skipped because reusing a single app across tests break this - it.skip("can set custom logging function", async function (this: AppContext) { - // setting a custom logging function must be done immediately after instantiating an app - - const { appId, baseUrl } = await importApp(buildAppConfig("with-pbs").anonAuth().partitionBasedSync().config); - const app = new Realm.App({ id: appId, baseUrl }); - - const partition = generatePartition(); - const credentials = Realm.Credentials.anonymous(); - - const logLevelStr = "info"; // "all", "trace", "debug", "detail", "info", "warn", "error", "fatal", "off" - const logLevelNum = 4; // == "info", see index.d.ts, logger.hpp for definitions - - const promisedLog = new Promise((resolve) => { - Realm.App.Sync.setLogLevel(app, logLevelStr); - Realm.App.Sync.setLogger(app, (level, message) => { - if (level == logLevelNum && message.includes("Connection") && message.includes("Session")) { - // we should, at some point, receive a log message that looks like - // Connection[1]: Session[1]: client_reset_config = false, Realm exists = true, client reset = false - resolve(true); - } - }); - }); - - const user = await app.logIn(credentials); - const config = getSyncConfiguration(user, partition); - await Realm.open(config); - await promisedLog; - }); - }); - describe("Connection", () => { afterEach(() => Realm.clearTestState()); it("can add connectionNotification", async function (this: AppContext) { diff --git a/integration-tests/tests/src/tests/sync/user.ts b/integration-tests/tests/src/tests/sync/user.ts index 701b1084fb..e35d6e9707 100644 --- a/integration-tests/tests/src/tests/sync/user.ts +++ b/integration-tests/tests/src/tests/sync/user.ts @@ -27,7 +27,7 @@ import { randomVerifiableEmail, } from "../../utils/generators"; import { buildAppConfig } from "../../utils/build-app-config"; -import { baseUrl } from "../../utils/import-app"; +import { baseUrl } from "../../hooks/import-app-before"; type AnyApp = Realm.App; type AnyUser = Realm.User; @@ -78,7 +78,7 @@ function removeExistingUsers(): void { }); } -describe.skipIf(environment.missingServer, "User", () => { +describe("User", () => { describe("email password", () => { importAppBefore(buildAppConfig("with-email-password").emailPasswordAuth()); removeExistingUsers(); diff --git a/integration-tests/tests/src/tests/sync/uuid.ts b/integration-tests/tests/src/tests/sync/uuid.ts index f0debdc2c5..7ce44de73c 100644 --- a/integration-tests/tests/src/tests/sync/uuid.ts +++ b/integration-tests/tests/src/tests/sync/uuid.ts @@ -21,7 +21,7 @@ import { authenticateUserBefore, importAppBefore, openRealmBefore } from "../../ import { itUploadsDeletesAndDownloads } from "./upload-delete-download"; import { buildAppConfig } from "../../utils/build-app-config"; -describe.skipIf(environment.missingServer, "Type roundtrip of UUID object", () => { +describe("Type roundtrip of UUID object", () => { importAppBefore(buildAppConfig("with-pbs").anonAuth().partitionBasedSync()); authenticateUserBefore(); const { UUID } = Realm.BSON; diff --git a/integration-tests/tests/src/typings.d.ts b/integration-tests/tests/src/typings.d.ts index 7be39d8424..7e949a9571 100644 --- a/integration-tests/tests/src/typings.d.ts +++ b/integration-tests/tests/src/typings.d.ts @@ -50,9 +50,9 @@ type KnownEnvironment = { // BaaS server and Realm App Importer specific variables below /** Are the tests running without a server? In which case all sync tests should be skipped. */ - missingServer?: true; + missingServer?: boolean; /** The URL of the Realm server to run tests against. */ - realmBaseUrl?: string; + baseUrl?: string; /** * Public key part used when authenticating towards BaaS during import of an app. * Note: This is only used when the app importer is ran from within the test suite. diff --git a/integration-tests/tests/src/utils/import-app.test.ts b/integration-tests/tests/src/utils/import-app.test.ts deleted file mode 100644 index ceaecb7d9a..0000000000 --- a/integration-tests/tests/src/utils/import-app.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 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. -// -//////////////////////////////////////////////////////////////////////////// -import { App } from "realm"; -import { expect } from "chai"; - -import { deleteApp, importApp } from "./import-app"; -import { buildAppConfig } from "./build-app-config"; - -describe.skipIf(environment.missingServer, "importApp utility", function () { - this.slow(2000); - - it("can import and delete an app", async () => { - const { appId, baseUrl } = await importApp(buildAppConfig("simple").config); - try { - const app = new App({ id: appId, baseUrl }); - expect(app).instanceOf(App); - } finally { - await deleteApp(appId); - } - }).timeout(2 * 60 * 1000); // This may take a long time when running against a real server -}); diff --git a/integration-tests/tests/src/utils/import-app.ts b/integration-tests/tests/src/utils/import-app.ts deleted file mode 100644 index 276b0f2a9b..0000000000 --- a/integration-tests/tests/src/utils/import-app.ts +++ /dev/null @@ -1,62 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 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. -// -//////////////////////////////////////////////////////////////////////////// - -import { AppConfig, AppImporter, Credentials } from "@realm/app-importer"; - -export type TemplateReplacements = Record>; -export type ErrorResponse = { message: string; appId: never }; -export type ImportResponse = { appId: string; message: never }; -export type Response = ImportResponse | ErrorResponse; - -const { realmBaseUrl = "http://localhost:9090", reuseApp = false } = environment; - -export const baseUrl = realmBaseUrl; - -function getCredentials(): Credentials { - const { publicKey, privateKey, username = "unique_user@domain.com", password = "password" } = environment; - if (typeof publicKey === "string" && typeof privateKey === "string") { - return { - kind: "api-key", - publicKey, - privateKey, - }; - } else { - return { - kind: "username-password", - username, - password, - }; - } -} - -const credentials = getCredentials(); - -const importer = new AppImporter({ - baseUrl: realmBaseUrl, - credentials, - reuseApp, -}); - -export async function importApp(config: AppConfig): Promise<{ appId: string; baseUrl: string }> { - const { appId } = await importer.importApp(config); - return { appId, baseUrl: realmBaseUrl }; -} - -export async function deleteApp(clientAppId: string): Promise { - await importer.deleteApp(clientAppId); -} diff --git a/integration-tests/tests/tsconfig.json b/integration-tests/tests/tsconfig.json index e314449020..8af0a6a999 100644 --- a/integration-tests/tests/tsconfig.json +++ b/integration-tests/tests/tsconfig.json @@ -10,7 +10,7 @@ "resolveJsonModule": true, "outDir": "dist", "lib": [ - "es2020" + "es2022" ], "types": [ "realm", diff --git a/packages/realm-react/src/__tests__/helpers.ts b/packages/realm-react/src/__tests__/helpers.ts index a0cfe9832d..5c03118827 100644 --- a/packages/realm-react/src/__tests__/helpers.ts +++ b/packages/realm-react/src/__tests__/helpers.ts @@ -27,9 +27,11 @@ const { PRIVATE_KEY: privateKey, USERNAME: username = "unique_user@domain.com", PASSWORD: password = "password", - REALM_BASE_URL: realmBaseUrl = "http://localhost:9090", + REALM_BASE_URL: baseUrl = "http://localhost:9090", } = process.env; +export { baseUrl }; + export async function testAuthOperation({ authOperation, expectedResult, @@ -45,8 +47,6 @@ export async function testAuthOperation({ }); } -export const baseUrl = realmBaseUrl; - function getCredentials(): Credentials { if (typeof publicKey === "string" && typeof privateKey === "string") { return {