Wed, 31 Dec 2014 06:09:35 +0100
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 | _("Test that node reassignment responses are respected on all kinds of " + |
michael@0 | 5 | "requests."); |
michael@0 | 6 | |
michael@0 | 7 | Cu.import("resource://gre/modules/Log.jsm"); |
michael@0 | 8 | Cu.import("resource://services-common/rest.js"); |
michael@0 | 9 | Cu.import("resource://services-sync/constants.js"); |
michael@0 | 10 | Cu.import("resource://services-sync/service.js"); |
michael@0 | 11 | Cu.import("resource://services-sync/status.js"); |
michael@0 | 12 | Cu.import("resource://services-sync/util.js"); |
michael@0 | 13 | Cu.import("resource://testing-common/services/sync/rotaryengine.js"); |
michael@0 | 14 | Cu.import("resource://testing-common/services/sync/utils.js"); |
michael@0 | 15 | |
michael@0 | 16 | Service.engineManager.clear(); |
michael@0 | 17 | |
michael@0 | 18 | function run_test() { |
michael@0 | 19 | Log.repository.getLogger("Sync.AsyncResource").level = Log.Level.Trace; |
michael@0 | 20 | Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace; |
michael@0 | 21 | Log.repository.getLogger("Sync.Resource").level = Log.Level.Trace; |
michael@0 | 22 | Log.repository.getLogger("Sync.RESTRequest").level = Log.Level.Trace; |
michael@0 | 23 | Log.repository.getLogger("Sync.Service").level = Log.Level.Trace; |
michael@0 | 24 | Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace; |
michael@0 | 25 | initTestLogging(); |
michael@0 | 26 | |
michael@0 | 27 | ensureLegacyIdentityManager(); |
michael@0 | 28 | |
michael@0 | 29 | Service.engineManager.register(RotaryEngine); |
michael@0 | 30 | |
michael@0 | 31 | // None of the failures in this file should result in a UI error. |
michael@0 | 32 | function onUIError() { |
michael@0 | 33 | do_throw("Errors should not be presented in the UI."); |
michael@0 | 34 | } |
michael@0 | 35 | Svc.Obs.add("weave:ui:login:error", onUIError); |
michael@0 | 36 | Svc.Obs.add("weave:ui:sync:error", onUIError); |
michael@0 | 37 | |
michael@0 | 38 | run_next_test(); |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | /** |
michael@0 | 42 | * Emulate the following Zeus config: |
michael@0 | 43 | * $draining = data.get($prefix . $host . " draining"); |
michael@0 | 44 | * if ($draining == "drain.") { |
michael@0 | 45 | * log.warn($log_host_db_status . " migrating=1 (node-reassignment)" . |
michael@0 | 46 | * $log_suffix); |
michael@0 | 47 | * http.sendResponse("401 Node reassignment", $content_type, |
michael@0 | 48 | * '"server request: node reassignment"', ""); |
michael@0 | 49 | * } |
michael@0 | 50 | */ |
michael@0 | 51 | const reassignBody = "\"server request: node reassignment\""; |
michael@0 | 52 | |
michael@0 | 53 | // API-compatible with SyncServer handler. Bind `handler` to something to use |
michael@0 | 54 | // as a ServerCollection handler. |
michael@0 | 55 | function handleReassign(handler, req, resp) { |
michael@0 | 56 | resp.setStatusLine(req.httpVersion, 401, "Node reassignment"); |
michael@0 | 57 | resp.setHeader("Content-Type", "application/json"); |
michael@0 | 58 | resp.bodyOutputStream.write(reassignBody, reassignBody.length); |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | /** |
michael@0 | 62 | * A node assignment handler. |
michael@0 | 63 | */ |
michael@0 | 64 | function installNodeHandler(server, next) { |
michael@0 | 65 | let newNodeBody = server.baseURI; |
michael@0 | 66 | function handleNodeRequest(req, resp) { |
michael@0 | 67 | _("Client made a request for a node reassignment."); |
michael@0 | 68 | resp.setStatusLine(req.httpVersion, 200, "OK"); |
michael@0 | 69 | resp.setHeader("Content-Type", "text/plain"); |
michael@0 | 70 | resp.bodyOutputStream.write(newNodeBody, newNodeBody.length); |
michael@0 | 71 | Utils.nextTick(next); |
michael@0 | 72 | } |
michael@0 | 73 | let nodePath = "/user/1.0/johndoe/node/weave"; |
michael@0 | 74 | server.server.registerPathHandler(nodePath, handleNodeRequest); |
michael@0 | 75 | _("Registered node handler at " + nodePath); |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | function prepareServer() { |
michael@0 | 79 | let deferred = Promise.defer(); |
michael@0 | 80 | configureIdentity({username: "johndoe"}).then(() => { |
michael@0 | 81 | let server = new SyncServer(); |
michael@0 | 82 | server.registerUser("johndoe"); |
michael@0 | 83 | server.start(); |
michael@0 | 84 | Service.serverURL = server.baseURI; |
michael@0 | 85 | Service.clusterURL = server.baseURI; |
michael@0 | 86 | do_check_eq(Service.userAPIURI, server.baseURI + "user/1.0/"); |
michael@0 | 87 | deferred.resolve(server); |
michael@0 | 88 | }); |
michael@0 | 89 | return deferred.promise; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | function getReassigned() { |
michael@0 | 93 | try { |
michael@0 | 94 | return Services.prefs.getBoolPref("services.sync.lastSyncReassigned"); |
michael@0 | 95 | } catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) { |
michael@0 | 96 | return false; |
michael@0 | 97 | } catch (ex) { |
michael@0 | 98 | do_throw("Got exception retrieving lastSyncReassigned: " + |
michael@0 | 99 | Utils.exceptionStr(ex)); |
michael@0 | 100 | } |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | /** |
michael@0 | 104 | * Make a test request to `url`, then watch the result of two syncs |
michael@0 | 105 | * to ensure that a node request was made. |
michael@0 | 106 | * Runs `between` between the two. This can be used to undo deliberate failure |
michael@0 | 107 | * setup, detach observers, etc. |
michael@0 | 108 | */ |
michael@0 | 109 | function syncAndExpectNodeReassignment(server, firstNotification, between, |
michael@0 | 110 | secondNotification, url) { |
michael@0 | 111 | let deferred = Promise.defer(); |
michael@0 | 112 | function onwards() { |
michael@0 | 113 | let nodeFetched = false; |
michael@0 | 114 | function onFirstSync() { |
michael@0 | 115 | _("First sync completed."); |
michael@0 | 116 | Svc.Obs.remove(firstNotification, onFirstSync); |
michael@0 | 117 | Svc.Obs.add(secondNotification, onSecondSync); |
michael@0 | 118 | |
michael@0 | 119 | do_check_eq(Service.clusterURL, ""); |
michael@0 | 120 | |
michael@0 | 121 | // Track whether we fetched node/weave. We want to wait for the second |
michael@0 | 122 | // sync to finish so that we're cleaned up for the next test, so don't |
michael@0 | 123 | // run_next_test in the node handler. |
michael@0 | 124 | nodeFetched = false; |
michael@0 | 125 | |
michael@0 | 126 | // Verify that the client requests a node reassignment. |
michael@0 | 127 | // Install a node handler to watch for these requests. |
michael@0 | 128 | installNodeHandler(server, function () { |
michael@0 | 129 | nodeFetched = true; |
michael@0 | 130 | }); |
michael@0 | 131 | |
michael@0 | 132 | // Allow for tests to clean up error conditions. |
michael@0 | 133 | between(); |
michael@0 | 134 | } |
michael@0 | 135 | function onSecondSync() { |
michael@0 | 136 | _("Second sync completed."); |
michael@0 | 137 | Svc.Obs.remove(secondNotification, onSecondSync); |
michael@0 | 138 | Service.scheduler.clearSyncTriggers(); |
michael@0 | 139 | |
michael@0 | 140 | // Make absolutely sure that any event listeners are done with their work |
michael@0 | 141 | // before we proceed. |
michael@0 | 142 | waitForZeroTimer(function () { |
michael@0 | 143 | _("Second sync nextTick."); |
michael@0 | 144 | do_check_true(nodeFetched); |
michael@0 | 145 | Service.startOver(); |
michael@0 | 146 | server.stop(deferred.resolve); |
michael@0 | 147 | }); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | Svc.Obs.add(firstNotification, onFirstSync); |
michael@0 | 151 | Service.sync(); |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | // Make sure that it works! |
michael@0 | 155 | let request = new RESTRequest(url); |
michael@0 | 156 | request.get(function () { |
michael@0 | 157 | do_check_eq(request.response.status, 401); |
michael@0 | 158 | Utils.nextTick(onwards); |
michael@0 | 159 | }); |
michael@0 | 160 | yield deferred.promise; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | add_task(function test_momentary_401_engine() { |
michael@0 | 164 | _("Test a failure for engine URLs that's resolved by reassignment."); |
michael@0 | 165 | let server = yield prepareServer(); |
michael@0 | 166 | let john = server.user("johndoe"); |
michael@0 | 167 | |
michael@0 | 168 | _("Enabling the Rotary engine."); |
michael@0 | 169 | let engine = Service.engineManager.get("rotary"); |
michael@0 | 170 | engine.enabled = true; |
michael@0 | 171 | |
michael@0 | 172 | // We need the server to be correctly set up prior to experimenting. Do this |
michael@0 | 173 | // through a sync. |
michael@0 | 174 | let global = {syncID: Service.syncID, |
michael@0 | 175 | storageVersion: STORAGE_VERSION, |
michael@0 | 176 | rotary: {version: engine.version, |
michael@0 | 177 | syncID: engine.syncID}} |
michael@0 | 178 | john.createCollection("meta").insert("global", global); |
michael@0 | 179 | |
michael@0 | 180 | _("First sync to prepare server contents."); |
michael@0 | 181 | Service.sync(); |
michael@0 | 182 | |
michael@0 | 183 | _("Setting up Rotary collection to 401."); |
michael@0 | 184 | let rotary = john.createCollection("rotary"); |
michael@0 | 185 | let oldHandler = rotary.collectionHandler; |
michael@0 | 186 | rotary.collectionHandler = handleReassign.bind(this, undefined); |
michael@0 | 187 | |
michael@0 | 188 | // We want to verify that the clusterURL pref has been cleared after a 401 |
michael@0 | 189 | // inside a sync. Flag the Rotary engine to need syncing. |
michael@0 | 190 | john.collection("rotary").timestamp += 1000; |
michael@0 | 191 | |
michael@0 | 192 | function between() { |
michael@0 | 193 | _("Undoing test changes."); |
michael@0 | 194 | rotary.collectionHandler = oldHandler; |
michael@0 | 195 | |
michael@0 | 196 | function onLoginStart() { |
michael@0 | 197 | // lastSyncReassigned shouldn't be cleared until a sync has succeeded. |
michael@0 | 198 | _("Ensuring that lastSyncReassigned is still set at next sync start."); |
michael@0 | 199 | Svc.Obs.remove("weave:service:login:start", onLoginStart); |
michael@0 | 200 | do_check_true(getReassigned()); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | _("Adding observer that lastSyncReassigned is still set on login."); |
michael@0 | 204 | Svc.Obs.add("weave:service:login:start", onLoginStart); |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | yield syncAndExpectNodeReassignment(server, |
michael@0 | 208 | "weave:service:sync:finish", |
michael@0 | 209 | between, |
michael@0 | 210 | "weave:service:sync:finish", |
michael@0 | 211 | Service.storageURL + "rotary"); |
michael@0 | 212 | }); |
michael@0 | 213 | |
michael@0 | 214 | // This test ends up being a failing fetch *after we're already logged in*. |
michael@0 | 215 | add_task(function test_momentary_401_info_collections() { |
michael@0 | 216 | _("Test a failure for info/collections that's resolved by reassignment."); |
michael@0 | 217 | let server = yield prepareServer(); |
michael@0 | 218 | |
michael@0 | 219 | _("First sync to prepare server contents."); |
michael@0 | 220 | Service.sync(); |
michael@0 | 221 | |
michael@0 | 222 | // Return a 401 for info requests, particularly info/collections. |
michael@0 | 223 | let oldHandler = server.toplevelHandlers.info; |
michael@0 | 224 | server.toplevelHandlers.info = handleReassign; |
michael@0 | 225 | |
michael@0 | 226 | function undo() { |
michael@0 | 227 | _("Undoing test changes."); |
michael@0 | 228 | server.toplevelHandlers.info = oldHandler; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | yield syncAndExpectNodeReassignment(server, |
michael@0 | 232 | "weave:service:sync:error", |
michael@0 | 233 | undo, |
michael@0 | 234 | "weave:service:sync:finish", |
michael@0 | 235 | Service.infoURL); |
michael@0 | 236 | }); |
michael@0 | 237 | |
michael@0 | 238 | add_task(function test_momentary_401_storage_loggedin() { |
michael@0 | 239 | _("Test a failure for any storage URL, not just engine parts. " + |
michael@0 | 240 | "Resolved by reassignment."); |
michael@0 | 241 | let server = yield prepareServer(); |
michael@0 | 242 | |
michael@0 | 243 | _("Performing initial sync to ensure we are logged in.") |
michael@0 | 244 | Service.sync(); |
michael@0 | 245 | |
michael@0 | 246 | // Return a 401 for all storage requests. |
michael@0 | 247 | let oldHandler = server.toplevelHandlers.storage; |
michael@0 | 248 | server.toplevelHandlers.storage = handleReassign; |
michael@0 | 249 | |
michael@0 | 250 | function undo() { |
michael@0 | 251 | _("Undoing test changes."); |
michael@0 | 252 | server.toplevelHandlers.storage = oldHandler; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | do_check_true(Service.isLoggedIn, "already logged in"); |
michael@0 | 256 | yield syncAndExpectNodeReassignment(server, |
michael@0 | 257 | "weave:service:sync:error", |
michael@0 | 258 | undo, |
michael@0 | 259 | "weave:service:sync:finish", |
michael@0 | 260 | Service.storageURL + "meta/global"); |
michael@0 | 261 | }); |
michael@0 | 262 | |
michael@0 | 263 | add_task(function test_momentary_401_storage_loggedout() { |
michael@0 | 264 | _("Test a failure for any storage URL, not just engine parts. " + |
michael@0 | 265 | "Resolved by reassignment."); |
michael@0 | 266 | let server = yield prepareServer(); |
michael@0 | 267 | |
michael@0 | 268 | // Return a 401 for all storage requests. |
michael@0 | 269 | let oldHandler = server.toplevelHandlers.storage; |
michael@0 | 270 | server.toplevelHandlers.storage = handleReassign; |
michael@0 | 271 | |
michael@0 | 272 | function undo() { |
michael@0 | 273 | _("Undoing test changes."); |
michael@0 | 274 | server.toplevelHandlers.storage = oldHandler; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | do_check_false(Service.isLoggedIn, "not already logged in"); |
michael@0 | 278 | yield syncAndExpectNodeReassignment(server, |
michael@0 | 279 | "weave:service:login:error", |
michael@0 | 280 | undo, |
michael@0 | 281 | "weave:service:sync:finish", |
michael@0 | 282 | Service.storageURL + "meta/global"); |
michael@0 | 283 | }); |
michael@0 | 284 | |
michael@0 | 285 | add_task(function test_loop_avoidance_storage() { |
michael@0 | 286 | _("Test that a repeated failure doesn't result in a sync loop " + |
michael@0 | 287 | "if node reassignment cannot resolve the failure."); |
michael@0 | 288 | |
michael@0 | 289 | let server = yield prepareServer(); |
michael@0 | 290 | |
michael@0 | 291 | // Return a 401 for all storage requests. |
michael@0 | 292 | let oldHandler = server.toplevelHandlers.storage; |
michael@0 | 293 | server.toplevelHandlers.storage = handleReassign; |
michael@0 | 294 | |
michael@0 | 295 | let firstNotification = "weave:service:login:error"; |
michael@0 | 296 | let secondNotification = "weave:service:login:error"; |
michael@0 | 297 | let thirdNotification = "weave:service:sync:finish"; |
michael@0 | 298 | |
michael@0 | 299 | let nodeFetched = false; |
michael@0 | 300 | let deferred = Promise.defer(); |
michael@0 | 301 | |
michael@0 | 302 | // Track the time. We want to make sure the duration between the first and |
michael@0 | 303 | // second sync is small, and then that the duration between second and third |
michael@0 | 304 | // is set to be large. |
michael@0 | 305 | let now; |
michael@0 | 306 | |
michael@0 | 307 | function onFirstSync() { |
michael@0 | 308 | _("First sync completed."); |
michael@0 | 309 | Svc.Obs.remove(firstNotification, onFirstSync); |
michael@0 | 310 | Svc.Obs.add(secondNotification, onSecondSync); |
michael@0 | 311 | |
michael@0 | 312 | do_check_eq(Service.clusterURL, ""); |
michael@0 | 313 | |
michael@0 | 314 | // We got a 401 mid-sync, and set the pref accordingly. |
michael@0 | 315 | do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned")); |
michael@0 | 316 | |
michael@0 | 317 | // Track whether we fetched node/weave. We want to wait for the second |
michael@0 | 318 | // sync to finish so that we're cleaned up for the next test, so don't |
michael@0 | 319 | // run_next_test in the node handler. |
michael@0 | 320 | nodeFetched = false; |
michael@0 | 321 | |
michael@0 | 322 | // Verify that the client requests a node reassignment. |
michael@0 | 323 | // Install a node handler to watch for these requests. |
michael@0 | 324 | installNodeHandler(server, function () { |
michael@0 | 325 | nodeFetched = true; |
michael@0 | 326 | }); |
michael@0 | 327 | |
michael@0 | 328 | // Update the timestamp. |
michael@0 | 329 | now = Date.now(); |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | function onSecondSync() { |
michael@0 | 333 | _("Second sync completed."); |
michael@0 | 334 | Svc.Obs.remove(secondNotification, onSecondSync); |
michael@0 | 335 | Svc.Obs.add(thirdNotification, onThirdSync); |
michael@0 | 336 | |
michael@0 | 337 | // This sync occurred within the backoff interval. |
michael@0 | 338 | let elapsedTime = Date.now() - now; |
michael@0 | 339 | do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL); |
michael@0 | 340 | |
michael@0 | 341 | // This pref will be true until a sync completes successfully. |
michael@0 | 342 | do_check_true(getReassigned()); |
michael@0 | 343 | |
michael@0 | 344 | // The timer will be set for some distant time. |
michael@0 | 345 | // We store nextSync in prefs, which offers us only limited resolution. |
michael@0 | 346 | // Include that logic here. |
michael@0 | 347 | let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000); |
michael@0 | 348 | _("Next sync scheduled for " + Service.scheduler.nextSync); |
michael@0 | 349 | _("Expected to be slightly greater than " + expectedNextSync); |
michael@0 | 350 | |
michael@0 | 351 | do_check_true(Service.scheduler.nextSync >= expectedNextSync); |
michael@0 | 352 | do_check_true(!!Service.scheduler.syncTimer); |
michael@0 | 353 | |
michael@0 | 354 | // Undo our evil scheme. |
michael@0 | 355 | server.toplevelHandlers.storage = oldHandler; |
michael@0 | 356 | |
michael@0 | 357 | // Bring the timer forward to kick off a successful sync, so we can watch |
michael@0 | 358 | // the pref get cleared. |
michael@0 | 359 | Service.scheduler.scheduleNextSync(0); |
michael@0 | 360 | } |
michael@0 | 361 | function onThirdSync() { |
michael@0 | 362 | Svc.Obs.remove(thirdNotification, onThirdSync); |
michael@0 | 363 | |
michael@0 | 364 | // That'll do for now; no more syncs. |
michael@0 | 365 | Service.scheduler.clearSyncTriggers(); |
michael@0 | 366 | |
michael@0 | 367 | // Make absolutely sure that any event listeners are done with their work |
michael@0 | 368 | // before we proceed. |
michael@0 | 369 | waitForZeroTimer(function () { |
michael@0 | 370 | _("Third sync nextTick."); |
michael@0 | 371 | do_check_false(getReassigned()); |
michael@0 | 372 | do_check_true(nodeFetched); |
michael@0 | 373 | Service.startOver(); |
michael@0 | 374 | server.stop(deferred.resolve); |
michael@0 | 375 | }); |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | Svc.Obs.add(firstNotification, onFirstSync); |
michael@0 | 379 | |
michael@0 | 380 | now = Date.now(); |
michael@0 | 381 | Service.sync(); |
michael@0 | 382 | yield deferred.promise; |
michael@0 | 383 | }); |
michael@0 | 384 | |
michael@0 | 385 | add_task(function test_loop_avoidance_engine() { |
michael@0 | 386 | _("Test that a repeated 401 in an engine doesn't result in a sync loop " + |
michael@0 | 387 | "if node reassignment cannot resolve the failure."); |
michael@0 | 388 | let server = yield prepareServer(); |
michael@0 | 389 | let john = server.user("johndoe"); |
michael@0 | 390 | |
michael@0 | 391 | _("Enabling the Rotary engine."); |
michael@0 | 392 | let engine = Service.engineManager.get("rotary"); |
michael@0 | 393 | engine.enabled = true; |
michael@0 | 394 | let deferred = Promise.defer(); |
michael@0 | 395 | |
michael@0 | 396 | // We need the server to be correctly set up prior to experimenting. Do this |
michael@0 | 397 | // through a sync. |
michael@0 | 398 | let global = {syncID: Service.syncID, |
michael@0 | 399 | storageVersion: STORAGE_VERSION, |
michael@0 | 400 | rotary: {version: engine.version, |
michael@0 | 401 | syncID: engine.syncID}} |
michael@0 | 402 | john.createCollection("meta").insert("global", global); |
michael@0 | 403 | |
michael@0 | 404 | _("First sync to prepare server contents."); |
michael@0 | 405 | Service.sync(); |
michael@0 | 406 | |
michael@0 | 407 | _("Setting up Rotary collection to 401."); |
michael@0 | 408 | let rotary = john.createCollection("rotary"); |
michael@0 | 409 | let oldHandler = rotary.collectionHandler; |
michael@0 | 410 | rotary.collectionHandler = handleReassign.bind(this, undefined); |
michael@0 | 411 | |
michael@0 | 412 | // Flag the Rotary engine to need syncing. |
michael@0 | 413 | john.collection("rotary").timestamp += 1000; |
michael@0 | 414 | |
michael@0 | 415 | function onLoginStart() { |
michael@0 | 416 | // lastSyncReassigned shouldn't be cleared until a sync has succeeded. |
michael@0 | 417 | _("Ensuring that lastSyncReassigned is still set at next sync start."); |
michael@0 | 418 | do_check_true(getReassigned()); |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | function beforeSuccessfulSync() { |
michael@0 | 422 | _("Undoing test changes."); |
michael@0 | 423 | rotary.collectionHandler = oldHandler; |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | function afterSuccessfulSync() { |
michael@0 | 427 | Svc.Obs.remove("weave:service:login:start", onLoginStart); |
michael@0 | 428 | Service.startOver(); |
michael@0 | 429 | server.stop(deferred.resolve); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | let firstNotification = "weave:service:sync:finish"; |
michael@0 | 433 | let secondNotification = "weave:service:sync:finish"; |
michael@0 | 434 | let thirdNotification = "weave:service:sync:finish"; |
michael@0 | 435 | |
michael@0 | 436 | let nodeFetched = false; |
michael@0 | 437 | |
michael@0 | 438 | // Track the time. We want to make sure the duration between the first and |
michael@0 | 439 | // second sync is small, and then that the duration between second and third |
michael@0 | 440 | // is set to be large. |
michael@0 | 441 | let now; |
michael@0 | 442 | |
michael@0 | 443 | function onFirstSync() { |
michael@0 | 444 | _("First sync completed."); |
michael@0 | 445 | Svc.Obs.remove(firstNotification, onFirstSync); |
michael@0 | 446 | Svc.Obs.add(secondNotification, onSecondSync); |
michael@0 | 447 | |
michael@0 | 448 | do_check_eq(Service.clusterURL, ""); |
michael@0 | 449 | |
michael@0 | 450 | _("Adding observer that lastSyncReassigned is still set on login."); |
michael@0 | 451 | Svc.Obs.add("weave:service:login:start", onLoginStart); |
michael@0 | 452 | |
michael@0 | 453 | // We got a 401 mid-sync, and set the pref accordingly. |
michael@0 | 454 | do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned")); |
michael@0 | 455 | |
michael@0 | 456 | // Track whether we fetched node/weave. We want to wait for the second |
michael@0 | 457 | // sync to finish so that we're cleaned up for the next test, so don't |
michael@0 | 458 | // run_next_test in the node handler. |
michael@0 | 459 | nodeFetched = false; |
michael@0 | 460 | |
michael@0 | 461 | // Verify that the client requests a node reassignment. |
michael@0 | 462 | // Install a node handler to watch for these requests. |
michael@0 | 463 | installNodeHandler(server, function () { |
michael@0 | 464 | nodeFetched = true; |
michael@0 | 465 | }); |
michael@0 | 466 | |
michael@0 | 467 | // Update the timestamp. |
michael@0 | 468 | now = Date.now(); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | function onSecondSync() { |
michael@0 | 472 | _("Second sync completed."); |
michael@0 | 473 | Svc.Obs.remove(secondNotification, onSecondSync); |
michael@0 | 474 | Svc.Obs.add(thirdNotification, onThirdSync); |
michael@0 | 475 | |
michael@0 | 476 | // This sync occurred within the backoff interval. |
michael@0 | 477 | let elapsedTime = Date.now() - now; |
michael@0 | 478 | do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL); |
michael@0 | 479 | |
michael@0 | 480 | // This pref will be true until a sync completes successfully. |
michael@0 | 481 | do_check_true(getReassigned()); |
michael@0 | 482 | |
michael@0 | 483 | // The timer will be set for some distant time. |
michael@0 | 484 | // We store nextSync in prefs, which offers us only limited resolution. |
michael@0 | 485 | // Include that logic here. |
michael@0 | 486 | let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000); |
michael@0 | 487 | _("Next sync scheduled for " + Service.scheduler.nextSync); |
michael@0 | 488 | _("Expected to be slightly greater than " + expectedNextSync); |
michael@0 | 489 | |
michael@0 | 490 | do_check_true(Service.scheduler.nextSync >= expectedNextSync); |
michael@0 | 491 | do_check_true(!!Service.scheduler.syncTimer); |
michael@0 | 492 | |
michael@0 | 493 | // Undo our evil scheme. |
michael@0 | 494 | beforeSuccessfulSync(); |
michael@0 | 495 | |
michael@0 | 496 | // Bring the timer forward to kick off a successful sync, so we can watch |
michael@0 | 497 | // the pref get cleared. |
michael@0 | 498 | Service.scheduler.scheduleNextSync(0); |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | function onThirdSync() { |
michael@0 | 502 | Svc.Obs.remove(thirdNotification, onThirdSync); |
michael@0 | 503 | |
michael@0 | 504 | // That'll do for now; no more syncs. |
michael@0 | 505 | Service.scheduler.clearSyncTriggers(); |
michael@0 | 506 | |
michael@0 | 507 | // Make absolutely sure that any event listeners are done with their work |
michael@0 | 508 | // before we proceed. |
michael@0 | 509 | waitForZeroTimer(function () { |
michael@0 | 510 | _("Third sync nextTick."); |
michael@0 | 511 | do_check_false(getReassigned()); |
michael@0 | 512 | do_check_true(nodeFetched); |
michael@0 | 513 | afterSuccessfulSync(); |
michael@0 | 514 | }); |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | Svc.Obs.add(firstNotification, onFirstSync); |
michael@0 | 518 | |
michael@0 | 519 | now = Date.now(); |
michael@0 | 520 | Service.sync(); |
michael@0 | 521 | yield deferred.promise; |
michael@0 | 522 | }); |