test_storage_adapter.js (10582B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { Sqlite } = ChromeUtils.importESModule( 5 "resource://gre/modules/Sqlite.sys.mjs" 6 ); 7 const { FirefoxAdapter } = ChromeUtils.importESModule( 8 "resource://services-common/kinto-storage-adapter.sys.mjs" 9 ); 10 11 // set up what we need to make storage adapters 12 const kintoFilename = "kinto.sqlite"; 13 14 function do_get_kinto_connection() { 15 return FirefoxAdapter.openConnection({ path: kintoFilename }); 16 } 17 18 function do_get_kinto_adapter(sqliteHandle) { 19 return new FirefoxAdapter("test", { sqliteHandle }); 20 } 21 22 function do_get_kinto_db() { 23 let profile = do_get_profile(); 24 let kintoDB = profile.clone(); 25 kintoDB.append(kintoFilename); 26 return kintoDB; 27 } 28 29 function cleanup_kinto() { 30 add_test(function cleanup_kinto_files() { 31 let kintoDB = do_get_kinto_db(); 32 // clean up the db 33 kintoDB.remove(false); 34 run_next_test(); 35 }); 36 } 37 38 function test_collection_operations() { 39 add_task(async function test_kinto_clear() { 40 let sqliteHandle = await do_get_kinto_connection(); 41 let adapter = do_get_kinto_adapter(sqliteHandle); 42 await adapter.clear(); 43 await sqliteHandle.close(); 44 }); 45 46 // test creating new records... and getting them again 47 add_task(async function test_kinto_create_new_get_existing() { 48 let sqliteHandle = await do_get_kinto_connection(); 49 let adapter = do_get_kinto_adapter(sqliteHandle); 50 let record = { id: "test-id", foo: "bar" }; 51 await adapter.execute(transaction => transaction.create(record)); 52 let newRecord = await adapter.get("test-id"); 53 // ensure the record is the same as when it was added 54 deepEqual(record, newRecord); 55 await sqliteHandle.close(); 56 }); 57 58 // test removing records 59 add_task(async function test_kinto_can_remove_some_records() { 60 let sqliteHandle = await do_get_kinto_connection(); 61 let adapter = do_get_kinto_adapter(sqliteHandle); 62 // create a second record 63 let record = { id: "test-id-2", foo: "baz" }; 64 await adapter.execute(transaction => transaction.create(record)); 65 let newRecord = await adapter.get("test-id-2"); 66 deepEqual(record, newRecord); 67 // delete the record 68 await adapter.execute(transaction => transaction.delete(record.id)); 69 newRecord = await adapter.get(record.id); 70 // ... and ensure it's no longer there 71 Assert.equal(newRecord, undefined); 72 // ensure the other record still exists 73 newRecord = await adapter.get("test-id"); 74 Assert.notEqual(newRecord, undefined); 75 await sqliteHandle.close(); 76 }); 77 78 // test getting records that don't exist 79 add_task(async function test_kinto_get_non_existant() { 80 let sqliteHandle = await do_get_kinto_connection(); 81 let adapter = do_get_kinto_adapter(sqliteHandle); 82 // Kinto expects adapters to either: 83 let newRecord = await adapter.get("missing-test-id"); 84 // resolve with an undefined record 85 Assert.equal(newRecord, undefined); 86 await sqliteHandle.close(); 87 }); 88 89 // test updating records... and getting them again 90 add_task(async function test_kinto_update_get_existing() { 91 let sqliteHandle = await do_get_kinto_connection(); 92 let adapter = do_get_kinto_adapter(sqliteHandle); 93 let originalRecord = { id: "test-id", foo: "bar" }; 94 let updatedRecord = { id: "test-id", foo: "baz" }; 95 await adapter.clear(); 96 await adapter.execute(transaction => transaction.create(originalRecord)); 97 await adapter.execute(transaction => transaction.update(updatedRecord)); 98 // ensure the record exists 99 let newRecord = await adapter.get("test-id"); 100 // ensure the record is the same as when it was added 101 deepEqual(updatedRecord, newRecord); 102 await sqliteHandle.close(); 103 }); 104 105 // test listing records 106 add_task(async function test_kinto_list() { 107 let sqliteHandle = await do_get_kinto_connection(); 108 let adapter = do_get_kinto_adapter(sqliteHandle); 109 let originalRecord = { id: "test-id-1", foo: "bar" }; 110 let records = await adapter.list(); 111 Assert.equal(records.length, 1); 112 await adapter.execute(transaction => transaction.create(originalRecord)); 113 records = await adapter.list(); 114 Assert.equal(records.length, 2); 115 await sqliteHandle.close(); 116 }); 117 118 // test aborting transaction 119 add_task(async function test_kinto_aborting_transaction() { 120 let sqliteHandle = await do_get_kinto_connection(); 121 let adapter = do_get_kinto_adapter(sqliteHandle); 122 await adapter.clear(); 123 let record = { id: 1, foo: "bar" }; 124 let error = null; 125 try { 126 await adapter.execute(transaction => { 127 transaction.create(record); 128 throw new Error("unexpected"); 129 }); 130 } catch (e) { 131 error = e; 132 } 133 Assert.notEqual(error, null); 134 let records = await adapter.list(); 135 Assert.equal(records.length, 0); 136 await sqliteHandle.close(); 137 }); 138 139 // test save and get last modified 140 add_task(async function test_kinto_last_modified() { 141 const initialValue = 0; 142 const intendedValue = 12345678; 143 144 let sqliteHandle = await do_get_kinto_connection(); 145 let adapter = do_get_kinto_adapter(sqliteHandle); 146 let lastModified = await adapter.getLastModified(); 147 Assert.equal(lastModified, initialValue); 148 let result = await adapter.saveLastModified(intendedValue); 149 Assert.equal(result, intendedValue); 150 lastModified = await adapter.getLastModified(); 151 Assert.equal(lastModified, intendedValue); 152 153 // test saveLastModified parses values correctly 154 result = await adapter.saveLastModified(" " + intendedValue + " blah"); 155 // should resolve with the parsed int 156 Assert.equal(result, intendedValue); 157 // and should have saved correctly 158 lastModified = await adapter.getLastModified(); 159 Assert.equal(lastModified, intendedValue); 160 await sqliteHandle.close(); 161 }); 162 163 // test loadDump(records) 164 add_task(async function test_kinto_import_records() { 165 let sqliteHandle = await do_get_kinto_connection(); 166 let adapter = do_get_kinto_adapter(sqliteHandle); 167 let record1 = { id: 1, foo: "bar" }; 168 let record2 = { id: 2, foo: "baz" }; 169 let impactedRecords = await adapter.loadDump([record1, record2]); 170 Assert.equal(impactedRecords.length, 2); 171 let newRecord1 = await adapter.get("1"); 172 // ensure the record is the same as when it was added 173 deepEqual(record1, newRecord1); 174 let newRecord2 = await adapter.get("2"); 175 // ensure the record is the same as when it was added 176 deepEqual(record2, newRecord2); 177 await sqliteHandle.close(); 178 }); 179 180 add_task(async function test_kinto_import_records_should_override_existing() { 181 let sqliteHandle = await do_get_kinto_connection(); 182 let adapter = do_get_kinto_adapter(sqliteHandle); 183 await adapter.clear(); 184 let records = await adapter.list(); 185 Assert.equal(records.length, 0); 186 let impactedRecords = await adapter.loadDump([ 187 { id: 1, foo: "bar" }, 188 { id: 2, foo: "baz" }, 189 ]); 190 Assert.equal(impactedRecords.length, 2); 191 await adapter.loadDump([ 192 { id: 1, foo: "baz" }, 193 { id: 3, foo: "bab" }, 194 ]); 195 records = await adapter.list(); 196 Assert.equal(records.length, 3); 197 let newRecord1 = await adapter.get("1"); 198 deepEqual(newRecord1.foo, "baz"); 199 await sqliteHandle.close(); 200 }); 201 202 add_task(async function test_import_updates_lastModified() { 203 let sqliteHandle = await do_get_kinto_connection(); 204 let adapter = do_get_kinto_adapter(sqliteHandle); 205 await adapter.loadDump([ 206 { id: 1, foo: "bar", last_modified: 1457896541 }, 207 { id: 2, foo: "baz", last_modified: 1458796542 }, 208 ]); 209 let lastModified = await adapter.getLastModified(); 210 Assert.equal(lastModified, 1458796542); 211 await sqliteHandle.close(); 212 }); 213 214 add_task(async function test_import_preserves_older_lastModified() { 215 let sqliteHandle = await do_get_kinto_connection(); 216 let adapter = do_get_kinto_adapter(sqliteHandle); 217 await adapter.saveLastModified(1458796543); 218 219 await adapter.loadDump([ 220 { id: 1, foo: "bar", last_modified: 1457896541 }, 221 { id: 2, foo: "baz", last_modified: 1458796542 }, 222 ]); 223 let lastModified = await adapter.getLastModified(); 224 Assert.equal(lastModified, 1458796543); 225 await sqliteHandle.close(); 226 }); 227 228 add_task(async function test_save_metadata_preserves_lastModified() { 229 let sqliteHandle = await do_get_kinto_connection(); 230 231 let adapter = do_get_kinto_adapter(sqliteHandle); 232 await adapter.saveLastModified(42); 233 234 await adapter.saveMetadata({ id: "col" }); 235 236 let lastModified = await adapter.getLastModified(); 237 Assert.equal(lastModified, 42); 238 await sqliteHandle.close(); 239 }); 240 } 241 242 // test kinto db setup and operations in various scenarios 243 // test from scratch - no current existing database 244 add_test(function test_db_creation() { 245 add_test(function test_create_from_scratch() { 246 // ensure the file does not exist in the profile 247 let kintoDB = do_get_kinto_db(); 248 Assert.ok(!kintoDB.exists()); 249 run_next_test(); 250 }); 251 252 test_collection_operations(); 253 254 cleanup_kinto(); 255 run_next_test(); 256 }); 257 258 // this is the closest we can get to a schema version upgrade at v1 - test an 259 // existing database 260 add_test(function test_creation_from_empty_db() { 261 add_test(function test_create_from_empty_db() { 262 // place an empty kinto db file in the profile 263 let profile = do_get_profile(); 264 265 let emptyDB = do_get_file("test_storage_adapter/empty.sqlite"); 266 emptyDB.copyTo(profile, kintoFilename); 267 268 run_next_test(); 269 }); 270 271 test_collection_operations(); 272 273 cleanup_kinto(); 274 run_next_test(); 275 }); 276 277 // test schema version upgrade at v2 278 add_test(function test_migration_from_v1_to_v2() { 279 add_test(function test_migrate_from_v1_to_v2() { 280 // place an empty kinto db file in the profile 281 let profile = do_get_profile(); 282 283 let v1DB = do_get_file("test_storage_adapter/v1.sqlite"); 284 v1DB.copyTo(profile, kintoFilename); 285 286 run_next_test(); 287 }); 288 289 add_test(async function schema_is_update_from_1_to_2() { 290 // The `v1.sqlite` has schema version 1. 291 let sqliteHandle = await Sqlite.openConnection({ path: kintoFilename }); 292 Assert.equal(await sqliteHandle.getSchemaVersion(), 1); 293 await sqliteHandle.close(); 294 295 // The `.openConnection()` migrates it to version 2. 296 sqliteHandle = await FirefoxAdapter.openConnection({ path: kintoFilename }); 297 Assert.equal(await sqliteHandle.getSchemaVersion(), 2); 298 await sqliteHandle.close(); 299 300 run_next_test(); 301 }); 302 303 test_collection_operations(); 304 305 cleanup_kinto(); 306 run_next_test(); 307 });