summaryrefslogtreecommitdiffstats
path: root/services/settings/test/unit/test_remote_settings_offline.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/settings/test/unit/test_remote_settings_offline.js')
-rw-r--r--services/settings/test/unit/test_remote_settings_offline.js141
1 files changed, 141 insertions, 0 deletions
diff --git a/services/settings/test/unit/test_remote_settings_offline.js b/services/settings/test/unit/test_remote_settings_offline.js
new file mode 100644
index 0000000000..d3403d4d89
--- /dev/null
+++ b/services/settings/test/unit/test_remote_settings_offline.js
@@ -0,0 +1,141 @@
+const { RemoteSettingsClient } = ChromeUtils.importESModule(
+ "resource://services-settings/RemoteSettingsClient.sys.mjs"
+);
+const { RemoteSettingsWorker } = ChromeUtils.importESModule(
+ "resource://services-settings/RemoteSettingsWorker.sys.mjs"
+);
+const { SharedUtils } = ChromeUtils.import(
+ "resource://services-settings/SharedUtils.jsm"
+);
+
+// A collection with a dump that's packaged on all builds where this test runs,
+// including on Android at mobile/android/installer/package-manifest.in
+const TEST_BUCKET = "main";
+const TEST_COLLECTION = "password-recipes";
+
+let client;
+let DUMP_RECORDS;
+let DUMP_LAST_MODIFIED;
+
+add_task(async function setup() {
+ // "services.settings.server" pref is not set.
+ // Test defaults to an unreachable server,
+ // and will only load from the dump if any.
+
+ client = new RemoteSettingsClient(TEST_COLLECTION, {
+ bucketName: TEST_BUCKET,
+ });
+
+ const dump = await SharedUtils.loadJSONDump(TEST_BUCKET, TEST_COLLECTION);
+ DUMP_RECORDS = dump.data;
+ DUMP_LAST_MODIFIED = dump.timestamp;
+
+ // Dumps are fetched via the following, which sorts the records, newest first.
+ // https://searchfox.org/mozilla-central/rev/5b3444ad300e244b5af4214212e22bd9e4b7088a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh#304
+ equal(
+ DUMP_LAST_MODIFIED,
+ DUMP_RECORDS[0].last_modified,
+ "records in dump ought to be sorted by last_modified"
+ );
+});
+
+async function importData(records) {
+ await RemoteSettingsWorker._execute("_test_only_import", [
+ TEST_BUCKET,
+ TEST_COLLECTION,
+ records,
+ records[0]?.last_modified || 0,
+ ]);
+}
+
+async function clear_state() {
+ await client.db.clear();
+}
+
+add_task(async function test_load_from_dump_when_offline() {
+ // Baseline: verify that the collection is empty at first,
+ // but non-empty after loading from the dump.
+ const before = await client.get({ syncIfEmpty: false });
+ equal(before.length, 0, "collection empty when offline");
+
+ // should import from dump since collection was not initialized.
+ const after = await client.get();
+ equal(after.length, DUMP_RECORDS.length, "collection loaded from dump");
+ equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
+});
+add_task(clear_state);
+
+add_task(async function test_optional_skip_dump_after_empty_import() {
+ // clear_state should have wiped the database.
+ const before = await client.get({ syncIfEmpty: false });
+ equal(before.length, 0, "collection empty after clearing");
+
+ // Verify that the dump is not imported again by client.get()
+ // when the database is initialized with an empty dump
+ // with `loadDumpIfNewer` disabled.
+ await importData([]); // <-- Empty set of records.
+
+ const after = await client.get({ loadDumpIfNewer: false });
+ equal(after.length, 0, "collection still empty due to import");
+ equal(await client.getLastModified(), 0, "Empty dump has no timestamp");
+});
+add_task(clear_state);
+
+add_task(async function test_optional_skip_dump_after_non_empty_import() {
+ await importData([{ last_modified: 1234, id: "dummy" }]);
+
+ const after = await client.get({ loadDumpIfNewer: false });
+ equal(after.length, 1, "Imported dummy data");
+ equal(await client.getLastModified(), 1234, "Expected timestamp of import");
+
+ await importData([]);
+ const after2 = await client.get({ loadDumpIfNewer: false });
+ equal(after2.length, 0, "Previous data wiped on duplicate import");
+ equal(await client.getLastModified(), 0, "Timestamp of empty collection");
+});
+add_task(clear_state);
+
+add_task(async function test_load_dump_after_empty_import() {
+ await importData([]); // <-- Empty set of records, i.e. last_modified = 0.
+
+ const after = await client.get();
+ equal(after.length, DUMP_RECORDS.length, "Imported dump");
+ equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
+});
+add_task(clear_state);
+
+add_task(async function test_load_dump_after_non_empty_import() {
+ // Dump is updated regularly, verify that the dump matches our expectations
+ // before running the test.
+ ok(DUMP_LAST_MODIFIED > 1234, "Assuming dump to be newer than dummy 1234");
+
+ await importData([{ last_modified: 1234, id: "dummy" }]);
+
+ const after = await client.get();
+ equal(after.length, DUMP_RECORDS.length, "Imported dump");
+ equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
+});
+add_task(clear_state);
+
+add_task(async function test_load_dump_after_import_from_broken_distro() {
+ // Dump is updated regularly, verify that the dump matches our expectations
+ // before running the test.
+ ok(DUMP_LAST_MODIFIED > 1234, "Assuming dump to be newer than dummy 1234");
+
+ // No last_modified time.
+ await importData([{ id: "dummy" }]);
+
+ const after = await client.get();
+ equal(after.length, DUMP_RECORDS.length, "Imported dump");
+ equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "dump's timestamp");
+});
+add_task(clear_state);
+
+add_task(async function test_skip_dump_if_same_last_modified() {
+ await importData([{ last_modified: DUMP_LAST_MODIFIED, id: "dummy" }]);
+
+ const after = await client.get();
+ equal(after.length, 1, "Not importing dump when time matches");
+ equal(await client.getLastModified(), DUMP_LAST_MODIFIED, "Same timestamp");
+});
+add_task(clear_state);