services/sync/tests/unit/test_fxa_node_reassignment.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.

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

mercurial