test_service_sync_updateEnabledEngines.js (17727B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { Service } = ChromeUtils.importESModule( 5 "resource://services-sync/service.sys.mjs" 6 ); 7 8 const { EngineSynchronizer } = ChromeUtils.importESModule( 9 "resource://services-sync/stages/enginesync.sys.mjs" 10 ); 11 12 function QuietStore() { 13 Store.call("Quiet"); 14 } 15 QuietStore.prototype = { 16 async getAllIDs() { 17 return []; 18 }, 19 }; 20 21 function SteamEngine() { 22 SyncEngine.call(this, "Steam", Service); 23 } 24 SteamEngine.prototype = { 25 // We're not interested in engine sync but what the service does. 26 _storeObj: QuietStore, 27 28 _sync: async function _sync() { 29 await this._syncStartup(); 30 }, 31 }; 32 Object.setPrototypeOf(SteamEngine.prototype, SyncEngine.prototype); 33 34 function StirlingEngine() { 35 SyncEngine.call(this, "Stirling", Service); 36 } 37 StirlingEngine.prototype = { 38 // This engine's enabled state is the same as the SteamEngine's. 39 get prefName() { 40 return "steam"; 41 }, 42 }; 43 Object.setPrototypeOf(StirlingEngine.prototype, SteamEngine.prototype); 44 45 // Tracking info/collections. 46 var collectionsHelper = track_collections_helper(); 47 var upd = collectionsHelper.with_updated_collection; 48 49 function sync_httpd_setup(handlers) { 50 handlers["/1.1/johndoe/info/collections"] = collectionsHelper.handler; 51 delete collectionsHelper.collections.crypto; 52 delete collectionsHelper.collections.meta; 53 54 let cr = new ServerWBO("keys"); 55 handlers["/1.1/johndoe/storage/crypto/keys"] = upd("crypto", cr.handler()); 56 57 let cl = new ServerCollection(); 58 handlers["/1.1/johndoe/storage/clients"] = upd("clients", cl.handler()); 59 60 return httpd_setup(handlers); 61 } 62 63 async function setUp(server) { 64 await SyncTestingInfrastructure(server, "johndoe", "ilovejane"); 65 // Ensure that the server has valid keys so that logging in will work and not 66 // result in a server wipe, rendering many of these tests useless. 67 await generateNewKeys(Service.collectionKeys); 68 let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 69 await serverKeys.encrypt(Service.identity.syncKeyBundle); 70 let { success } = await serverKeys.upload( 71 Service.resource(Service.cryptoKeysURL) 72 ); 73 ok(success); 74 } 75 76 const PAYLOAD = 42; 77 78 add_task(async function setup() { 79 await Service.engineManager.clear(); 80 validate_all_future_pings(); 81 82 await Service.engineManager.register(SteamEngine); 83 await Service.engineManager.register(StirlingEngine); 84 }); 85 86 add_task(async function test_newAccount() { 87 enableValidationPrefs(); 88 89 _("Test: New account does not disable locally enabled engines."); 90 let engine = Service.engineManager.get("steam"); 91 let server = sync_httpd_setup({ 92 "/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(), 93 "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(), 94 }); 95 await setUp(server); 96 97 try { 98 _("Engine is enabled from the beginning."); 99 Service._ignorePrefObserver = true; 100 engine.enabled = true; 101 Service._ignorePrefObserver = false; 102 103 _("Sync."); 104 await Service.sync(); 105 106 _("Engine continues to be enabled."); 107 Assert.ok(engine.enabled); 108 } finally { 109 await Service.startOver(); 110 await promiseStopServer(server); 111 } 112 }); 113 114 add_task(async function test_enabledLocally() { 115 enableValidationPrefs(); 116 117 _("Test: Engine is disabled on remote clients and enabled locally"); 118 Service.syncID = "abcdefghij"; 119 let engine = Service.engineManager.get("steam"); 120 let metaWBO = new ServerWBO("global", { 121 syncID: Service.syncID, 122 storageVersion: STORAGE_VERSION, 123 engines: {}, 124 }); 125 let server = sync_httpd_setup({ 126 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 127 "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(), 128 }); 129 await setUp(server); 130 131 try { 132 _("Enable engine locally."); 133 engine.enabled = true; 134 135 _("Sync."); 136 await Service.sync(); 137 138 _("Meta record now contains the new engine."); 139 Assert.ok(!!metaWBO.data.engines.steam); 140 141 _("Engine continues to be enabled."); 142 Assert.ok(engine.enabled); 143 } finally { 144 await Service.startOver(); 145 await promiseStopServer(server); 146 } 147 }); 148 149 add_task(async function test_disabledLocally() { 150 enableValidationPrefs(); 151 152 _("Test: Engine is enabled on remote clients and disabled locally"); 153 Service.syncID = "abcdefghij"; 154 let engine = Service.engineManager.get("steam"); 155 let syncID = await engine.resetLocalSyncID(); 156 let metaWBO = new ServerWBO("global", { 157 syncID: Service.syncID, 158 storageVersion: STORAGE_VERSION, 159 engines: { steam: { syncID, version: engine.version } }, 160 }); 161 let steamCollection = new ServerWBO("steam", PAYLOAD); 162 163 let server = sync_httpd_setup({ 164 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 165 "/1.1/johndoe/storage/steam": steamCollection.handler(), 166 }); 167 await setUp(server); 168 169 try { 170 _("Disable engine locally."); 171 Service._ignorePrefObserver = true; 172 engine.enabled = true; 173 Service._ignorePrefObserver = false; 174 engine.enabled = false; 175 176 _("Sync."); 177 await Service.sync(); 178 179 _("Meta record no longer contains engine."); 180 Assert.ok(!metaWBO.data.engines.steam); 181 182 _("Server records are wiped."); 183 Assert.equal(steamCollection.payload, undefined); 184 185 _("Engine continues to be disabled."); 186 Assert.ok(!engine.enabled); 187 } finally { 188 await Service.startOver(); 189 await promiseStopServer(server); 190 } 191 }); 192 193 add_task(async function test_disabledLocally_wipe503() { 194 enableValidationPrefs(); 195 196 _("Test: Engine is enabled on remote clients and disabled locally"); 197 Service.syncID = "abcdefghij"; 198 let engine = Service.engineManager.get("steam"); 199 let syncID = await engine.resetLocalSyncID(); 200 let metaWBO = new ServerWBO("global", { 201 syncID: Service.syncID, 202 storageVersion: STORAGE_VERSION, 203 engines: { steam: { syncID, version: engine.version } }, 204 }); 205 206 function service_unavailable(request, response) { 207 let body = "Service Unavailable"; 208 response.setStatusLine(request.httpVersion, 503, "Service Unavailable"); 209 response.setHeader("Retry-After", "23"); 210 response.bodyOutputStream.write(body, body.length); 211 } 212 213 let server = sync_httpd_setup({ 214 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 215 "/1.1/johndoe/storage/steam": service_unavailable, 216 }); 217 await setUp(server); 218 219 _("Disable engine locally."); 220 Service._ignorePrefObserver = true; 221 engine.enabled = true; 222 Service._ignorePrefObserver = false; 223 engine.enabled = false; 224 225 _("Sync."); 226 await Service.sync(); 227 Assert.equal(Service.status.sync, SERVER_MAINTENANCE); 228 229 await Service.startOver(); 230 await promiseStopServer(server); 231 }); 232 233 add_task(async function test_enabledRemotely() { 234 enableValidationPrefs(); 235 236 _("Test: Engine is disabled locally and enabled on a remote client"); 237 Service.syncID = "abcdefghij"; 238 let engine = Service.engineManager.get("steam"); 239 let syncID = await engine.resetLocalSyncID(); 240 let metaWBO = new ServerWBO("global", { 241 syncID: Service.syncID, 242 storageVersion: STORAGE_VERSION, 243 engines: { steam: { syncID, version: engine.version } }, 244 }); 245 let server = sync_httpd_setup({ 246 "/1.1/johndoe/storage/meta/global": upd("meta", metaWBO.handler()), 247 248 "/1.1/johndoe/storage/steam": upd( 249 "steam", 250 new ServerWBO("steam", {}).handler() 251 ), 252 }); 253 await setUp(server); 254 255 // We need to be very careful how we do this, so that we don't trigger a 256 // fresh start! 257 try { 258 _("Upload some keys to avoid a fresh start."); 259 let wbo = await Service.collectionKeys.generateNewKeysWBO(); 260 await wbo.encrypt(Service.identity.syncKeyBundle); 261 Assert.equal( 262 200, 263 (await wbo.upload(Service.resource(Service.cryptoKeysURL))).status 264 ); 265 266 _("Engine is disabled."); 267 Assert.ok(!engine.enabled); 268 269 _("Sync."); 270 await Service.sync(); 271 272 _("Engine is enabled."); 273 Assert.ok(engine.enabled); 274 275 _("Meta record still present."); 276 Assert.equal(metaWBO.data.engines.steam.syncID, await engine.getSyncID()); 277 } finally { 278 await Service.startOver(); 279 await promiseStopServer(server); 280 } 281 }); 282 283 add_task(async function test_disabledRemotelyTwoClients() { 284 enableValidationPrefs(); 285 286 _( 287 "Test: Engine is enabled locally and disabled on a remote client... with two clients." 288 ); 289 Service.syncID = "abcdefghij"; 290 let engine = Service.engineManager.get("steam"); 291 let metaWBO = new ServerWBO("global", { 292 syncID: Service.syncID, 293 storageVersion: STORAGE_VERSION, 294 engines: {}, 295 }); 296 let server = sync_httpd_setup({ 297 "/1.1/johndoe/storage/meta/global": upd("meta", metaWBO.handler()), 298 299 "/1.1/johndoe/storage/steam": upd( 300 "steam", 301 new ServerWBO("steam", {}).handler() 302 ), 303 }); 304 await setUp(server); 305 306 try { 307 _("Enable engine locally."); 308 Service._ignorePrefObserver = true; 309 engine.enabled = true; 310 Service._ignorePrefObserver = false; 311 312 _("Sync."); 313 await Service.sync(); 314 315 _("Disable engine by deleting from meta/global."); 316 let d = metaWBO.data; 317 delete d.engines.steam; 318 metaWBO.payload = JSON.stringify(d); 319 metaWBO.modified = Date.now() / 1000; 320 321 _("Add a second client and verify that the local pref is changed."); 322 Service.clientsEngine._store._remoteClients.foobar = { 323 name: "foobar", 324 type: "desktop", 325 }; 326 await Service.sync(); 327 328 _("Engine is disabled."); 329 Assert.ok(!engine.enabled); 330 } finally { 331 await Service.startOver(); 332 await promiseStopServer(server); 333 } 334 }); 335 336 add_task(async function test_disabledRemotely() { 337 enableValidationPrefs(); 338 339 _("Test: Engine is enabled locally and disabled on a remote client"); 340 Service.syncID = "abcdefghij"; 341 let engine = Service.engineManager.get("steam"); 342 let metaWBO = new ServerWBO("global", { 343 syncID: Service.syncID, 344 storageVersion: STORAGE_VERSION, 345 engines: {}, 346 }); 347 let server = sync_httpd_setup({ 348 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 349 "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(), 350 }); 351 await setUp(server); 352 353 try { 354 _("Enable engine locally."); 355 Service._ignorePrefObserver = true; 356 engine.enabled = true; 357 Service._ignorePrefObserver = false; 358 359 _("Sync."); 360 await Service.sync(); 361 362 _("Engine is not disabled: only one client."); 363 Assert.ok(engine.enabled); 364 } finally { 365 await Service.startOver(); 366 await promiseStopServer(server); 367 } 368 }); 369 370 add_task(async function test_dependentEnginesEnabledLocally() { 371 enableValidationPrefs(); 372 373 _("Test: Engine is disabled on remote clients and enabled locally"); 374 Service.syncID = "abcdefghij"; 375 let steamEngine = Service.engineManager.get("steam"); 376 let stirlingEngine = Service.engineManager.get("stirling"); 377 let metaWBO = new ServerWBO("global", { 378 syncID: Service.syncID, 379 storageVersion: STORAGE_VERSION, 380 engines: {}, 381 }); 382 let server = sync_httpd_setup({ 383 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 384 "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(), 385 "/1.1/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler(), 386 }); 387 await setUp(server); 388 389 try { 390 _("Enable engine locally. Doing it on one is enough."); 391 steamEngine.enabled = true; 392 393 _("Sync."); 394 await Service.sync(); 395 396 _("Meta record now contains the new engines."); 397 Assert.ok(!!metaWBO.data.engines.steam); 398 Assert.ok(!!metaWBO.data.engines.stirling); 399 400 _("Engines continue to be enabled."); 401 Assert.ok(steamEngine.enabled); 402 Assert.ok(stirlingEngine.enabled); 403 } finally { 404 await Service.startOver(); 405 await promiseStopServer(server); 406 } 407 }); 408 409 add_task(async function test_dependentEnginesDisabledLocally() { 410 enableValidationPrefs(); 411 412 _( 413 "Test: Two dependent engines are enabled on remote clients and disabled locally" 414 ); 415 Service.syncID = "abcdefghij"; 416 let steamEngine = Service.engineManager.get("steam"); 417 let steamSyncID = await steamEngine.resetLocalSyncID(); 418 let stirlingEngine = Service.engineManager.get("stirling"); 419 let stirlingSyncID = await stirlingEngine.resetLocalSyncID(); 420 let metaWBO = new ServerWBO("global", { 421 syncID: Service.syncID, 422 storageVersion: STORAGE_VERSION, 423 engines: { 424 steam: { syncID: steamSyncID, version: steamEngine.version }, 425 stirling: { syncID: stirlingSyncID, version: stirlingEngine.version }, 426 }, 427 }); 428 429 let steamCollection = new ServerWBO("steam", PAYLOAD); 430 let stirlingCollection = new ServerWBO("stirling", PAYLOAD); 431 432 let server = sync_httpd_setup({ 433 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 434 "/1.1/johndoe/storage/steam": steamCollection.handler(), 435 "/1.1/johndoe/storage/stirling": stirlingCollection.handler(), 436 }); 437 await setUp(server); 438 439 try { 440 _("Disable engines locally. Doing it on one is enough."); 441 Service._ignorePrefObserver = true; 442 steamEngine.enabled = true; 443 Assert.ok(stirlingEngine.enabled); 444 Service._ignorePrefObserver = false; 445 steamEngine.enabled = false; 446 Assert.ok(!stirlingEngine.enabled); 447 448 _("Sync."); 449 await Service.sync(); 450 451 _("Meta record no longer contains engines."); 452 Assert.ok(!metaWBO.data.engines.steam); 453 Assert.ok(!metaWBO.data.engines.stirling); 454 455 _("Server records are wiped."); 456 Assert.equal(steamCollection.payload, undefined); 457 Assert.equal(stirlingCollection.payload, undefined); 458 459 _("Engines continue to be disabled."); 460 Assert.ok(!steamEngine.enabled); 461 Assert.ok(!stirlingEngine.enabled); 462 } finally { 463 await Service.startOver(); 464 await promiseStopServer(server); 465 } 466 }); 467 468 add_task(async function test_service_updateLocalEnginesState() { 469 Service.syncID = "abcdefghij"; 470 const engine = Service.engineManager.get("steam"); 471 const metaWBO = new ServerWBO("global", { 472 syncID: Service.syncID, 473 storageVersion: STORAGE_VERSION, 474 declined: ["steam"], 475 engines: {}, 476 }); 477 const server = httpd_setup({ 478 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 479 }); 480 await SyncTestingInfrastructure(server, "johndoe"); 481 482 // Disconnect sync. 483 await Service.startOver(); 484 Service._ignorePrefObserver = true; 485 // Steam engine is enabled on our machine. 486 engine.enabled = true; 487 Service._ignorePrefObserver = false; 488 Service.identity._findCluster = () => server.baseURI + "/1.1/johndoe/"; 489 490 // Update engine state from the server. 491 await Service.updateLocalEnginesState(); 492 // Now disabled. 493 Assert.ok(!engine.enabled); 494 }); 495 496 add_task(async function test_service_enableAfterUpdateState() { 497 Service.syncID = "abcdefghij"; 498 const engine = Service.engineManager.get("steam"); 499 const metaWBO = new ServerWBO("global", { 500 syncID: Service.syncID, 501 storageVersion: STORAGE_VERSION, 502 declined: ["steam"], 503 engines: { someengine: {} }, 504 }); 505 const server = httpd_setup({ 506 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 507 }); 508 await SyncTestingInfrastructure(server, "johndoe"); 509 510 // Disconnect sync. 511 await Service.startOver(); 512 Service.identity._findCluster = () => server.baseURI + "/1.1/johndoe/"; 513 514 // Update engine state from the server. 515 await Service.updateLocalEnginesState(); 516 // Now disabled, reflecting what's on the server. 517 Assert.ok(!engine.enabled); 518 // Enable the engine, as though the user selected it via CWTS. 519 engine.enabled = true; 520 521 // Do the "reconcile local and remote states" dance. 522 let engineSync = new EngineSynchronizer(Service); 523 await engineSync._updateEnabledEngines(); 524 await Service._maybeUpdateDeclined(); 525 // engine should remain enabled. 526 Assert.ok(engine.enabled); 527 // engine should no longer appear in declined on the server. 528 Assert.deepEqual(metaWBO.data.declined, []); 529 }); 530 531 add_task(async function test_service_disableAfterUpdateState() { 532 Service.syncID = "abcdefghij"; 533 const engine = Service.engineManager.get("steam"); 534 const metaWBO = new ServerWBO("global", { 535 syncID: Service.syncID, 536 storageVersion: STORAGE_VERSION, 537 declined: [], 538 engines: { steam: {} }, 539 }); 540 const server = httpd_setup({ 541 "/1.1/johndoe/storage/meta/global": metaWBO.handler(), 542 }); 543 await SyncTestingInfrastructure(server, "johndoe"); 544 545 // Disconnect sync. 546 await Service.startOver(); 547 Service.identity._findCluster = () => server.baseURI + "/1.1/johndoe/"; 548 549 // Update engine state from the server. 550 await Service.updateLocalEnginesState(); 551 // Now enabled, reflecting what's on the server. 552 Assert.ok(engine.enabled); 553 // Disable the engine, as though via CWTS. 554 engine.enabled = false; 555 556 // Do the "reconcile local and remote states" dance. 557 let engineSync = new EngineSynchronizer(Service); 558 await engineSync._updateEnabledEngines(); 559 await Service._maybeUpdateDeclined(); 560 // engine should remain disabled. 561 Assert.ok(!engine.enabled); 562 // engine should now appear in declined on the server. 563 Assert.deepEqual(metaWBO.data.declined, ["steam"]); 564 // and should have been removed from engines. 565 Assert.deepEqual(metaWBO.data.engines, {}); 566 }); 567 568 add_task(async function test_service_updateLocalEnginesState_no_meta_global() { 569 Service.syncID = "abcdefghij"; 570 const engine = Service.engineManager.get("steam"); 571 // The server doesn't contain /meta/global (sync was never enabled). 572 const server = httpd_setup({}); 573 await SyncTestingInfrastructure(server, "johndoe"); 574 575 // Disconnect sync. 576 await Service.startOver(); 577 Service._ignorePrefObserver = true; 578 // Steam engine is enabled on our machine. 579 engine.enabled = true; 580 Service._ignorePrefObserver = false; 581 Service.identity._findCluster = () => server.baseURI + "/1.1/johndoe/"; 582 583 // Update engine state from the server. 584 await Service.updateLocalEnginesState(); 585 // Still enabled. 586 Assert.ok(engine.enabled); 587 });