test_extension_storage_engine.js (7786B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 ChromeUtils.defineESModuleGetters(this, { 7 Service: "resource://services-sync/service.sys.mjs", 8 extensionStorageSync: "resource://gre/modules/ExtensionStorageSync.sys.mjs", 9 }); 10 11 const { ExtensionStorageEngineBridge, ExtensionStorageEngineKinto } = 12 ChromeUtils.importESModule( 13 "resource://services-sync/engines/extension-storage.sys.mjs" 14 ); 15 16 Services.prefs.setStringPref("webextensions.storage.sync.log.level", "debug"); 17 18 add_task(async function test_switching_between_kinto_and_bridged() { 19 function assertUsingKinto(message) { 20 let kintoEngine = Service.engineManager.get("extension-storage"); 21 Assert.ok(kintoEngine instanceof ExtensionStorageEngineKinto, message); 22 } 23 function assertUsingBridged(message) { 24 let bridgedEngine = Service.engineManager.get("extension-storage"); 25 Assert.ok(bridgedEngine instanceof ExtensionStorageEngineBridge, message); 26 } 27 28 let isUsingKinto = Services.prefs.getBoolPref( 29 "webextensions.storage.sync.kinto", 30 false 31 ); 32 if (isUsingKinto) { 33 assertUsingKinto("Should use Kinto engine before flipping pref"); 34 } else { 35 assertUsingBridged("Should use bridged engine before flipping pref"); 36 } 37 38 _("Flip pref"); 39 Services.prefs.setBoolPref("webextensions.storage.sync.kinto", !isUsingKinto); 40 await Service.engineManager.switchAlternatives(); 41 42 if (isUsingKinto) { 43 assertUsingBridged("Should use bridged engine after flipping pref"); 44 } else { 45 assertUsingKinto("Should use Kinto engine after flipping pref"); 46 } 47 48 _("Clean up"); 49 Services.prefs.clearUserPref("webextensions.storage.sync.kinto"); 50 await Service.engineManager.switchAlternatives(); 51 }); 52 53 add_task(async function test_enable() { 54 const PREF = "services.sync.engine.extension-storage.force"; 55 56 let addonsEngine = Service.engineManager.get("addons"); 57 let extensionStorageEngine = Service.engineManager.get("extension-storage"); 58 59 try { 60 Assert.ok( 61 addonsEngine.enabled, 62 "Add-ons engine should be enabled by default" 63 ); 64 Assert.ok( 65 extensionStorageEngine.enabled, 66 "Extension storage engine should be enabled by default" 67 ); 68 69 addonsEngine.enabled = false; 70 Assert.ok( 71 !extensionStorageEngine.enabled, 72 "Disabling add-ons should disable extension storage" 73 ); 74 75 extensionStorageEngine.enabled = true; 76 Assert.ok( 77 !extensionStorageEngine.enabled, 78 "Enabling extension storage without override pref shouldn't work" 79 ); 80 81 Services.prefs.setBoolPref(PREF, true); 82 Assert.ok( 83 extensionStorageEngine.enabled, 84 "Setting override pref should enable extension storage" 85 ); 86 87 extensionStorageEngine.enabled = false; 88 Assert.ok( 89 !extensionStorageEngine.enabled, 90 "Disabling extension storage engine with override pref should work" 91 ); 92 93 extensionStorageEngine.enabled = true; 94 Assert.ok( 95 extensionStorageEngine.enabled, 96 "Enabling extension storage with override pref should work" 97 ); 98 } finally { 99 addonsEngine.enabled = true; 100 Services.prefs.clearUserPref(PREF); 101 } 102 }); 103 104 add_task(async function test_notifyPendingChanges() { 105 let engine = new ExtensionStorageEngineBridge(Service); 106 await engine.initialize(); 107 108 let extension = { id: "ext-1" }; 109 let expectedChange = { 110 a: "b", 111 c: "d", 112 }; 113 114 let lastSync = 0; 115 let syncID = Utils.makeGUID(); 116 let error = null; 117 engine._rustStore = { 118 getSyncedChanges() { 119 if (error) { 120 throw new Error(error.message); 121 } else { 122 return [ 123 { extId: extension.id, changes: JSON.stringify(expectedChange) }, 124 ]; 125 } 126 }, 127 }; 128 129 engine._bridge = { 130 ensureCurrentSyncId(id) { 131 if (syncID != id) { 132 syncID = id; 133 lastSync = 0; 134 } 135 return id; 136 }, 137 resetSyncId() { 138 return syncID; 139 }, 140 syncStarted() {}, 141 lastSync() { 142 return lastSync; 143 }, 144 setLastSync(lastSyncMillis) { 145 lastSync = lastSyncMillis; 146 }, 147 apply() { 148 return []; 149 }, 150 setUploaded(_modified, _ids) {}, 151 syncFinished() {}, 152 }; 153 154 let server = await serverForFoo(engine); 155 156 let actualChanges = []; 157 let listener = changes => actualChanges.push(changes); 158 extensionStorageSync.addOnChangedListener(extension, listener); 159 160 try { 161 await SyncTestingInfrastructure(server); 162 163 info("Sync engine; notify about changes"); 164 await sync_engine_and_validate_telem(engine, false); 165 deepEqual( 166 actualChanges, 167 [expectedChange], 168 "Should notify about changes during sync" 169 ); 170 171 error = new Error("oops!"); 172 actualChanges = []; 173 await sync_engine_and_validate_telem(engine, false); 174 deepEqual( 175 actualChanges, 176 [], 177 "Should finish syncing even if notifying about changes fails" 178 ); 179 } finally { 180 extensionStorageSync.removeOnChangedListener(extension, listener); 181 await promiseStopServer(server); 182 await engine.finalize(); 183 } 184 }); 185 186 // It's difficult to know what to test - there's already tests for the bridged 187 // engine etc - so we just try and check that this engine conforms to the 188 // mozIBridgedSyncEngine interface guarantees. 189 add_task(async function test_engine() { 190 // Forcibly set the bridged engine in the engine manager. the reason we do 191 // this, unlike the other tests where we just create the engine, is so that 192 // telemetry can get at the engine's `overrideTelemetryName`, which it gets 193 // through the engine manager. 194 await Service.engineManager.unregister("extension-storage"); 195 await Service.engineManager.register(ExtensionStorageEngineBridge); 196 let engine = Service.engineManager.get("extension-storage"); 197 Assert.equal(engine.version, 1); 198 199 Assert.deepEqual(await engine.getSyncID(), null); 200 await engine.resetLocalSyncID(); 201 Assert.notEqual(await engine.getSyncID(), null); 202 203 Assert.equal(await engine.getLastSync(), 0); 204 // lastSync is seconds on this side of the world, but milli-seconds on the other. 205 await engine.setLastSync(1234.567); 206 // should have 2 digit precision. 207 Assert.equal(await engine.getLastSync(), 1234.57); 208 await engine.setLastSync(0); 209 210 // Set some data. 211 await extensionStorageSync.set({ id: "ext-2" }, { ext_2_key: "ext_2_value" }); 212 // Now do a sync with out regular test server. 213 let server = await serverForFoo(engine); 214 try { 215 await SyncTestingInfrastructure(server); 216 217 info("Add server records"); 218 let foo = server.user("foo"); 219 let collection = foo.collection("extension-storage"); 220 let now = new_timestamp(); 221 222 collection.insert( 223 "fakeguid0000", 224 encryptPayload({ 225 id: "fakeguid0000", 226 extId: "ext-1", 227 data: JSON.stringify({ foo: "bar" }), 228 }), 229 now 230 ); 231 232 info("Sync the engine"); 233 234 let ping = await sync_engine_and_validate_telem(engine, false); 235 Assert.ok(ping.engines.find(e => e.name == "rust-webext-storage")); 236 Assert.equal( 237 ping.engines.find(e => e.name == "extension-storage"), 238 null 239 ); 240 241 // We should have applied the data from the existing collection record. 242 Assert.deepEqual(await extensionStorageSync.get({ id: "ext-1" }, null), { 243 foo: "bar", 244 }); 245 246 // should now be 2 records on the server. 247 let payloads = collection.payloads(); 248 Assert.equal(payloads.length, 2); 249 // find the new one we wrote. 250 let newPayload = 251 payloads[0].id == "fakeguid0000" ? payloads[1] : payloads[0]; 252 Assert.equal(newPayload.data, `{"ext_2_key":"ext_2_value"}`); 253 // should have updated the timestamp. 254 greater(await engine.getLastSync(), 0, "Should update last sync time"); 255 } finally { 256 await promiseStopServer(server); 257 await engine.finalize(); 258 } 259 });