services/sync/tests/unit/test_syncscheduler.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 Cu.import("resource://services-sync/constants.js");
michael@0 5 Cu.import("resource://services-sync/engines.js");
michael@0 6 Cu.import("resource://services-sync/engines/clients.js");
michael@0 7 Cu.import("resource://services-sync/policies.js");
michael@0 8 Cu.import("resource://services-sync/record.js");
michael@0 9 Cu.import("resource://services-sync/service.js");
michael@0 10 Cu.import("resource://services-sync/status.js");
michael@0 11 Cu.import("resource://services-sync/util.js");
michael@0 12 Cu.import("resource://testing-common/services/sync/utils.js");
michael@0 13
michael@0 14 Service.engineManager.clear();
michael@0 15
michael@0 16 function CatapultEngine() {
michael@0 17 SyncEngine.call(this, "Catapult", Service);
michael@0 18 }
michael@0 19 CatapultEngine.prototype = {
michael@0 20 __proto__: SyncEngine.prototype,
michael@0 21 exception: null, // tests fill this in
michael@0 22 _sync: function _sync() {
michael@0 23 throw this.exception;
michael@0 24 }
michael@0 25 };
michael@0 26
michael@0 27 Service.engineManager.register(CatapultEngine);
michael@0 28
michael@0 29 let scheduler = new SyncScheduler(Service);
michael@0 30 let clientsEngine = Service.clientsEngine;
michael@0 31
michael@0 32 function sync_httpd_setup() {
michael@0 33 let global = new ServerWBO("global", {
michael@0 34 syncID: Service.syncID,
michael@0 35 storageVersion: STORAGE_VERSION,
michael@0 36 engines: {clients: {version: clientsEngine.version,
michael@0 37 syncID: clientsEngine.syncID}}
michael@0 38 });
michael@0 39 let clientsColl = new ServerCollection({}, true);
michael@0 40
michael@0 41 // Tracking info/collections.
michael@0 42 let collectionsHelper = track_collections_helper();
michael@0 43 let upd = collectionsHelper.with_updated_collection;
michael@0 44
michael@0 45 return httpd_setup({
michael@0 46 "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
michael@0 47 "/1.1/johndoe/info/collections": collectionsHelper.handler,
michael@0 48 "/1.1/johndoe/storage/crypto/keys":
michael@0 49 upd("crypto", (new ServerWBO("keys")).handler()),
michael@0 50 "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
michael@0 51 "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "null")
michael@0 52 });
michael@0 53 }
michael@0 54
michael@0 55 function setUp(server) {
michael@0 56 let deferred = Promise.defer();
michael@0 57 configureIdentity({username: "johndoe"}).then(() => {
michael@0 58 Service.clusterURL = server.baseURI + "/";
michael@0 59
michael@0 60 generateNewKeys(Service.collectionKeys);
michael@0 61 let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
michael@0 62 serverKeys.encrypt(Service.identity.syncKeyBundle);
michael@0 63 let result = serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
michael@0 64 deferred.resolve(result);
michael@0 65 });
michael@0 66 return deferred.promise;
michael@0 67 }
michael@0 68
michael@0 69 function cleanUpAndGo(server) {
michael@0 70 let deferred = Promise.defer();
michael@0 71 Utils.nextTick(function () {
michael@0 72 Service.startOver();
michael@0 73 if (server) {
michael@0 74 server.stop(deferred.resolve);
michael@0 75 } else {
michael@0 76 deferred.resolve();
michael@0 77 }
michael@0 78 });
michael@0 79 return deferred.promise;
michael@0 80 }
michael@0 81
michael@0 82 function run_test() {
michael@0 83 initTestLogging("Trace");
michael@0 84
michael@0 85 Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
michael@0 86 Log.repository.getLogger("Sync.scheduler").level = Log.Level.Trace;
michael@0 87
michael@0 88 // The scheduler checks Weave.fxaEnabled to determine whether to use
michael@0 89 // FxA defaults or legacy defaults. As .fxaEnabled checks the username, we
michael@0 90 // set a username here then reset the default to ensure they are used.
michael@0 91 ensureLegacyIdentityManager();
michael@0 92 setBasicCredentials("johndoe");
michael@0 93 scheduler.setDefaults();
michael@0 94
michael@0 95 run_next_test();
michael@0 96 }
michael@0 97
michael@0 98 add_test(function test_prefAttributes() {
michael@0 99 _("Test various attributes corresponding to preferences.");
michael@0 100
michael@0 101 const INTERVAL = 42 * 60 * 1000; // 42 minutes
michael@0 102 const THRESHOLD = 3142;
michael@0 103 const SCORE = 2718;
michael@0 104 const TIMESTAMP1 = 1275493471649;
michael@0 105
michael@0 106 _("The 'nextSync' attribute stores a millisecond timestamp rounded down to the nearest second.");
michael@0 107 do_check_eq(scheduler.nextSync, 0);
michael@0 108 scheduler.nextSync = TIMESTAMP1;
michael@0 109 do_check_eq(scheduler.nextSync, Math.floor(TIMESTAMP1 / 1000) * 1000);
michael@0 110
michael@0 111 _("'syncInterval' defaults to singleDeviceInterval.");
michael@0 112 do_check_eq(Svc.Prefs.get('syncInterval'), undefined);
michael@0 113 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 114
michael@0 115 _("'syncInterval' corresponds to a preference setting.");
michael@0 116 scheduler.syncInterval = INTERVAL;
michael@0 117 do_check_eq(scheduler.syncInterval, INTERVAL);
michael@0 118 do_check_eq(Svc.Prefs.get('syncInterval'), INTERVAL);
michael@0 119
michael@0 120 _("'syncThreshold' corresponds to preference, defaults to SINGLE_USER_THRESHOLD");
michael@0 121 do_check_eq(Svc.Prefs.get('syncThreshold'), undefined);
michael@0 122 do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
michael@0 123 scheduler.syncThreshold = THRESHOLD;
michael@0 124 do_check_eq(scheduler.syncThreshold, THRESHOLD);
michael@0 125
michael@0 126 _("'globalScore' corresponds to preference, defaults to zero.");
michael@0 127 do_check_eq(Svc.Prefs.get('globalScore'), 0);
michael@0 128 do_check_eq(scheduler.globalScore, 0);
michael@0 129 scheduler.globalScore = SCORE;
michael@0 130 do_check_eq(scheduler.globalScore, SCORE);
michael@0 131 do_check_eq(Svc.Prefs.get('globalScore'), SCORE);
michael@0 132
michael@0 133 _("Intervals correspond to default preferences.");
michael@0 134 do_check_eq(scheduler.singleDeviceInterval,
michael@0 135 Svc.Prefs.get("scheduler.sync11.singleDeviceInterval") * 1000);
michael@0 136 do_check_eq(scheduler.idleInterval,
michael@0 137 Svc.Prefs.get("scheduler.idleInterval") * 1000);
michael@0 138 do_check_eq(scheduler.activeInterval,
michael@0 139 Svc.Prefs.get("scheduler.activeInterval") * 1000);
michael@0 140 do_check_eq(scheduler.immediateInterval,
michael@0 141 Svc.Prefs.get("scheduler.immediateInterval") * 1000);
michael@0 142
michael@0 143 _("Custom values for prefs will take effect after a restart.");
michael@0 144 Svc.Prefs.set("scheduler.sync11.singleDeviceInterval", 42);
michael@0 145 Svc.Prefs.set("scheduler.idleInterval", 23);
michael@0 146 Svc.Prefs.set("scheduler.activeInterval", 18);
michael@0 147 Svc.Prefs.set("scheduler.immediateInterval", 31415);
michael@0 148 scheduler.setDefaults();
michael@0 149 do_check_eq(scheduler.idleInterval, 23000);
michael@0 150 do_check_eq(scheduler.singleDeviceInterval, 42000);
michael@0 151 do_check_eq(scheduler.activeInterval, 18000);
michael@0 152 do_check_eq(scheduler.immediateInterval, 31415000);
michael@0 153
michael@0 154 Svc.Prefs.resetBranch("");
michael@0 155 scheduler.setDefaults();
michael@0 156 run_next_test();
michael@0 157 });
michael@0 158
michael@0 159 add_identity_test(this, function test_updateClientMode() {
michael@0 160 _("Test updateClientMode adjusts scheduling attributes based on # of clients appropriately");
michael@0 161 do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
michael@0 162 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 163 do_check_false(scheduler.numClients > 1);
michael@0 164 do_check_false(scheduler.idle);
michael@0 165
michael@0 166 // Trigger a change in interval & threshold by adding a client.
michael@0 167 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 168 scheduler.updateClientMode();
michael@0 169
michael@0 170 do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD);
michael@0 171 do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
michael@0 172 do_check_true(scheduler.numClients > 1);
michael@0 173 do_check_false(scheduler.idle);
michael@0 174
michael@0 175 // Resets the number of clients to 0.
michael@0 176 clientsEngine.resetClient();
michael@0 177 scheduler.updateClientMode();
michael@0 178
michael@0 179 // Goes back to single user if # clients is 1.
michael@0 180 do_check_eq(scheduler.numClients, 1);
michael@0 181 do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
michael@0 182 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 183 do_check_false(scheduler.numClients > 1);
michael@0 184 do_check_false(scheduler.idle);
michael@0 185
michael@0 186 yield cleanUpAndGo();
michael@0 187 });
michael@0 188
michael@0 189 add_identity_test(this, function test_masterpassword_locked_retry_interval() {
michael@0 190 _("Test Status.login = MASTER_PASSWORD_LOCKED results in reschedule at MASTER_PASSWORD interval");
michael@0 191 let loginFailed = false;
michael@0 192 Svc.Obs.add("weave:service:login:error", function onLoginError() {
michael@0 193 Svc.Obs.remove("weave:service:login:error", onLoginError);
michael@0 194 loginFailed = true;
michael@0 195 });
michael@0 196
michael@0 197 let rescheduleInterval = false;
michael@0 198
michael@0 199 let oldScheduleAtInterval = SyncScheduler.prototype.scheduleAtInterval;
michael@0 200 SyncScheduler.prototype.scheduleAtInterval = function (interval) {
michael@0 201 rescheduleInterval = true;
michael@0 202 do_check_eq(interval, MASTER_PASSWORD_LOCKED_RETRY_INTERVAL);
michael@0 203 };
michael@0 204
michael@0 205 let oldVerifyLogin = Service.verifyLogin;
michael@0 206 Service.verifyLogin = function () {
michael@0 207 Status.login = MASTER_PASSWORD_LOCKED;
michael@0 208 return false;
michael@0 209 };
michael@0 210
michael@0 211 let server = sync_httpd_setup();
michael@0 212 yield setUp(server);
michael@0 213
michael@0 214 Service.sync();
michael@0 215
michael@0 216 do_check_true(loginFailed);
michael@0 217 do_check_eq(Status.login, MASTER_PASSWORD_LOCKED);
michael@0 218 do_check_true(rescheduleInterval);
michael@0 219
michael@0 220 Service.verifyLogin = oldVerifyLogin;
michael@0 221 SyncScheduler.prototype.scheduleAtInterval = oldScheduleAtInterval;
michael@0 222
michael@0 223 yield cleanUpAndGo(server);
michael@0 224 });
michael@0 225
michael@0 226 add_identity_test(this, function test_calculateBackoff() {
michael@0 227 do_check_eq(Status.backoffInterval, 0);
michael@0 228
michael@0 229 // Test no interval larger than the maximum backoff is used if
michael@0 230 // Status.backoffInterval is smaller.
michael@0 231 Status.backoffInterval = 5;
michael@0 232 let backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL,
michael@0 233 Status.backoffInterval);
michael@0 234
michael@0 235 do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL);
michael@0 236
michael@0 237 // Test Status.backoffInterval is used if it is
michael@0 238 // larger than MAXIMUM_BACKOFF_INTERVAL.
michael@0 239 Status.backoffInterval = MAXIMUM_BACKOFF_INTERVAL + 10;
michael@0 240 backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL,
michael@0 241 Status.backoffInterval);
michael@0 242
michael@0 243 do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL + 10);
michael@0 244
michael@0 245 yield cleanUpAndGo();
michael@0 246 });
michael@0 247
michael@0 248 add_identity_test(this, function test_scheduleNextSync_nowOrPast() {
michael@0 249 let deferred = Promise.defer();
michael@0 250 Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
michael@0 251 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
michael@0 252 cleanUpAndGo(server).then(deferred.resolve);
michael@0 253 });
michael@0 254
michael@0 255 let server = sync_httpd_setup();
michael@0 256 yield setUp(server);
michael@0 257
michael@0 258 // We're late for a sync...
michael@0 259 scheduler.scheduleNextSync(-1);
michael@0 260 yield deferred.promise;
michael@0 261 });
michael@0 262
michael@0 263 add_identity_test(this, function test_scheduleNextSync_future_noBackoff() {
michael@0 264 _("scheduleNextSync() uses the current syncInterval if no interval is provided.");
michael@0 265 // Test backoffInterval is 0 as expected.
michael@0 266 do_check_eq(Status.backoffInterval, 0);
michael@0 267
michael@0 268 _("Test setting sync interval when nextSync == 0");
michael@0 269 scheduler.nextSync = 0;
michael@0 270 scheduler.scheduleNextSync();
michael@0 271
michael@0 272 // nextSync - Date.now() might be smaller than expectedInterval
michael@0 273 // since some time has passed since we called scheduleNextSync().
michael@0 274 do_check_true(scheduler.nextSync - Date.now()
michael@0 275 <= scheduler.syncInterval);
michael@0 276 do_check_eq(scheduler.syncTimer.delay, scheduler.syncInterval);
michael@0 277
michael@0 278 _("Test setting sync interval when nextSync != 0");
michael@0 279 scheduler.nextSync = Date.now() + scheduler.singleDeviceInterval;
michael@0 280 scheduler.scheduleNextSync();
michael@0 281
michael@0 282 // nextSync - Date.now() might be smaller than expectedInterval
michael@0 283 // since some time has passed since we called scheduleNextSync().
michael@0 284 do_check_true(scheduler.nextSync - Date.now()
michael@0 285 <= scheduler.syncInterval);
michael@0 286 do_check_true(scheduler.syncTimer.delay <= scheduler.syncInterval);
michael@0 287
michael@0 288 _("Scheduling requests for intervals larger than the current one will be ignored.");
michael@0 289 // Request a sync at a longer interval. The sync that's already scheduled
michael@0 290 // for sooner takes precedence.
michael@0 291 let nextSync = scheduler.nextSync;
michael@0 292 let timerDelay = scheduler.syncTimer.delay;
michael@0 293 let requestedInterval = scheduler.syncInterval * 10;
michael@0 294 scheduler.scheduleNextSync(requestedInterval);
michael@0 295 do_check_eq(scheduler.nextSync, nextSync);
michael@0 296 do_check_eq(scheduler.syncTimer.delay, timerDelay);
michael@0 297
michael@0 298 // We can schedule anything we want if there isn't a sync scheduled.
michael@0 299 scheduler.nextSync = 0;
michael@0 300 scheduler.scheduleNextSync(requestedInterval);
michael@0 301 do_check_true(scheduler.nextSync <= Date.now() + requestedInterval);
michael@0 302 do_check_eq(scheduler.syncTimer.delay, requestedInterval);
michael@0 303
michael@0 304 // Request a sync at the smallest possible interval (0 triggers now).
michael@0 305 scheduler.scheduleNextSync(1);
michael@0 306 do_check_true(scheduler.nextSync <= Date.now() + 1);
michael@0 307 do_check_eq(scheduler.syncTimer.delay, 1);
michael@0 308
michael@0 309 yield cleanUpAndGo();
michael@0 310 });
michael@0 311
michael@0 312 add_identity_test(this, function test_scheduleNextSync_future_backoff() {
michael@0 313 _("scheduleNextSync() will honour backoff in all scheduling requests.");
michael@0 314 // Let's take a backoff interval that's bigger than the default sync interval.
michael@0 315 const BACKOFF = 7337;
michael@0 316 Status.backoffInterval = scheduler.syncInterval + BACKOFF;
michael@0 317
michael@0 318 _("Test setting sync interval when nextSync == 0");
michael@0 319 scheduler.nextSync = 0;
michael@0 320 scheduler.scheduleNextSync();
michael@0 321
michael@0 322 // nextSync - Date.now() might be smaller than expectedInterval
michael@0 323 // since some time has passed since we called scheduleNextSync().
michael@0 324 do_check_true(scheduler.nextSync - Date.now()
michael@0 325 <= Status.backoffInterval);
michael@0 326 do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval);
michael@0 327
michael@0 328 _("Test setting sync interval when nextSync != 0");
michael@0 329 scheduler.nextSync = Date.now() + scheduler.singleDeviceInterval;
michael@0 330 scheduler.scheduleNextSync();
michael@0 331
michael@0 332 // nextSync - Date.now() might be smaller than expectedInterval
michael@0 333 // since some time has passed since we called scheduleNextSync().
michael@0 334 do_check_true(scheduler.nextSync - Date.now()
michael@0 335 <= Status.backoffInterval);
michael@0 336 do_check_true(scheduler.syncTimer.delay <= Status.backoffInterval);
michael@0 337
michael@0 338 // Request a sync at a longer interval. The sync that's already scheduled
michael@0 339 // for sooner takes precedence.
michael@0 340 let nextSync = scheduler.nextSync;
michael@0 341 let timerDelay = scheduler.syncTimer.delay;
michael@0 342 let requestedInterval = scheduler.syncInterval * 10;
michael@0 343 do_check_true(requestedInterval > Status.backoffInterval);
michael@0 344 scheduler.scheduleNextSync(requestedInterval);
michael@0 345 do_check_eq(scheduler.nextSync, nextSync);
michael@0 346 do_check_eq(scheduler.syncTimer.delay, timerDelay);
michael@0 347
michael@0 348 // We can schedule anything we want if there isn't a sync scheduled.
michael@0 349 scheduler.nextSync = 0;
michael@0 350 scheduler.scheduleNextSync(requestedInterval);
michael@0 351 do_check_true(scheduler.nextSync <= Date.now() + requestedInterval);
michael@0 352 do_check_eq(scheduler.syncTimer.delay, requestedInterval);
michael@0 353
michael@0 354 // Request a sync at the smallest possible interval (0 triggers now).
michael@0 355 scheduler.scheduleNextSync(1);
michael@0 356 do_check_true(scheduler.nextSync <= Date.now() + Status.backoffInterval);
michael@0 357 do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval);
michael@0 358
michael@0 359 yield cleanUpAndGo();
michael@0 360 });
michael@0 361
michael@0 362 add_identity_test(this, function test_handleSyncError() {
michael@0 363 let server = sync_httpd_setup();
michael@0 364 yield setUp(server);
michael@0 365
michael@0 366 // Force sync to fail.
michael@0 367 Svc.Prefs.set("firstSync", "notReady");
michael@0 368
michael@0 369 _("Ensure expected initial environment.");
michael@0 370 do_check_eq(scheduler._syncErrors, 0);
michael@0 371 do_check_false(Status.enforceBackoff);
michael@0 372 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 373 do_check_eq(Status.backoffInterval, 0);
michael@0 374
michael@0 375 // Trigger sync with an error several times & observe
michael@0 376 // functionality of handleSyncError()
michael@0 377 _("Test first error calls scheduleNextSync on default interval");
michael@0 378 Service.sync();
michael@0 379 do_check_true(scheduler.nextSync <= Date.now() + scheduler.singleDeviceInterval);
michael@0 380 do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
michael@0 381 do_check_eq(scheduler._syncErrors, 1);
michael@0 382 do_check_false(Status.enforceBackoff);
michael@0 383 scheduler.syncTimer.clear();
michael@0 384
michael@0 385 _("Test second error still calls scheduleNextSync on default interval");
michael@0 386 Service.sync();
michael@0 387 do_check_true(scheduler.nextSync <= Date.now() + scheduler.singleDeviceInterval);
michael@0 388 do_check_eq(scheduler.syncTimer.delay, scheduler.singleDeviceInterval);
michael@0 389 do_check_eq(scheduler._syncErrors, 2);
michael@0 390 do_check_false(Status.enforceBackoff);
michael@0 391 scheduler.syncTimer.clear();
michael@0 392
michael@0 393 _("Test third error sets Status.enforceBackoff and calls scheduleAtInterval");
michael@0 394 Service.sync();
michael@0 395 let maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL);
michael@0 396 do_check_eq(Status.backoffInterval, 0);
michael@0 397 do_check_true(scheduler.nextSync <= (Date.now() + maxInterval));
michael@0 398 do_check_true(scheduler.syncTimer.delay <= maxInterval);
michael@0 399 do_check_eq(scheduler._syncErrors, 3);
michael@0 400 do_check_true(Status.enforceBackoff);
michael@0 401
michael@0 402 // Status.enforceBackoff is false but there are still errors.
michael@0 403 Status.resetBackoff();
michael@0 404 do_check_false(Status.enforceBackoff);
michael@0 405 do_check_eq(scheduler._syncErrors, 3);
michael@0 406 scheduler.syncTimer.clear();
michael@0 407
michael@0 408 _("Test fourth error still calls scheduleAtInterval even if enforceBackoff was reset");
michael@0 409 Service.sync();
michael@0 410 maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL);
michael@0 411 do_check_true(scheduler.nextSync <= Date.now() + maxInterval);
michael@0 412 do_check_true(scheduler.syncTimer.delay <= maxInterval);
michael@0 413 do_check_eq(scheduler._syncErrors, 4);
michael@0 414 do_check_true(Status.enforceBackoff);
michael@0 415 scheduler.syncTimer.clear();
michael@0 416
michael@0 417 _("Arrange for a successful sync to reset the scheduler error count");
michael@0 418 let deferred = Promise.defer();
michael@0 419 Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
michael@0 420 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
michael@0 421 cleanUpAndGo(server).then(deferred.resolve);
michael@0 422 });
michael@0 423 Svc.Prefs.set("firstSync", "wipeRemote");
michael@0 424 scheduler.scheduleNextSync(-1);
michael@0 425 yield deferred.promise;
michael@0 426 });
michael@0 427
michael@0 428 add_identity_test(this, function test_client_sync_finish_updateClientMode() {
michael@0 429 let server = sync_httpd_setup();
michael@0 430 yield setUp(server);
michael@0 431
michael@0 432 // Confirm defaults.
michael@0 433 do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
michael@0 434 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 435 do_check_false(scheduler.idle);
michael@0 436
michael@0 437 // Trigger a change in interval & threshold by adding a client.
michael@0 438 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 439 do_check_false(scheduler.numClients > 1);
michael@0 440 scheduler.updateClientMode();
michael@0 441 Service.sync();
michael@0 442
michael@0 443 do_check_eq(scheduler.syncThreshold, MULTI_DEVICE_THRESHOLD);
michael@0 444 do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
michael@0 445 do_check_true(scheduler.numClients > 1);
michael@0 446 do_check_false(scheduler.idle);
michael@0 447
michael@0 448 // Resets the number of clients to 0.
michael@0 449 clientsEngine.resetClient();
michael@0 450 Service.sync();
michael@0 451
michael@0 452 // Goes back to single user if # clients is 1.
michael@0 453 do_check_eq(scheduler.numClients, 1);
michael@0 454 do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
michael@0 455 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 456 do_check_false(scheduler.numClients > 1);
michael@0 457 do_check_false(scheduler.idle);
michael@0 458
michael@0 459 yield cleanUpAndGo(server);
michael@0 460 });
michael@0 461
michael@0 462 add_identity_test(this, function test_autoconnect_nextSync_past() {
michael@0 463 let deferred = Promise.defer();
michael@0 464 // nextSync will be 0 by default, so it's way in the past.
michael@0 465
michael@0 466 Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
michael@0 467 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
michael@0 468 cleanUpAndGo(server).then(deferred.resolve);
michael@0 469 });
michael@0 470
michael@0 471 let server = sync_httpd_setup();
michael@0 472 yield setUp(server);
michael@0 473
michael@0 474 scheduler.delayedAutoConnect(0);
michael@0 475 yield deferred.promise;
michael@0 476 });
michael@0 477
michael@0 478 add_identity_test(this, function test_autoconnect_nextSync_future() {
michael@0 479 let deferred = Promise.defer();
michael@0 480 let previousSync = Date.now() + scheduler.syncInterval / 2;
michael@0 481 scheduler.nextSync = previousSync;
michael@0 482 // nextSync rounds to the nearest second.
michael@0 483 let expectedSync = scheduler.nextSync;
michael@0 484 let expectedInterval = expectedSync - Date.now() - 1000;
michael@0 485
michael@0 486 // Ensure we don't actually try to sync (or log in for that matter).
michael@0 487 function onLoginStart() {
michael@0 488 do_throw("Should not get here!");
michael@0 489 }
michael@0 490 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 491
michael@0 492 waitForZeroTimer(function () {
michael@0 493 do_check_eq(scheduler.nextSync, expectedSync);
michael@0 494 do_check_true(scheduler.syncTimer.delay >= expectedInterval);
michael@0 495
michael@0 496 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 497 cleanUpAndGo().then(deferred.resolve);
michael@0 498 });
michael@0 499
michael@0 500 yield configureIdentity({username: "johndoe"});
michael@0 501 scheduler.delayedAutoConnect(0);
michael@0 502 yield deferred.promise;
michael@0 503 });
michael@0 504
michael@0 505 // XXX - this test can't be run with the browserid identity as it relies
michael@0 506 // on the syncKey getter behaving in a certain way...
michael@0 507 add_task(function test_autoconnect_mp_locked() {
michael@0 508 let server = sync_httpd_setup();
michael@0 509 yield setUp(server);
michael@0 510
michael@0 511 // Pretend user did not unlock master password.
michael@0 512 let origLocked = Utils.mpLocked;
michael@0 513 Utils.mpLocked = function() true;
michael@0 514
michael@0 515 let origGetter = Service.identity.__lookupGetter__("syncKey");
michael@0 516 let origSetter = Service.identity.__lookupSetter__("syncKey");
michael@0 517 delete Service.identity.syncKey;
michael@0 518 Service.identity.__defineGetter__("syncKey", function() {
michael@0 519 _("Faking Master Password entry cancelation.");
michael@0 520 throw "User canceled Master Password entry";
michael@0 521 });
michael@0 522
michael@0 523 let deferred = Promise.defer();
michael@0 524 // A locked master password will still trigger a sync, but then we'll hit
michael@0 525 // MASTER_PASSWORD_LOCKED and hence MASTER_PASSWORD_LOCKED_RETRY_INTERVAL.
michael@0 526 Svc.Obs.add("weave:service:login:error", function onLoginError() {
michael@0 527 Svc.Obs.remove("weave:service:login:error", onLoginError);
michael@0 528 Utils.nextTick(function aLittleBitAfterLoginError() {
michael@0 529 do_check_eq(Status.login, MASTER_PASSWORD_LOCKED);
michael@0 530
michael@0 531 Utils.mpLocked = origLocked;
michael@0 532 delete Service.identity.syncKey;
michael@0 533 Service.identity.__defineGetter__("syncKey", origGetter);
michael@0 534 Service.identity.__defineSetter__("syncKey", origSetter);
michael@0 535
michael@0 536 cleanUpAndGo(server).then(deferred.resolve);
michael@0 537 });
michael@0 538 });
michael@0 539
michael@0 540 scheduler.delayedAutoConnect(0);
michael@0 541 yield deferred.promise;
michael@0 542 });
michael@0 543
michael@0 544 add_identity_test(this, function test_no_autoconnect_during_wizard() {
michael@0 545 let server = sync_httpd_setup();
michael@0 546 yield setUp(server);
michael@0 547
michael@0 548 // Simulate the Sync setup wizard.
michael@0 549 Svc.Prefs.set("firstSync", "notReady");
michael@0 550
michael@0 551 // Ensure we don't actually try to sync (or log in for that matter).
michael@0 552 function onLoginStart() {
michael@0 553 do_throw("Should not get here!");
michael@0 554 }
michael@0 555 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 556
michael@0 557 let deferred = Promise.defer();
michael@0 558 waitForZeroTimer(function () {
michael@0 559 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 560 cleanUpAndGo(server).then(deferred.resolve);
michael@0 561 });
michael@0 562
michael@0 563 scheduler.delayedAutoConnect(0);
michael@0 564 yield deferred.promise;
michael@0 565 });
michael@0 566
michael@0 567 add_identity_test(this, function test_no_autoconnect_status_not_ok() {
michael@0 568 let server = sync_httpd_setup();
michael@0 569
michael@0 570 // Ensure we don't actually try to sync (or log in for that matter).
michael@0 571 function onLoginStart() {
michael@0 572 do_throw("Should not get here!");
michael@0 573 }
michael@0 574 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 575
michael@0 576 let deferred = Promise.defer();
michael@0 577 waitForZeroTimer(function () {
michael@0 578 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 579
michael@0 580 do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
michael@0 581 do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME);
michael@0 582
michael@0 583 cleanUpAndGo(server).then(deferred.resolve);
michael@0 584 });
michael@0 585
michael@0 586 scheduler.delayedAutoConnect(0);
michael@0 587 yield deferred.promise;
michael@0 588 });
michael@0 589
michael@0 590 add_identity_test(this, function test_autoconnectDelay_pref() {
michael@0 591 let deferred = Promise.defer();
michael@0 592 Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
michael@0 593 Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
michael@0 594 cleanUpAndGo(server).then(deferred.resolve);
michael@0 595 });
michael@0 596
michael@0 597 Svc.Prefs.set("autoconnectDelay", 1);
michael@0 598
michael@0 599 let server = sync_httpd_setup();
michael@0 600 yield setUp(server);
michael@0 601
michael@0 602 Svc.Obs.notify("weave:service:ready");
michael@0 603
michael@0 604 // autoconnectDelay pref is multiplied by 1000.
michael@0 605 do_check_eq(scheduler._autoTimer.delay, 1000);
michael@0 606 do_check_eq(Status.service, STATUS_OK);
michael@0 607 yield deferred.promise;
michael@0 608 });
michael@0 609
michael@0 610 add_identity_test(this, function test_idle_adjustSyncInterval() {
michael@0 611 // Confirm defaults.
michael@0 612 do_check_eq(scheduler.idle, false);
michael@0 613
michael@0 614 // Single device: nothing changes.
michael@0 615 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 616 do_check_eq(scheduler.idle, true);
michael@0 617 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 618
michael@0 619 // Multiple devices: switch to idle interval.
michael@0 620 scheduler.idle = false;
michael@0 621 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 622 scheduler.updateClientMode();
michael@0 623 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 624 do_check_eq(scheduler.idle, true);
michael@0 625 do_check_eq(scheduler.syncInterval, scheduler.idleInterval);
michael@0 626
michael@0 627 yield cleanUpAndGo();
michael@0 628 });
michael@0 629
michael@0 630 add_identity_test(this, function test_back_triggersSync() {
michael@0 631 // Confirm defaults.
michael@0 632 do_check_false(scheduler.idle);
michael@0 633 do_check_eq(Status.backoffInterval, 0);
michael@0 634
michael@0 635 // Set up: Define 2 clients and put the system in idle.
michael@0 636 scheduler.numClients = 2;
michael@0 637 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 638 do_check_true(scheduler.idle);
michael@0 639
michael@0 640 let deferred = Promise.defer();
michael@0 641 // We don't actually expect the sync (or the login, for that matter) to
michael@0 642 // succeed. We just want to ensure that it was attempted.
michael@0 643 Svc.Obs.add("weave:service:login:error", function onLoginError() {
michael@0 644 Svc.Obs.remove("weave:service:login:error", onLoginError);
michael@0 645 cleanUpAndGo().then(deferred.resolve);
michael@0 646 });
michael@0 647
michael@0 648 // Send an 'active' event to trigger sync soonish.
michael@0 649 scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
michael@0 650 yield deferred.promise;
michael@0 651 });
michael@0 652
michael@0 653 add_identity_test(this, function test_active_triggersSync_observesBackoff() {
michael@0 654 // Confirm defaults.
michael@0 655 do_check_false(scheduler.idle);
michael@0 656
michael@0 657 // Set up: Set backoff, define 2 clients and put the system in idle.
michael@0 658 const BACKOFF = 7337;
michael@0 659 Status.backoffInterval = scheduler.idleInterval + BACKOFF;
michael@0 660 scheduler.numClients = 2;
michael@0 661 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 662 do_check_eq(scheduler.idle, true);
michael@0 663
michael@0 664 function onLoginStart() {
michael@0 665 do_throw("Shouldn't have kicked off a sync!");
michael@0 666 }
michael@0 667 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 668
michael@0 669 let deferred = Promise.defer();
michael@0 670 timer = Utils.namedTimer(function () {
michael@0 671 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 672
michael@0 673 do_check_true(scheduler.nextSync <= Date.now() + Status.backoffInterval);
michael@0 674 do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval);
michael@0 675
michael@0 676 cleanUpAndGo().then(deferred.resolve);
michael@0 677 }, IDLE_OBSERVER_BACK_DELAY * 1.5, {}, "timer");
michael@0 678
michael@0 679 // Send an 'active' event to try to trigger sync soonish.
michael@0 680 scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
michael@0 681 yield deferred.promise;
michael@0 682 });
michael@0 683
michael@0 684 add_identity_test(this, function test_back_debouncing() {
michael@0 685 _("Ensure spurious back-then-idle events, as observed on OS X, don't trigger a sync.");
michael@0 686
michael@0 687 // Confirm defaults.
michael@0 688 do_check_eq(scheduler.idle, false);
michael@0 689
michael@0 690 // Set up: Define 2 clients and put the system in idle.
michael@0 691 scheduler.numClients = 2;
michael@0 692 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 693 do_check_eq(scheduler.idle, true);
michael@0 694
michael@0 695 function onLoginStart() {
michael@0 696 do_throw("Shouldn't have kicked off a sync!");
michael@0 697 }
michael@0 698 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 699
michael@0 700 // Create spurious back-then-idle events as observed on OS X:
michael@0 701 scheduler.observe(null, "active", Svc.Prefs.get("scheduler.idleTime"));
michael@0 702 scheduler.observe(null, "idle", Svc.Prefs.get("scheduler.idleTime"));
michael@0 703
michael@0 704 let deferred = Promise.defer();
michael@0 705 timer = Utils.namedTimer(function () {
michael@0 706 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 707 cleanUpAndGo().then(deferred.resolve);
michael@0 708 }, IDLE_OBSERVER_BACK_DELAY * 1.5, {}, "timer");
michael@0 709 yield deferred.promise;
michael@0 710 });
michael@0 711
michael@0 712 add_identity_test(this, function test_no_sync_node() {
michael@0 713 // Test when Status.sync == NO_SYNC_NODE_FOUND
michael@0 714 // it is not overwritten on sync:finish
michael@0 715 let server = sync_httpd_setup();
michael@0 716 yield setUp(server);
michael@0 717
michael@0 718 Service.serverURL = server.baseURI + "/";
michael@0 719
michael@0 720 Service.sync();
michael@0 721 do_check_eq(Status.sync, NO_SYNC_NODE_FOUND);
michael@0 722 do_check_eq(scheduler.syncTimer.delay, NO_SYNC_NODE_INTERVAL);
michael@0 723
michael@0 724 yield cleanUpAndGo(server);
michael@0 725 });
michael@0 726
michael@0 727 add_identity_test(this, function test_sync_failed_partial_500s() {
michael@0 728 _("Test a 5xx status calls handleSyncError.");
michael@0 729 scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF;
michael@0 730 let server = sync_httpd_setup();
michael@0 731
michael@0 732 let engine = Service.engineManager.get("catapult");
michael@0 733 engine.enabled = true;
michael@0 734 engine.exception = {status: 500};
michael@0 735
michael@0 736 do_check_eq(Status.sync, SYNC_SUCCEEDED);
michael@0 737
michael@0 738 do_check_true(yield setUp(server));
michael@0 739
michael@0 740 Service.sync();
michael@0 741
michael@0 742 do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
michael@0 743
michael@0 744 let maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL);
michael@0 745 do_check_eq(Status.backoffInterval, 0);
michael@0 746 do_check_true(Status.enforceBackoff);
michael@0 747 do_check_eq(scheduler._syncErrors, 4);
michael@0 748 do_check_true(scheduler.nextSync <= (Date.now() + maxInterval));
michael@0 749 do_check_true(scheduler.syncTimer.delay <= maxInterval);
michael@0 750
michael@0 751 yield cleanUpAndGo(server);
michael@0 752 });
michael@0 753
michael@0 754 add_identity_test(this, function test_sync_failed_partial_400s() {
michael@0 755 _("Test a non-5xx status doesn't call handleSyncError.");
michael@0 756 scheduler._syncErrors = MAX_ERROR_COUNT_BEFORE_BACKOFF;
michael@0 757 let server = sync_httpd_setup();
michael@0 758
michael@0 759 let engine = Service.engineManager.get("catapult");
michael@0 760 engine.enabled = true;
michael@0 761 engine.exception = {status: 400};
michael@0 762
michael@0 763 // Have multiple devices for an active interval.
michael@0 764 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 765
michael@0 766 do_check_eq(Status.sync, SYNC_SUCCEEDED);
michael@0 767
michael@0 768 do_check_true(yield setUp(server));
michael@0 769
michael@0 770 Service.sync();
michael@0 771
michael@0 772 do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
michael@0 773 do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
michael@0 774
michael@0 775 do_check_eq(Status.backoffInterval, 0);
michael@0 776 do_check_false(Status.enforceBackoff);
michael@0 777 do_check_eq(scheduler._syncErrors, 0);
michael@0 778 do_check_true(scheduler.nextSync <= (Date.now() + scheduler.activeInterval));
michael@0 779 do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
michael@0 780
michael@0 781 yield cleanUpAndGo(server);
michael@0 782 });
michael@0 783
michael@0 784 add_identity_test(this, function test_sync_X_Weave_Backoff() {
michael@0 785 let server = sync_httpd_setup();
michael@0 786 yield setUp(server);
michael@0 787
michael@0 788 // Use an odd value on purpose so that it doesn't happen to coincide with one
michael@0 789 // of the sync intervals.
michael@0 790 const BACKOFF = 7337;
michael@0 791
michael@0 792 // Extend info/collections so that we can put it into server maintenance mode.
michael@0 793 const INFO_COLLECTIONS = "/1.1/johndoe/info/collections";
michael@0 794 let infoColl = server._handler._overridePaths[INFO_COLLECTIONS];
michael@0 795 let serverBackoff = false;
michael@0 796 function infoCollWithBackoff(request, response) {
michael@0 797 if (serverBackoff) {
michael@0 798 response.setHeader("X-Weave-Backoff", "" + BACKOFF);
michael@0 799 }
michael@0 800 infoColl(request, response);
michael@0 801 }
michael@0 802 server.registerPathHandler(INFO_COLLECTIONS, infoCollWithBackoff);
michael@0 803
michael@0 804 // Pretend we have two clients so that the regular sync interval is
michael@0 805 // sufficiently low.
michael@0 806 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 807 let rec = clientsEngine._store.createRecord("foo", "clients");
michael@0 808 rec.encrypt(Service.collectionKeys.keyForCollection("clients"));
michael@0 809 rec.upload(Service.resource(clientsEngine.engineURL + rec.id));
michael@0 810
michael@0 811 // Sync once to log in and get everything set up. Let's verify our initial
michael@0 812 // values.
michael@0 813 Service.sync();
michael@0 814 do_check_eq(Status.backoffInterval, 0);
michael@0 815 do_check_eq(Status.minimumNextSync, 0);
michael@0 816 do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
michael@0 817 do_check_true(scheduler.nextSync <=
michael@0 818 Date.now() + scheduler.syncInterval);
michael@0 819 // Sanity check that we picked the right value for BACKOFF:
michael@0 820 do_check_true(scheduler.syncInterval < BACKOFF * 1000);
michael@0 821
michael@0 822 // Turn on server maintenance and sync again.
michael@0 823 serverBackoff = true;
michael@0 824 Service.sync();
michael@0 825
michael@0 826 do_check_true(Status.backoffInterval >= BACKOFF * 1000);
michael@0 827 // Allowing 1 second worth of of leeway between when Status.minimumNextSync
michael@0 828 // was set and when this line gets executed.
michael@0 829 let minimumExpectedDelay = (BACKOFF - 1) * 1000;
michael@0 830 do_check_true(Status.minimumNextSync >= Date.now() + minimumExpectedDelay);
michael@0 831
michael@0 832 // Verify that the next sync is actually going to wait that long.
michael@0 833 do_check_true(scheduler.nextSync >= Date.now() + minimumExpectedDelay);
michael@0 834 do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay);
michael@0 835
michael@0 836 yield cleanUpAndGo(server);
michael@0 837 });
michael@0 838
michael@0 839 add_identity_test(this, function test_sync_503_Retry_After() {
michael@0 840 let server = sync_httpd_setup();
michael@0 841 yield setUp(server);
michael@0 842
michael@0 843 // Use an odd value on purpose so that it doesn't happen to coincide with one
michael@0 844 // of the sync intervals.
michael@0 845 const BACKOFF = 7337;
michael@0 846
michael@0 847 // Extend info/collections so that we can put it into server maintenance mode.
michael@0 848 const INFO_COLLECTIONS = "/1.1/johndoe/info/collections";
michael@0 849 let infoColl = server._handler._overridePaths[INFO_COLLECTIONS];
michael@0 850 let serverMaintenance = false;
michael@0 851 function infoCollWithMaintenance(request, response) {
michael@0 852 if (!serverMaintenance) {
michael@0 853 infoColl(request, response);
michael@0 854 return;
michael@0 855 }
michael@0 856 response.setHeader("Retry-After", "" + BACKOFF);
michael@0 857 response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
michael@0 858 }
michael@0 859 server.registerPathHandler(INFO_COLLECTIONS, infoCollWithMaintenance);
michael@0 860
michael@0 861 // Pretend we have two clients so that the regular sync interval is
michael@0 862 // sufficiently low.
michael@0 863 clientsEngine._store.create({id: "foo", cleartext: "bar"});
michael@0 864 let rec = clientsEngine._store.createRecord("foo", "clients");
michael@0 865 rec.encrypt(Service.collectionKeys.keyForCollection("clients"));
michael@0 866 rec.upload(Service.resource(clientsEngine.engineURL + rec.id));
michael@0 867
michael@0 868 // Sync once to log in and get everything set up. Let's verify our initial
michael@0 869 // values.
michael@0 870 Service.sync();
michael@0 871 do_check_false(Status.enforceBackoff);
michael@0 872 do_check_eq(Status.backoffInterval, 0);
michael@0 873 do_check_eq(Status.minimumNextSync, 0);
michael@0 874 do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
michael@0 875 do_check_true(scheduler.nextSync <=
michael@0 876 Date.now() + scheduler.syncInterval);
michael@0 877 // Sanity check that we picked the right value for BACKOFF:
michael@0 878 do_check_true(scheduler.syncInterval < BACKOFF * 1000);
michael@0 879
michael@0 880 // Turn on server maintenance and sync again.
michael@0 881 serverMaintenance = true;
michael@0 882 Service.sync();
michael@0 883
michael@0 884 do_check_true(Status.enforceBackoff);
michael@0 885 do_check_true(Status.backoffInterval >= BACKOFF * 1000);
michael@0 886 // Allowing 1 second worth of of leeway between when Status.minimumNextSync
michael@0 887 // was set and when this line gets executed.
michael@0 888 let minimumExpectedDelay = (BACKOFF - 1) * 1000;
michael@0 889 do_check_true(Status.minimumNextSync >= Date.now() + minimumExpectedDelay);
michael@0 890
michael@0 891 // Verify that the next sync is actually going to wait that long.
michael@0 892 do_check_true(scheduler.nextSync >= Date.now() + minimumExpectedDelay);
michael@0 893 do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay);
michael@0 894
michael@0 895 yield cleanUpAndGo(server);
michael@0 896 });
michael@0 897
michael@0 898 add_identity_test(this, function test_loginError_recoverable_reschedules() {
michael@0 899 _("Verify that a recoverable login error schedules a new sync.");
michael@0 900 yield configureIdentity({username: "johndoe"});
michael@0 901 Service.serverURL = "http://localhost:1234/";
michael@0 902 Service.clusterURL = Service.serverURL;
michael@0 903 Service.persistLogin();
michael@0 904 Status.resetSync(); // reset Status.login
michael@0 905
michael@0 906 let deferred = Promise.defer();
michael@0 907 Svc.Obs.add("weave:service:login:error", function onLoginError() {
michael@0 908 Svc.Obs.remove("weave:service:login:error", onLoginError);
michael@0 909 Utils.nextTick(function aLittleBitAfterLoginError() {
michael@0 910 do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
michael@0 911
michael@0 912 let expectedNextSync = Date.now() + scheduler.syncInterval;
michael@0 913 do_check_true(scheduler.nextSync > Date.now());
michael@0 914 do_check_true(scheduler.nextSync <= expectedNextSync);
michael@0 915 do_check_true(scheduler.syncTimer.delay > 0);
michael@0 916 do_check_true(scheduler.syncTimer.delay <= scheduler.syncInterval);
michael@0 917
michael@0 918 Svc.Obs.remove("weave:service:sync:start", onSyncStart);
michael@0 919 cleanUpAndGo().then(deferred.resolve);
michael@0 920 });
michael@0 921 });
michael@0 922
michael@0 923 // Let's set it up so that a sync is overdue, both in terms of previously
michael@0 924 // scheduled syncs and the global score. We still do not expect an immediate
michael@0 925 // sync because we just tried (duh).
michael@0 926 scheduler.nextSync = Date.now() - 100000;
michael@0 927 scheduler.globalScore = SINGLE_USER_THRESHOLD + 1;
michael@0 928 function onSyncStart() {
michael@0 929 do_throw("Shouldn't have started a sync!");
michael@0 930 }
michael@0 931 Svc.Obs.add("weave:service:sync:start", onSyncStart);
michael@0 932
michael@0 933 // Sanity check.
michael@0 934 do_check_eq(scheduler.syncTimer, null);
michael@0 935 do_check_eq(Status.checkSetup(), STATUS_OK);
michael@0 936 do_check_eq(Status.login, LOGIN_SUCCEEDED);
michael@0 937
michael@0 938 scheduler.scheduleNextSync(0);
michael@0 939 yield deferred.promise;
michael@0 940 });
michael@0 941
michael@0 942 add_identity_test(this, function test_loginError_fatal_clearsTriggers() {
michael@0 943 _("Verify that a fatal login error clears sync triggers.");
michael@0 944 yield configureIdentity({username: "johndoe"});
michael@0 945
michael@0 946 let server = httpd_setup({
michael@0 947 "/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized")
michael@0 948 });
michael@0 949
michael@0 950 Service.serverURL = server.baseURI + "/";
michael@0 951 Service.clusterURL = Service.serverURL;
michael@0 952 Service.persistLogin();
michael@0 953 Status.resetSync(); // reset Status.login
michael@0 954
michael@0 955 let deferred = Promise.defer();
michael@0 956 Svc.Obs.add("weave:service:login:error", function onLoginError() {
michael@0 957 Svc.Obs.remove("weave:service:login:error", onLoginError);
michael@0 958 Utils.nextTick(function aLittleBitAfterLoginError() {
michael@0 959 do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
michael@0 960
michael@0 961 do_check_eq(scheduler.nextSync, 0);
michael@0 962 do_check_eq(scheduler.syncTimer, null);
michael@0 963
michael@0 964 cleanUpAndGo(server).then(deferred.resolve);
michael@0 965 });
michael@0 966 });
michael@0 967
michael@0 968 // Sanity check.
michael@0 969 do_check_eq(scheduler.nextSync, 0);
michael@0 970 do_check_eq(scheduler.syncTimer, null);
michael@0 971 do_check_eq(Status.checkSetup(), STATUS_OK);
michael@0 972 do_check_eq(Status.login, LOGIN_SUCCEEDED);
michael@0 973
michael@0 974 scheduler.scheduleNextSync(0);
michael@0 975 yield deferred.promise;
michael@0 976 });
michael@0 977
michael@0 978 add_identity_test(this, function test_proper_interval_on_only_failing() {
michael@0 979 _("Ensure proper behavior when only failed records are applied.");
michael@0 980
michael@0 981 // If an engine reports that no records succeeded, we shouldn't decrease the
michael@0 982 // sync interval.
michael@0 983 do_check_false(scheduler.hasIncomingItems);
michael@0 984 const INTERVAL = 10000000;
michael@0 985 scheduler.syncInterval = INTERVAL;
michael@0 986
michael@0 987 Svc.Obs.notify("weave:service:sync:applied", {
michael@0 988 applied: 2,
michael@0 989 succeeded: 0,
michael@0 990 failed: 2,
michael@0 991 newFailed: 2,
michael@0 992 reconciled: 0
michael@0 993 });
michael@0 994
michael@0 995 let deferred = Promise.defer();
michael@0 996 Utils.nextTick(function() {
michael@0 997 scheduler.adjustSyncInterval();
michael@0 998 do_check_false(scheduler.hasIncomingItems);
michael@0 999 do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
michael@0 1000
michael@0 1001 deferred.resolve();
michael@0 1002 });
michael@0 1003 yield deferred.promise;
michael@0 1004 });

mercurial