services/sync/tests/unit/test_fxa_node_reassignment.js

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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 happens correctly using the FxA identity mgr.");
michael@0 5 // The node-reassignment logic is quite different for FxA than for the legacy
michael@0 6 // provider. In particular, there's no special request necessary for
michael@0 7 // reassignment - it comes from the token server - so we need to ensure the
michael@0 8 // Fxa cluster manager grabs a new token.
michael@0 9
michael@0 10 Cu.import("resource://gre/modules/Log.jsm");
michael@0 11 Cu.import("resource://services-common/rest.js");
michael@0 12 Cu.import("resource://services-sync/constants.js");
michael@0 13 Cu.import("resource://services-sync/service.js");
michael@0 14 Cu.import("resource://services-sync/status.js");
michael@0 15 Cu.import("resource://services-sync/util.js");
michael@0 16 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
michael@0 17 Cu.import("resource://services-sync/browserid_identity.js");
michael@0 18 Cu.import("resource://testing-common/services/sync/utils.js");
michael@0 19
michael@0 20 Service.engineManager.clear();
michael@0 21
michael@0 22 function run_test() {
michael@0 23 Log.repository.getLogger("Sync.AsyncResource").level = Log.Level.Trace;
michael@0 24 Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
michael@0 25 Log.repository.getLogger("Sync.Resource").level = Log.Level.Trace;
michael@0 26 Log.repository.getLogger("Sync.RESTRequest").level = Log.Level.Trace;
michael@0 27 Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
michael@0 28 Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
michael@0 29 initTestLogging();
michael@0 30
michael@0 31 Service.engineManager.register(RotaryEngine);
michael@0 32
michael@0 33 // Setup the FxA identity manager and cluster manager.
michael@0 34 Status.__authManager = Service.identity = new BrowserIDManager();
michael@0 35 Service._clusterManager = Service.identity.createClusterManager(Service);
michael@0 36
michael@0 37 // None of the failures in this file should result in a UI error.
michael@0 38 function onUIError() {
michael@0 39 do_throw("Errors should not be presented in the UI.");
michael@0 40 }
michael@0 41 Svc.Obs.add("weave:ui:login:error", onUIError);
michael@0 42 Svc.Obs.add("weave:ui:sync:error", onUIError);
michael@0 43
michael@0 44 run_next_test();
michael@0 45 }
michael@0 46
michael@0 47
michael@0 48 // API-compatible with SyncServer handler. Bind `handler` to something to use
michael@0 49 // as a ServerCollection handler.
michael@0 50 function handleReassign(handler, req, resp) {
michael@0 51 resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
michael@0 52 resp.setHeader("Content-Type", "application/json");
michael@0 53 let reassignBody = JSON.stringify({error: "401inator in place"});
michael@0 54 resp.bodyOutputStream.write(reassignBody, reassignBody.length);
michael@0 55 }
michael@0 56
michael@0 57 let numTokenRequests = 0;
michael@0 58
michael@0 59 function prepareServer(cbAfterTokenFetch) {
michael@0 60 let config = makeIdentityConfig({username: "johndoe"});
michael@0 61 let server = new SyncServer();
michael@0 62 server.registerUser("johndoe");
michael@0 63 server.start();
michael@0 64
michael@0 65 // Set the token endpoint for the initial token request that's done implicitly
michael@0 66 // via configureIdentity.
michael@0 67 config.fxaccount.token.endpoint = server.baseURI + "1.1/johndoe";
michael@0 68 // And future token fetches will do magic around numReassigns.
michael@0 69 let numReassigns = 0;
michael@0 70 return configureIdentity(config).then(() => {
michael@0 71 Service.identity._tokenServerClient = {
michael@0 72 getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
michael@0 73 // Build a new URL with trailing zeros for the SYNC_VERSION part - this
michael@0 74 // will still be seen as equivalent by the test server, but different
michael@0 75 // by sync itself.
michael@0 76 numReassigns += 1;
michael@0 77 let trailingZeros = new Array(numReassigns + 1).join('0');
michael@0 78 let token = config.fxaccount.token;
michael@0 79 token.endpoint = server.baseURI + "1.1" + trailingZeros + "/johndoe";
michael@0 80 token.uid = config.username;
michael@0 81 numTokenRequests += 1;
michael@0 82 cb(null, token);
michael@0 83 if (cbAfterTokenFetch) {
michael@0 84 cbAfterTokenFetch();
michael@0 85 }
michael@0 86 },
michael@0 87 };
michael@0 88 Service.clusterURL = config.fxaccount.token.endpoint;
michael@0 89 return server;
michael@0 90 });
michael@0 91 }
michael@0 92
michael@0 93 function getReassigned() {
michael@0 94 try {
michael@0 95 return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
michael@0 96 } catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
michael@0 97 return false;
michael@0 98 } catch (ex) {
michael@0 99 do_throw("Got exception retrieving lastSyncReassigned: " +
michael@0 100 Utils.exceptionStr(ex));
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 /**
michael@0 105 * Make a test request to `url`, then watch the result of two syncs
michael@0 106 * to ensure that a node request was made.
michael@0 107 * Runs `between` between the two. This can be used to undo deliberate failure
michael@0 108 * setup, detach observers, etc.
michael@0 109 */
michael@0 110 function syncAndExpectNodeReassignment(server, firstNotification, between,
michael@0 111 secondNotification, url) {
michael@0 112 _("Starting syncAndExpectNodeReassignment\n");
michael@0 113 let deferred = Promise.defer();
michael@0 114 function onwards() {
michael@0 115 let numTokenRequestsBefore;
michael@0 116 function onFirstSync() {
michael@0 117 _("First sync completed.");
michael@0 118 Svc.Obs.remove(firstNotification, onFirstSync);
michael@0 119 Svc.Obs.add(secondNotification, onSecondSync);
michael@0 120
michael@0 121 do_check_eq(Service.clusterURL, "");
michael@0 122
michael@0 123 // Track whether we fetched a new token.
michael@0 124 numTokenRequestsBefore = numTokenRequests;
michael@0 125
michael@0 126 // Allow for tests to clean up error conditions.
michael@0 127 between();
michael@0 128 }
michael@0 129 function onSecondSync() {
michael@0 130 _("Second sync completed.");
michael@0 131 Svc.Obs.remove(secondNotification, onSecondSync);
michael@0 132 Service.scheduler.clearSyncTriggers();
michael@0 133
michael@0 134 // Make absolutely sure that any event listeners are done with their work
michael@0 135 // before we proceed.
michael@0 136 waitForZeroTimer(function () {
michael@0 137 _("Second sync nextTick.");
michael@0 138 do_check_eq(numTokenRequests, numTokenRequestsBefore + 1, "fetched a new token");
michael@0 139 Service.startOver();
michael@0 140 server.stop(deferred.resolve);
michael@0 141 });
michael@0 142 }
michael@0 143
michael@0 144 Svc.Obs.add(firstNotification, onFirstSync);
michael@0 145 Service.sync();
michael@0 146 }
michael@0 147
michael@0 148 // Make sure that it works!
michael@0 149 _("Making request to " + url + " which should 401");
michael@0 150 let request = new RESTRequest(url);
michael@0 151 request.get(function () {
michael@0 152 do_check_eq(request.response.status, 401);
michael@0 153 Utils.nextTick(onwards);
michael@0 154 });
michael@0 155 yield deferred.promise;
michael@0 156 }
michael@0 157
michael@0 158 add_task(function test_momentary_401_engine() {
michael@0 159 _("Test a failure for engine URLs that's resolved by reassignment.");
michael@0 160 let server = yield prepareServer();
michael@0 161 let john = server.user("johndoe");
michael@0 162
michael@0 163 _("Enabling the Rotary engine.");
michael@0 164 let engine = Service.engineManager.get("rotary");
michael@0 165 engine.enabled = true;
michael@0 166
michael@0 167 // We need the server to be correctly set up prior to experimenting. Do this
michael@0 168 // through a sync.
michael@0 169 let global = {syncID: Service.syncID,
michael@0 170 storageVersion: STORAGE_VERSION,
michael@0 171 rotary: {version: engine.version,
michael@0 172 syncID: engine.syncID}}
michael@0 173 john.createCollection("meta").insert("global", global);
michael@0 174
michael@0 175 _("First sync to prepare server contents.");
michael@0 176 Service.sync();
michael@0 177
michael@0 178 _("Setting up Rotary collection to 401.");
michael@0 179 let rotary = john.createCollection("rotary");
michael@0 180 let oldHandler = rotary.collectionHandler;
michael@0 181 rotary.collectionHandler = handleReassign.bind(this, undefined);
michael@0 182
michael@0 183 // We want to verify that the clusterURL pref has been cleared after a 401
michael@0 184 // inside a sync. Flag the Rotary engine to need syncing.
michael@0 185 john.collection("rotary").timestamp += 1000;
michael@0 186
michael@0 187 function between() {
michael@0 188 _("Undoing test changes.");
michael@0 189 rotary.collectionHandler = oldHandler;
michael@0 190
michael@0 191 function onLoginStart() {
michael@0 192 // lastSyncReassigned shouldn't be cleared until a sync has succeeded.
michael@0 193 _("Ensuring that lastSyncReassigned is still set at next sync start.");
michael@0 194 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 195 do_check_true(getReassigned());
michael@0 196 }
michael@0 197
michael@0 198 _("Adding observer that lastSyncReassigned is still set on login.");
michael@0 199 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 200 }
michael@0 201
michael@0 202 yield syncAndExpectNodeReassignment(server,
michael@0 203 "weave:service:sync:finish",
michael@0 204 between,
michael@0 205 "weave:service:sync:finish",
michael@0 206 Service.storageURL + "rotary");
michael@0 207 });
michael@0 208
michael@0 209 // This test ends up being a failing info fetch *after we're already logged in*.
michael@0 210 add_task(function test_momentary_401_info_collections_loggedin() {
michael@0 211 _("Test a failure for info/collections after login that's resolved by reassignment.");
michael@0 212 let server = yield prepareServer();
michael@0 213
michael@0 214 _("First sync to prepare server contents.");
michael@0 215 Service.sync();
michael@0 216
michael@0 217 _("Arrange for info/collections to return a 401.");
michael@0 218 let oldHandler = server.toplevelHandlers.info;
michael@0 219 server.toplevelHandlers.info = handleReassign;
michael@0 220
michael@0 221 function undo() {
michael@0 222 _("Undoing test changes.");
michael@0 223 server.toplevelHandlers.info = oldHandler;
michael@0 224 }
michael@0 225
michael@0 226 do_check_true(Service.isLoggedIn, "already logged in");
michael@0 227
michael@0 228 yield syncAndExpectNodeReassignment(server,
michael@0 229 "weave:service:sync:error",
michael@0 230 undo,
michael@0 231 "weave:service:sync:finish",
michael@0 232 Service.infoURL);
michael@0 233 });
michael@0 234
michael@0 235 // This test ends up being a failing info fetch *before we're logged in*.
michael@0 236 // In this case we expect to recover during the login phase - so the first
michael@0 237 // sync succeeds.
michael@0 238 add_task(function test_momentary_401_info_collections_loggedout() {
michael@0 239 _("Test a failure for info/collections before login that's resolved by reassignment.");
michael@0 240
michael@0 241 let oldHandler;
michael@0 242 let sawTokenFetch = false;
michael@0 243
michael@0 244 function afterTokenFetch() {
michael@0 245 // After a single token fetch, we undo our evil handleReassign hack, so
michael@0 246 // the next /info request returns the collection instead of a 401
michael@0 247 server.toplevelHandlers.info = oldHandler;
michael@0 248 sawTokenFetch = true;
michael@0 249 }
michael@0 250
michael@0 251 let server = yield prepareServer(afterTokenFetch);
michael@0 252
michael@0 253 // Return a 401 for the next /info request - it will be reset immediately
michael@0 254 // after a new token is fetched.
michael@0 255 oldHandler = server.toplevelHandlers.info
michael@0 256 server.toplevelHandlers.info = handleReassign;
michael@0 257
michael@0 258 do_check_false(Service.isLoggedIn, "not already logged in");
michael@0 259
michael@0 260 Service.sync();
michael@0 261 do_check_eq(Status.sync, SYNC_SUCCEEDED, "sync succeeded");
michael@0 262 // sync was successful - check we grabbed a new token.
michael@0 263 do_check_true(sawTokenFetch, "a new token was fetched by this test.")
michael@0 264 // and we are done.
michael@0 265 Service.startOver();
michael@0 266 let deferred = Promise.defer();
michael@0 267 server.stop(deferred.resolve);
michael@0 268 yield deferred.promise;
michael@0 269 });
michael@0 270
michael@0 271 // This test ends up being a failing meta/global fetch *after we're already logged in*.
michael@0 272 add_task(function test_momentary_401_storage_loggedin() {
michael@0 273 _("Test a failure for any storage URL after login that's resolved by" +
michael@0 274 "reassignment.");
michael@0 275 let server = yield prepareServer();
michael@0 276
michael@0 277 _("First sync to prepare server contents.");
michael@0 278 Service.sync();
michael@0 279
michael@0 280 _("Arrange for meta/global to return a 401.");
michael@0 281 let oldHandler = server.toplevelHandlers.storage;
michael@0 282 server.toplevelHandlers.storage = handleReassign;
michael@0 283
michael@0 284 function undo() {
michael@0 285 _("Undoing test changes.");
michael@0 286 server.toplevelHandlers.storage = oldHandler;
michael@0 287 }
michael@0 288
michael@0 289 do_check_true(Service.isLoggedIn, "already logged in");
michael@0 290
michael@0 291 yield syncAndExpectNodeReassignment(server,
michael@0 292 "weave:service:sync:error",
michael@0 293 undo,
michael@0 294 "weave:service:sync:finish",
michael@0 295 Service.storageURL + "meta/global");
michael@0 296 });
michael@0 297
michael@0 298 // This test ends up being a failing meta/global fetch *before we've logged in*.
michael@0 299 add_task(function test_momentary_401_storage_loggedout() {
michael@0 300 _("Test a failure for any storage URL before login, not just engine parts. " +
michael@0 301 "Resolved by reassignment.");
michael@0 302 let server = yield prepareServer();
michael@0 303
michael@0 304 // Return a 401 for all storage requests.
michael@0 305 let oldHandler = server.toplevelHandlers.storage;
michael@0 306 server.toplevelHandlers.storage = handleReassign;
michael@0 307
michael@0 308 function undo() {
michael@0 309 _("Undoing test changes.");
michael@0 310 server.toplevelHandlers.storage = oldHandler;
michael@0 311 }
michael@0 312
michael@0 313 do_check_false(Service.isLoggedIn, "already logged in");
michael@0 314
michael@0 315 yield syncAndExpectNodeReassignment(server,
michael@0 316 "weave:service:login:error",
michael@0 317 undo,
michael@0 318 "weave:service:sync:finish",
michael@0 319 Service.storageURL + "meta/global");
michael@0 320 });
michael@0 321

mercurial