summaryrefslogtreecommitdiffstats
path: root/services/common/tests/unit/test_storage_adapter.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/common/tests/unit/test_storage_adapter.js')
-rw-r--r--services/common/tests/unit/test_storage_adapter.js307
1 files changed, 307 insertions, 0 deletions
diff --git a/services/common/tests/unit/test_storage_adapter.js b/services/common/tests/unit/test_storage_adapter.js
new file mode 100644
index 0000000000..1dda35ad12
--- /dev/null
+++ b/services/common/tests/unit/test_storage_adapter.js
@@ -0,0 +1,307 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { Sqlite } = ChromeUtils.importESModule(
+ "resource://gre/modules/Sqlite.sys.mjs"
+);
+const { FirefoxAdapter } = ChromeUtils.importESModule(
+ "resource://services-common/kinto-storage-adapter.sys.mjs"
+);
+
+// set up what we need to make storage adapters
+const kintoFilename = "kinto.sqlite";
+
+function do_get_kinto_connection() {
+ return FirefoxAdapter.openConnection({ path: kintoFilename });
+}
+
+function do_get_kinto_adapter(sqliteHandle) {
+ return new FirefoxAdapter("test", { sqliteHandle });
+}
+
+function do_get_kinto_db() {
+ let profile = do_get_profile();
+ let kintoDB = profile.clone();
+ kintoDB.append(kintoFilename);
+ return kintoDB;
+}
+
+function cleanup_kinto() {
+ add_test(function cleanup_kinto_files() {
+ let kintoDB = do_get_kinto_db();
+ // clean up the db
+ kintoDB.remove(false);
+ run_next_test();
+ });
+}
+
+function test_collection_operations() {
+ add_task(async function test_kinto_clear() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.clear();
+ await sqliteHandle.close();
+ });
+
+ // test creating new records... and getting them again
+ add_task(async function test_kinto_create_new_get_existing() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ let record = { id: "test-id", foo: "bar" };
+ await adapter.execute(transaction => transaction.create(record));
+ let newRecord = await adapter.get("test-id");
+ // ensure the record is the same as when it was added
+ deepEqual(record, newRecord);
+ await sqliteHandle.close();
+ });
+
+ // test removing records
+ add_task(async function test_kinto_can_remove_some_records() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ // create a second record
+ let record = { id: "test-id-2", foo: "baz" };
+ await adapter.execute(transaction => transaction.create(record));
+ let newRecord = await adapter.get("test-id-2");
+ deepEqual(record, newRecord);
+ // delete the record
+ await adapter.execute(transaction => transaction.delete(record.id));
+ newRecord = await adapter.get(record.id);
+ // ... and ensure it's no longer there
+ Assert.equal(newRecord, undefined);
+ // ensure the other record still exists
+ newRecord = await adapter.get("test-id");
+ Assert.notEqual(newRecord, undefined);
+ await sqliteHandle.close();
+ });
+
+ // test getting records that don't exist
+ add_task(async function test_kinto_get_non_existant() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ // Kinto expects adapters to either:
+ let newRecord = await adapter.get("missing-test-id");
+ // resolve with an undefined record
+ Assert.equal(newRecord, undefined);
+ await sqliteHandle.close();
+ });
+
+ // test updating records... and getting them again
+ add_task(async function test_kinto_update_get_existing() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ let originalRecord = { id: "test-id", foo: "bar" };
+ let updatedRecord = { id: "test-id", foo: "baz" };
+ await adapter.clear();
+ await adapter.execute(transaction => transaction.create(originalRecord));
+ await adapter.execute(transaction => transaction.update(updatedRecord));
+ // ensure the record exists
+ let newRecord = await adapter.get("test-id");
+ // ensure the record is the same as when it was added
+ deepEqual(updatedRecord, newRecord);
+ await sqliteHandle.close();
+ });
+
+ // test listing records
+ add_task(async function test_kinto_list() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ let originalRecord = { id: "test-id-1", foo: "bar" };
+ let records = await adapter.list();
+ Assert.equal(records.length, 1);
+ await adapter.execute(transaction => transaction.create(originalRecord));
+ records = await adapter.list();
+ Assert.equal(records.length, 2);
+ await sqliteHandle.close();
+ });
+
+ // test aborting transaction
+ add_task(async function test_kinto_aborting_transaction() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.clear();
+ let record = { id: 1, foo: "bar" };
+ let error = null;
+ try {
+ await adapter.execute(transaction => {
+ transaction.create(record);
+ throw new Error("unexpected");
+ });
+ } catch (e) {
+ error = e;
+ }
+ Assert.notEqual(error, null);
+ let records = await adapter.list();
+ Assert.equal(records.length, 0);
+ await sqliteHandle.close();
+ });
+
+ // test save and get last modified
+ add_task(async function test_kinto_last_modified() {
+ const initialValue = 0;
+ const intendedValue = 12345678;
+
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ let lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, initialValue);
+ let result = await adapter.saveLastModified(intendedValue);
+ Assert.equal(result, intendedValue);
+ lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, intendedValue);
+
+ // test saveLastModified parses values correctly
+ result = await adapter.saveLastModified(" " + intendedValue + " blah");
+ // should resolve with the parsed int
+ Assert.equal(result, intendedValue);
+ // and should have saved correctly
+ lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, intendedValue);
+ await sqliteHandle.close();
+ });
+
+ // test loadDump(records)
+ add_task(async function test_kinto_import_records() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ let record1 = { id: 1, foo: "bar" };
+ let record2 = { id: 2, foo: "baz" };
+ let impactedRecords = await adapter.loadDump([record1, record2]);
+ Assert.equal(impactedRecords.length, 2);
+ let newRecord1 = await adapter.get("1");
+ // ensure the record is the same as when it was added
+ deepEqual(record1, newRecord1);
+ let newRecord2 = await adapter.get("2");
+ // ensure the record is the same as when it was added
+ deepEqual(record2, newRecord2);
+ await sqliteHandle.close();
+ });
+
+ add_task(async function test_kinto_import_records_should_override_existing() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.clear();
+ let records = await adapter.list();
+ Assert.equal(records.length, 0);
+ let impactedRecords = await adapter.loadDump([
+ { id: 1, foo: "bar" },
+ { id: 2, foo: "baz" },
+ ]);
+ Assert.equal(impactedRecords.length, 2);
+ await adapter.loadDump([
+ { id: 1, foo: "baz" },
+ { id: 3, foo: "bab" },
+ ]);
+ records = await adapter.list();
+ Assert.equal(records.length, 3);
+ let newRecord1 = await adapter.get("1");
+ deepEqual(newRecord1.foo, "baz");
+ await sqliteHandle.close();
+ });
+
+ add_task(async function test_import_updates_lastModified() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.loadDump([
+ { id: 1, foo: "bar", last_modified: 1457896541 },
+ { id: 2, foo: "baz", last_modified: 1458796542 },
+ ]);
+ let lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, 1458796542);
+ await sqliteHandle.close();
+ });
+
+ add_task(async function test_import_preserves_older_lastModified() {
+ let sqliteHandle = await do_get_kinto_connection();
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.saveLastModified(1458796543);
+
+ await adapter.loadDump([
+ { id: 1, foo: "bar", last_modified: 1457896541 },
+ { id: 2, foo: "baz", last_modified: 1458796542 },
+ ]);
+ let lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, 1458796543);
+ await sqliteHandle.close();
+ });
+
+ add_task(async function test_save_metadata_preserves_lastModified() {
+ let sqliteHandle = await do_get_kinto_connection();
+
+ let adapter = do_get_kinto_adapter(sqliteHandle);
+ await adapter.saveLastModified(42);
+
+ await adapter.saveMetadata({ id: "col" });
+
+ let lastModified = await adapter.getLastModified();
+ Assert.equal(lastModified, 42);
+ await sqliteHandle.close();
+ });
+}
+
+// test kinto db setup and operations in various scenarios
+// test from scratch - no current existing database
+add_test(function test_db_creation() {
+ add_test(function test_create_from_scratch() {
+ // ensure the file does not exist in the profile
+ let kintoDB = do_get_kinto_db();
+ Assert.ok(!kintoDB.exists());
+ run_next_test();
+ });
+
+ test_collection_operations();
+
+ cleanup_kinto();
+ run_next_test();
+});
+
+// this is the closest we can get to a schema version upgrade at v1 - test an
+// existing database
+add_test(function test_creation_from_empty_db() {
+ add_test(function test_create_from_empty_db() {
+ // place an empty kinto db file in the profile
+ let profile = do_get_profile();
+
+ let emptyDB = do_get_file("test_storage_adapter/empty.sqlite");
+ emptyDB.copyTo(profile, kintoFilename);
+
+ run_next_test();
+ });
+
+ test_collection_operations();
+
+ cleanup_kinto();
+ run_next_test();
+});
+
+// test schema version upgrade at v2
+add_test(function test_migration_from_v1_to_v2() {
+ add_test(function test_migrate_from_v1_to_v2() {
+ // place an empty kinto db file in the profile
+ let profile = do_get_profile();
+
+ let v1DB = do_get_file("test_storage_adapter/v1.sqlite");
+ v1DB.copyTo(profile, kintoFilename);
+
+ run_next_test();
+ });
+
+ add_test(async function schema_is_update_from_1_to_2() {
+ // The `v1.sqlite` has schema version 1.
+ let sqliteHandle = await Sqlite.openConnection({ path: kintoFilename });
+ Assert.equal(await sqliteHandle.getSchemaVersion(), 1);
+ await sqliteHandle.close();
+
+ // The `.openConnection()` migrates it to version 2.
+ sqliteHandle = await FirefoxAdapter.openConnection({ path: kintoFilename });
+ Assert.equal(await sqliteHandle.getSchemaVersion(), 2);
+ await sqliteHandle.close();
+
+ run_next_test();
+ });
+
+ test_collection_operations();
+
+ cleanup_kinto();
+ run_next_test();
+});