Skip to content

Commit

Permalink
Migrate existing client reset tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kneth committed Sep 22, 2022
1 parent 50c5642 commit c3c2c07
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 123 deletions.
1 change: 1 addition & 0 deletions integration-tests/tests/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ import "./sync/flexible";
import "./sync/asymmetric";
import "./sync/sync-as-local";
import "./transaction";
import "./sync/client-reset";
150 changes: 150 additions & 0 deletions integration-tests/tests/src/tests/sync/client-reset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 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 { ObjectId } from "bson";
import { expect } from "chai";
import Realm, { BSON, ClientResetMode, SessionStopPolicy } from "realm";
import { authenticateUserBefore, importAppBefore } from "../../hooks";
import { Dog, DogSchema, PersonSchema } from "../../schemas/person-and-dog-with-object-ids";
import { delay } from "../../utils/delay";

import { expectClientResetError } from "../../utils/expect-sync-error";
import { openRealm } from "../../utils/open-realm";

describe.skipIf(environment.missingServer, "client reset handling", function () {
importAppBefore("with-db");
authenticateUserBefore();

it("handles manual client resets with partition-based sync enabled", async function (this: RealmContext) {
await expectClientResetError(
{
schema: [PersonSchema, DogSchema],
sync: {
_sessionStopPolicy: SessionStopPolicy.Immediately,
partitionValue: "client-reset-test",
user: this.user,
clientReset: {
mode: ClientResetMode.Manual,
},
},
},
this.user,
(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) => {
expect(error.name).to.equal("ClientReset");
expect(error.message).to.equal("Simulate Client Reset");
expect(error.code).to.equal(211);
},
);
});

it("client reset fails, the error handler is called", 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")

let beforeCalled = false;
let afterCalled = false;
let errorCalled = false;

const { config, realm } = await openRealm(
{
schema: [PersonSchema, DogSchema],
sync: {
partitionValue: "client-reset-test",
error: () => {
errorCalled = true;
},
clientReset: {
mode: ClientResetMode.DiscardLocal,
clientResetBefore: () => {
beforeCalled = true;
},
clientResetAfter: () => {
afterCalled = true;
},
},
},
},
this.user,
);

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

await delay(1000);

expect(beforeCalled).to.be.false;
expect(afterCalled).to.be.false;
expect(errorCalled).to.be.true;
});

it("handles discard local client reset with partition-based sync enabled", async function (this: RealmContext) {
// (i) using a client reset in "DiscardLocal" 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

let beforeCalled = false;
let afterCalled = false;

const { config, realm } = await openRealm(
{
schema: [PersonSchema, DogSchema],
sync: {
partitionValue: "client-reset-test",
clientReset: {
mode: ClientResetMode.DiscardLocal,
clientResetBefore: (realm) => {
beforeCalled = true;
expect(realm.objects(DogSchema.name).length).to.equal(1);
},
clientResetAfter: (beforeRealm, afterRealm) => {
afterCalled = true;
expect(beforeRealm.objects(DogSchema.name).length).to.equal(1);
expect(afterRealm.objects(DogSchema.name).length).to.equal(1);
},
},
},
},
this.user,
);

realm.write(() => {
realm.create(DogSchema.name, { _id: new ObjectId(), name: "Lassy", age: 5 });
});

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

await delay(1000);

expect(beforeCalled).to.be.true;
expect(afterCalled).to.be.true;
expect(realm.objects(DogSchema.name).length).to.equal(1);
});
});
2 changes: 1 addition & 1 deletion integration-tests/tests/src/tests/sync/flexible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1627,7 +1627,7 @@ describe.skipIf(environment.missingServer, "Flexible sync", function () {
});
});

describe("client reset handling", function () {
describe("client reset handling for flexible sync", function () {
it("handles manual client resets with flexible sync enabled", async function (this: RealmContext) {
await expectClientResetError(
{
Expand Down
26 changes: 26 additions & 0 deletions integration-tests/tests/src/utils/delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 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.
//
////////////////////////////////////////////////////////////////////////////

/**
* Pause the execution
* @param ms time to pause (in milliseconds)
* @returns a promise which is resolved when execution can continue
*/
export async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
122 changes: 0 additions & 122 deletions tests/js/session-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -556,128 +556,6 @@ module.exports = {
realm.close();
},

testClientReset() {
// FIXME: try to enable for React Native
if (!platformSupported) {
return;
}

const partition = Utils.genPartition();
let creds = Realm.Credentials.anonymous();
let app = new Realm.App(appConfig);
return app.logIn(creds).then((user) => {
return new Promise((resolve, _reject) => {
let realm;
const config = getSyncConfiguration(user, partition);
config.sync.error = (sender, error) => {
try {
console.log(JSON.stringify(error));
TestCase.assertEqual(error.name, "ClientReset");
TestCase.assertEqual(error.message, "Simulate Client Reset");
TestCase.assertEqual(error.code, 211); // 211 -> diverging histories
const path = realm.path;
realm.close();
Realm.App.Sync.initiateClientReset(app, path);
// open Realm with error.config, and copy required objects a Realm at `path`
resolve();
} catch (e) {
_reject(e);
}
};
realm = new Realm(config);
const session = realm.syncSession;

TestCase.assertEqual(session.config.error, config.sync.error);
session._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", false); // 211 -> diverging histories
});
});
},

testClientResetDiscardLocalFailed() {
if (!platformSupported) {
return;
}

// 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 partition = Utils.genPartition();
let creds = Realm.Credentials.anonymous();
let app = new Realm.App(appConfig);
return new Promise((resolve, reject) => {
return app.logIn(creds).then((user) => {
const config = getSyncConfiguration(user, partition);
config.sync.clientReset = {
mode: "discardLocal",
clientResetBefore: (realm) => {
reject("clientResetBefore");
},
clientResetAfter: (beforeRealm, afterRealm) => {
reject("clientResetAfter");
},
};
config.sync.error = (sender, error) => {
resolve();
};

Realm.open(config).then((r) => {
r.syncSession._simulateError(132, "Simulate Client Reset", "realm::sync::ProtocolError", true); // 132 -> automatic client reset failed
});
});
});
},

testClientResetDiscardLocal() {
if (!platformSupported) {
return;
}

// (i) using a client reset in "DiscardLocal" 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

let beforeCalled = false;
let afterCalled = false;

const partition = Utils.genPartition();
let creds = Realm.Credentials.anonymous();
let app = new Realm.App(appConfig);
return new Promise((resolve, reject) => {
return app.logIn(creds).then((user) => {
const config = getSyncConfiguration(user, partition);
config.sync.clientReset = {
mode: "discardLocal",
clientResetBefore: (realm) => {
beforeCalled = true;
TestCase.assertEqual(realm.objects("Dog").length, 1, "local");
},
clientResetAfter: (beforeRealm, afterRealm) => {
afterCalled = true;
TestCase.assertEqual(beforeRealm.objects("Dog").length, 1, "local");
TestCase.assertEqual(afterRealm.objects("Dog").length, 1, "after");
},
};
config.sync.error = (sender, error) => {
reject(`error: ${JSON.stringify(error)}`);
};

Realm.open(config).then((r) => {
r.write(() => {
r.create("Dog", { _id: new ObjectId(), name: "Lassy" });
});
r.syncSession._simulateError(211, "Simulate Client Reset", "realm::sync::ProtocolError", false);
setTimeout(() => {
TestCase.assertTrue(beforeCalled, "before");
TestCase.assertTrue(afterCalled, "after");
TestCase.assertEqual(r.objects("Dog").length, 1);
resolve();
}, 1000);
});
});
});
},

testAddConnectionNotification() {
const partition = Utils.genPartition();
let app = new Realm.App(appConfig);
Expand Down

0 comments on commit c3c2c07

Please sign in to comment.