summaryrefslogtreecommitdiffstats
path: root/services/settings/test/unit/test_remote_settings_recover_broken.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/settings/test/unit/test_remote_settings_recover_broken.js')
-rw-r--r--services/settings/test/unit/test_remote_settings_recover_broken.js153
1 files changed, 153 insertions, 0 deletions
diff --git a/services/settings/test/unit/test_remote_settings_recover_broken.js b/services/settings/test/unit/test_remote_settings_recover_broken.js
new file mode 100644
index 0000000000..cedd5cbe30
--- /dev/null
+++ b/services/settings/test/unit/test_remote_settings_recover_broken.js
@@ -0,0 +1,153 @@
+/* import-globals-from ../../../common/tests/unit/head_helpers.js */
+
+const { SyncHistory } = ChromeUtils.importESModule(
+ "resource://services-settings/SyncHistory.sys.mjs"
+);
+const { RemoteSettingsClient } = ChromeUtils.importESModule(
+ "resource://services-settings/RemoteSettingsClient.sys.mjs"
+);
+const { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+const { Utils } = ChromeUtils.importESModule(
+ "resource://services-settings/Utils.sys.mjs"
+);
+
+const PREF_SETTINGS_SERVER = "services.settings.server";
+const CHANGES_PATH = "/v1" + Utils.CHANGES_PATH;
+const BROKEN_SYNC_THRESHOLD = 10; // See default pref value
+
+let server;
+let client;
+let maybeSyncBackup;
+
+async function clear_state() {
+ // Disable logging output.
+ Services.prefs.setCharPref("services.settings.loglevel", "critical");
+ // Pull data from the test server.
+ Services.prefs.setCharPref(
+ PREF_SETTINGS_SERVER,
+ `http://localhost:${server.identity.primaryPort}/v1`
+ );
+
+ // Clear sync history.
+ await new SyncHistory("").clear();
+
+ // Simulate a response whose ETag gets incremented on each call
+ // (in order to generate several history entries, indexed by timestamp).
+ let timestamp = 1337;
+ server.registerPathHandler(CHANGES_PATH, (request, response) => {
+ response.setStatusLine(null, 200, "OK");
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.setHeader("Date", new Date(1000000).toUTCString());
+ response.setHeader("ETag", `"${timestamp}"`);
+ response.write(
+ JSON.stringify({
+ timestamp,
+ changes: [
+ {
+ last_modified: ++timestamp,
+ bucket: "main",
+ collection: "desktop-manager",
+ },
+ ],
+ })
+ );
+ });
+
+ // Restore original maybeSync() method between each test.
+ client.maybeSync = maybeSyncBackup;
+}
+
+function run_test() {
+ // Set up an HTTP Server
+ server = new HttpServer();
+ server.start(-1);
+
+ client = RemoteSettings("desktop-manager");
+ maybeSyncBackup = client.maybeSync;
+
+ run_next_test();
+
+ registerCleanupFunction(() => {
+ server.stop(() => {});
+ // Restore original maybeSync() method when test suite is done.
+ client.maybeSync = maybeSyncBackup;
+ });
+}
+
+add_task(clear_state);
+
+add_task(async function test_db_is_destroyed_when_sync_is_broken() {
+ // Simulate a successful sync.
+ client.maybeSync = async () => {
+ // Store some data in local DB.
+ await client.db.importChanges({}, 1515, []);
+ };
+ await RemoteSettings.pollChanges({ trigger: "timer" });
+
+ // Register a client with a failing sync method.
+ client.maybeSync = () => {
+ throw new RemoteSettingsClient.InvalidSignatureError(
+ "main/desktop-manager"
+ );
+ };
+
+ // Now obtain several failures in a row.
+ for (var i = 0; i < BROKEN_SYNC_THRESHOLD; i++) {
+ try {
+ await RemoteSettings.pollChanges({ trigger: "timer" });
+ } catch (e) {}
+ }
+
+ // Synchronization is in broken state.
+ Assert.equal(
+ await client.db.getLastModified(),
+ 1515,
+ "Local DB was not destroyed yet"
+ );
+
+ // Synchronize again. Broken state will be detected.
+ try {
+ await RemoteSettings.pollChanges({ trigger: "timer" });
+ } catch (e) {}
+
+ // DB was destroyed.
+ Assert.equal(
+ await client.db.getLastModified(),
+ null,
+ "Local DB was destroyed"
+ );
+});
+
+add_task(clear_state);
+
+add_task(async function test_db_is_not_destroyed_when_state_is_server_error() {
+ // Since we don't mock the server endpoints to obtain the changeset of this
+ // collection, the call to `maybeSync()` will fail with network errors.
+
+ // Store some data in local DB.
+ await client.db.importChanges({}, 1515, []);
+
+ // Now obtain several failures in a row.
+ let lastError;
+ for (var i = 0; i < BROKEN_SYNC_THRESHOLD + 1; i++) {
+ try {
+ await RemoteSettings.pollChanges({ trigger: "timer" });
+ } catch (e) {
+ lastError = e;
+ }
+ }
+ Assert.ok(
+ /Cannot parse server content/.test(lastError.message),
+ "Error is about server"
+ );
+ // DB was not destroyed.
+ Assert.equal(
+ await client.db.getLastModified(),
+ 1515,
+ "Local DB was not destroyed"
+ );
+});
+
+add_task(clear_state);