Bug 884421 - Automatic network port selection for /services tests; r=rnewman
authorGregory Szorc <gps@mozilla.com>
Tue, 16 Jul 2013 17:14:21 -0700
changeset 151565 b817406485f8c5ee866ce16fa020fd5433b238e1
parent 151564 bf1f7d342f5b50800c0aa9e91a738935d62ee291
child 151566 5bdc21ebbc1981fec83bef0e2fbc7e466eed151a
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs884421
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 884421 - Automatic network port selection for /services tests; r=rnewman
services/common/modules-testing/bagheeraserver.js
services/common/modules-testing/storageserver.js
services/common/tests/unit/head_global.js
services/common/tests/unit/head_helpers.js
services/common/tests/unit/test_bagheera_client.js
services/common/tests/unit/test_bagheera_server.js
services/common/tests/unit/test_restrequest.js
services/common/tests/unit/test_storage_server.js
services/common/tests/unit/test_storageservice_client.js
services/common/tests/unit/test_tokenauthenticatedrequest.js
services/common/tests/unit/test_tokenserverclient.js
services/healthreport/tests/xpcshell/test_healthreporter.js
services/sync/modules-testing/utils.js
services/sync/tests/unit/head_http_server.js
services/sync/tests/unit/test_addons_engine.js
services/sync/tests/unit/test_bookmark_engine.js
services/sync/tests/unit/test_bookmark_smart_bookmarks.js
services/sync/tests/unit/test_clients_engine.js
services/sync/tests/unit/test_collections_recovery.js
services/sync/tests/unit/test_corrupt_keys.js
services/sync/tests/unit/test_engine_abort.js
services/sync/tests/unit/test_errorhandler.js
services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
services/sync/tests/unit/test_history_engine.js
services/sync/tests/unit/test_hmac_error.js
services/sync/tests/unit/test_httpd_sync_server.js
services/sync/tests/unit/test_interval_triggers.js
services/sync/tests/unit/test_jpakeclient.js
services/sync/tests/unit/test_node_reassignment.js
services/sync/tests/unit/test_records_wbo.js
services/sync/tests/unit/test_resource.js
services/sync/tests/unit/test_resource_async.js
services/sync/tests/unit/test_resource_ua.js
services/sync/tests/unit/test_score_triggers.js
services/sync/tests/unit/test_service_changePassword.js
services/sync/tests/unit/test_service_checkAccount.js
services/sync/tests/unit/test_service_cluster.js
services/sync/tests/unit/test_service_createAccount.js
services/sync/tests/unit/test_service_detect_upgrade.js
services/sync/tests/unit/test_service_getStorageInfo.js
services/sync/tests/unit/test_service_login.js
services/sync/tests/unit/test_service_passwordUTF8.js
services/sync/tests/unit/test_service_startOver.js
services/sync/tests/unit/test_service_startup.js
services/sync/tests/unit/test_service_sync_401.js
services/sync/tests/unit/test_service_sync_remoteSetup.js
services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
services/sync/tests/unit/test_service_verifyLogin.js
services/sync/tests/unit/test_service_wipeClient.js
services/sync/tests/unit/test_service_wipeServer.js
services/sync/tests/unit/test_syncengine.js
services/sync/tests/unit/test_syncengine_sync.js
services/sync/tests/unit/test_syncscheduler.js
services/sync/tests/unit/test_syncstoragerequest.js
--- a/services/common/modules-testing/bagheeraserver.js
+++ b/services/common/modules-testing/bagheeraserver.js
@@ -17,22 +17,20 @@ Cu.import("resource://testing-common/htt
  * This is an implementation of the Bagheera server.
  *
  * The purpose of the server is to facilitate testing of the Bagheera
  * client and the Firefox Health report. It is *not* meant to be a
  * production grade server.
  *
  * The Bagheera server is essentially a glorified document store.
  */
-this.BagheeraServer = function BagheeraServer(serverURI="http://localhost") {
+this.BagheeraServer = function BagheeraServer() {
   this._log = Log4Moz.repository.getLogger("metrics.BagheeraServer");
 
-  this.serverURI = serverURI;
   this.server = new HttpServer();
-  this.port = 8080;
   this.namespaces = {};
 
   this.allowAllNamespaces = false;
 }
 
 BagheeraServer.prototype = {
   /**
    * Whether this server has a namespace defined.
@@ -121,25 +119,24 @@ BagheeraServer.prototype = {
   createNamespace: function createNamespace(ns) {
     if (ns in this.namespaces) {
       throw new Error("Namespace already exists: " + ns);
     }
 
     this.namespaces[ns] = {};
   },
 
-  start: function start(port) {
-    if (!port) {
-      throw new Error("port argument must be specified.");
-    }
-
-    this.port = port;
-
+  start: function start(port=-1) {
     this.server.registerPrefixHandler("/", this._handleRequest.bind(this));
     this.server.start(port);
+    let i = this.server.identity;
+
+    this.serverURI = i.primaryScheme + "://" + i.primaryHost + ":" +
+                     i.primaryPort + "/";
+    this.port = i.primaryPort;
   },
 
   stop: function stop(cb) {
     let handler = {onStopped: cb};
 
     this.server.stop(handler);
   },
 
--- a/services/common/modules-testing/storageserver.js
+++ b/services/common/modules-testing/storageserver.js
@@ -877,48 +877,50 @@ this.StorageServer = function StorageSer
   // Install our own default handler. This allows us to mess around with the
   // whole URL space.
   let handler = this.server._handler;
   handler._handleDefault = this.handleDefault.bind(this, handler);
 }
 StorageServer.prototype = {
   DEFAULT_QUOTA: 1024 * 1024, // # bytes.
 
-  port:   8080,
   server: null,    // HttpServer.
   users:  null,    // Map of username => {collections, password}.
 
   /**
    * If true, the server will allow any arbitrary user to be used.
    *
    * No authentication will be performed. Whatever user is detected from the
    * URL or auth headers will be created (if needed) and used.
    */
   allowAllUsers: false,
 
   /**
    * Start the StorageServer's underlying HTTP server.
    *
    * @param port
-   *        The numeric port on which to start. A falsy value implies the
-   *        default (8080).
+   *        The numeric port on which to start. A falsy value implies to
+   *        select any available port.
    * @param cb
    *        A callback function (of no arguments) which is invoked after
    *        startup.
    */
   start: function start(port, cb) {
     if (this.started) {
       this._log.warn("Warning: server already started on " + this.port);
       return;
     }
-    if (port) {
-      this.port = port;
+    if (!port) {
+      port = -1;
     }
+    this.port = port;
+
     try {
       this.server.start(this.port);
+      this.port = this.server.identity.primaryPort;
       this.started = true;
       if (cb) {
         cb();
       }
     } catch (ex) {
       _("==========================================");
       _("Got exception starting Storage HTTP server on port " + this.port);
       _("Error: " + CommonUtils.exceptionStr(ex));
@@ -927,20 +929,20 @@ StorageServer.prototype = {
       do_throw(ex);
     }
   },
 
   /**
    * Start the server synchronously.
    *
    * @param port
-   *        The numeric port on which to start. A falsy value implies the
-   *        default (8080).
+   *        The numeric port on which to start. The default is to choose
+   *        any available port.
    */
-  startSynchronous: function startSynchronous(port) {
+  startSynchronous: function startSynchronous(port=-1) {
     let cb = Async.makeSpinningCallback();
     this.start(port, cb);
     cb.wait();
   },
 
   /**
    * Stop the StorageServer's HTTP server.
    *
--- a/services/common/tests/unit/head_global.js
+++ b/services/common/tests/unit/head_global.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} = Components;
 
-// Where to bind test HTTP servers to.
-const TEST_SERVER_URL = "http://localhost:8080/";
-
 let gSyncProfile = do_get_profile();
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let XULAppInfo = {
   vendor: "Mozilla",
   name: "XPCShell",
   ID: "xpcshell@tests.mozilla.org",
--- a/services/common/tests/unit/head_helpers.js
+++ b/services/common/tests/unit/head_helpers.js
@@ -41,43 +41,36 @@ function do_check_throws(aFunc, aResult,
  *
  * @param [arg0, arg1, arg2, ...]
  *        Any number of arguments to print out
  * @usage _("Hello World") -> prints "Hello World"
  * @usage _(1, 2, 3) -> prints "1 2 3"
  */
 let _ = function(some, debug, text, to) print(Array.slice(arguments).join(" "));
 
-/**
- * Obtain a port number to run a server on.
- *
- * In the ideal world, this would be dynamic so multiple servers could be run
- * in parallel.
- */
-function get_server_port() {
-  return 8080;
-}
-
-function httpd_setup (handlers, port) {
-  let port   = port || 8080;
+function httpd_setup (handlers, port=-1) {
   let server = new HttpServer();
   for (let path in handlers) {
     server.registerPathHandler(path, handlers[path]);
   }
   try {
     server.start(port);
   } catch (ex) {
     _("==========================================");
     _("Got exception starting HTTP server on port " + port);
     _("Error: " + CommonUtils.exceptionStr(ex));
     _("Is there a process already listening on port " + port + "?");
     _("==========================================");
     do_throw(ex);
   }
 
+  // Set the base URI for convenience.
+  let i = server.identity;
+  server.baseURI = i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort;
+
   return server;
 }
 
 function httpd_handler(statusCode, status, body) {
   return function handler(request, response) {
     _("Processing request");
     // Allow test functions to inspect the request.
     request.body = readBytesFromInputStream(request.bodyInputStream);
--- a/services/common/tests/unit/test_bagheera_client.js
+++ b/services/common/tests/unit/test_bagheera_client.js
@@ -4,42 +4,38 @@
 "use strict";
 
 Cu.import("resource://services-common/bagheeraclient.js");
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://testing-common/services-common/bagheeraserver.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
-const PORT = 8080;
+function getClientAndServer() {
+  let server = new BagheeraServer();
+  server.start();
 
-function getClientAndServer(port=PORT) {
-  let uri = "http://localhost";
-  let server = new BagheeraServer(uri);
-
-  server.start(port);
-
-  let client = new BagheeraClient(uri + ":" + port);
+  let client = new BagheeraClient(server.serverURI);
 
   return [client, server];
 }
 
 function run_test() {
   initTestLogging("Trace");
   run_next_test();
 }
 
 add_test(function test_constructor() {
-  let client = new BagheeraClient("http://localhost:8080/");
+  let client = new BagheeraClient("http://localhost:1234/");
 
   run_next_test();
 });
 
 add_test(function test_post_json_transport_failure() {
-  let client = new BagheeraClient("http://localhost:8080/");
+  let client = new BagheeraClient("http://localhost:1234/");
 
   client.uploadJSON("foo", "bar", {}).then(function onResult(result) {
     do_check_false(result.transportSuccess);
 
     run_next_test();
   });
 });
 
@@ -133,9 +129,9 @@ add_test(function test_delete_document()
   client.deleteDocument("foo", "bar").then(function onResult(result) {
     do_check_true(result.transportSuccess);
     do_check_true(result.serverSuccess);
 
     do_check_null(server.getDocument("foo", "bar"));
 
     server.stop(run_next_test);
   });
-});
\ No newline at end of file
+});
--- a/services/common/tests/unit/test_bagheera_server.js
+++ b/services/common/tests/unit/test_bagheera_server.js
@@ -1,17 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://testing-common/services-common/bagheeraserver.js");
 
-const PORT = 8080;
-
 function run_test() {
   run_next_test();
 }
 
 add_test(function test_server_empty() {
   let server = new BagheeraServer();
 
   do_check_false(server.hasNamespace("foo"));
@@ -19,27 +17,14 @@ add_test(function test_server_empty() {
   do_check_null(server.getDocument("foo", "bar"));
 
   server.createNamespace("foo");
   do_check_true(server.hasNamespace("foo"));
 
   run_next_test();
 });
 
-add_test(function test_server_start_no_port() {
-  let server = new BagheeraServer();
-
-  try {
-    server.start();
-  } catch (ex) {
-    do_check_true(ex.message.startsWith("port argument must be"));
-  }
-
-  run_next_test();
-});
-
 add_test(function test_server_start() {
   let server = new BagheeraServer();
-  server.start(PORT);
-
+  server.start();
   server.stop(run_next_test);
 });
 
--- a/services/common/tests/unit/test_restrequest.js
+++ b/services/common/tests/unit/test_restrequest.js
@@ -1,18 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-common/utils.js");
 
-const TEST_RESOURCE_URL = TEST_SERVER_URL + "resource";
-
 //DEBUG = true;
 
 function run_test() {
   Log4Moz.repository.getLogger("Services.Common.RESTRequest").level =
     Log4Moz.Level.Trace;
   initTestLogging("Trace");
 
   run_next_test();
@@ -69,20 +67,20 @@ add_test(function test_proxy_auth_redire
     response.setStatusLine(metadata.httpVersion, 200, "OK");
     response.bodyOutputStream.write(body, body.length);
   }
 
   let server = httpd_setup({
     "/original": original,
     "/pac3":     pacHandler
   });
-  PACSystemSettings.PACURI = "http://localhost:8080/pac3";
+  PACSystemSettings.PACURI = server.baseURI + "/pac3";
   installFakePAC();
 
-  let res = new RESTRequest("http://localhost:8080/original");
+  let res = new RESTRequest(server.baseURI + "/original");
   res.get(function (error) {
     do_check_true(pacFetched);
     do_check_true(fetched);
     do_check_true(!error);
     do_check_true(this.response.success);
     do_check_eq("TADA!", this.response.body);
     uninstallFakePAC();
     server.stop(run_next_test);
@@ -107,18 +105,17 @@ add_test(function test_forbidden_port() 
 
 /**
  * Demonstrate API short-hand: create a request and dispatch it immediately.
  */
 add_test(function test_simple_get() {
   let handler = httpd_handler(200, "OK", "Huzzah!");
   let server = httpd_setup({"/resource": handler});
 
-  let uri = TEST_RESOURCE_URL;
-  let request = new RESTRequest(uri).get(function (error) {
+  let request = new RESTRequest(server.baseURI + "/resource").get(function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "Huzzah!");
 
     server.stop(run_next_test);
@@ -129,17 +126,17 @@ add_test(function test_simple_get() {
 
 /**
  * Test HTTP GET with all bells and whistles.
  */
 add_test(function test_get() {
   let handler = httpd_handler(200, "OK", "Huzzah!");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   do_check_eq(request.status, request.NOT_SENT);
 
   request.onProgress = request.onComplete = function () {
     do_throw("This function should have been overwritten!");
   };
 
   let onProgress_called = false;
   function onProgress() {
@@ -193,28 +190,28 @@ add_test(function test_get_utf8() {
     let converter = Cc["@mozilla.org/intl/converter-output-stream;1"]
                     .createInstance(Ci.nsIConverterOutputStream);
     converter.init(res.bodyOutputStream, "UTF-8", 0, 0x0000);
     converter.writeString(response);
     converter.close();
   }});
 
   // Check if charset in Content-Type is propertly interpreted.
-  let request1 = new RESTRequest(TEST_RESOURCE_URL);
+  let request1 = new RESTRequest(server.baseURI + "/resource");
   request1.get(function(error) {
     do_check_null(error);
 
     do_check_eq(request1.response.status, 200);
     do_check_eq(request1.response.body, response);
     do_check_eq(request1.response.headers["content-type"],
                 contentType + charsetSuffix);
 
     // Check that we default to UTF-8 if Content-Type doesn't have a charset.
     charset = false;
-    let request2 = new RESTRequest(TEST_RESOURCE_URL);
+    let request2 = new RESTRequest(server.baseURI + "/resource");
     request2.get(function(error) {
       do_check_null(error);
 
       do_check_eq(request2.response.status, 200);
       do_check_eq(request2.response.body, response);
       do_check_eq(request2.response.headers["content-type"], contentType);
       do_check_eq(request2.response.charset, "utf-8");
 
@@ -240,30 +237,30 @@ add_test(function test_charsets() {
     let converter = Cc["@mozilla.org/intl/converter-output-stream;1"]
                     .createInstance(Ci.nsIConverterOutputStream);
     converter.init(res.bodyOutputStream, "us-ascii", 0, 0x0000);
     converter.writeString(response);
     converter.close();
   }});
 
   // Check that provided charset overrides hint.
-  let request1 = new RESTRequest(TEST_RESOURCE_URL);
+  let request1 = new RESTRequest(server.baseURI + "/resource");
   request1.charset = "not-a-charset";
   request1.get(function(error) {
     do_check_null(error);
 
     do_check_eq(request1.response.status, 200);
     do_check_eq(request1.response.body, response);
     do_check_eq(request1.response.headers["content-type"],
                 contentType + charsetSuffix);
     do_check_eq(request1.response.charset, "us-ascii");
 
     // Check that hint is used if Content-Type doesn't have a charset.
     charset = false;
-    let request2 = new RESTRequest(TEST_RESOURCE_URL);
+    let request2 = new RESTRequest(server.baseURI + "/resource");
     request2.charset = "us-ascii";
     request2.get(function(error) {
       do_check_null(error);
 
       do_check_eq(request2.response.status, 200);
       do_check_eq(request2.response.body, response);
       do_check_eq(request2.response.headers["content-type"], contentType);
       do_check_eq(request2.response.charset, "us-ascii");
@@ -275,17 +272,17 @@ add_test(function test_charsets() {
 
 /**
  * Test HTTP PUT with a simple string argument and default Content-Type.
  */
 add_test(function test_put() {
   let handler = httpd_handler(200, "OK", "Got it!");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   do_check_eq(request.status, request.NOT_SENT);
 
   request.onProgress = request.onComplete = function () {
     do_throw("This function should have been overwritten!");
   };
 
   let onProgress_called = false;
   function onProgress() {
@@ -324,17 +321,17 @@ add_test(function test_put() {
 
 /**
  * Test HTTP POST with a simple string argument and default Content-Type.
  */
 add_test(function test_post() {
   let handler = httpd_handler(200, "OK", "Got it!");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   do_check_eq(request.status, request.NOT_SENT);
 
   request.onProgress = request.onComplete = function () {
     do_throw("This function should have been overwritten!");
   };
 
   let onProgress_called = false;
   function onProgress() {
@@ -373,17 +370,17 @@ add_test(function test_post() {
 
 /**
  * Test HTTP DELETE.
  */
 add_test(function test_delete() {
   let handler = httpd_handler(200, "OK", "Got it!");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   do_check_eq(request.status, request.NOT_SENT);
 
   request.onProgress = request.onComplete = function () {
     do_throw("This function should have been overwritten!");
   };
 
   let onProgress_called = false;
   function onProgress() {
@@ -419,17 +416,17 @@ add_test(function test_delete() {
 
 /**
  * Test an HTTP response with a non-200 status code.
  */
 add_test(function test_get_404() {
   let handler = httpd_handler(404, "Not Found", "Cannae find it!");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_false(this.response.success);
     do_check_eq(this.response.status, 404);
     do_check_eq(this.response.body, "Cannae find it!");
 
@@ -445,17 +442,17 @@ add_test(function test_put_json() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   let sample_data = {
     some: "sample_data",
     injson: "format",
     number: 42
   };
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.put(sample_data, function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
 
@@ -475,17 +472,17 @@ add_test(function test_post_json() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   let sample_data = {
     some: "sample_data",
     injson: "format",
     number: 42
   };
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.post(sample_data, function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
 
@@ -499,17 +496,17 @@ add_test(function test_post_json() {
 
 /**
  * HTTP PUT with a custom Content-Type header.
  */
 add_test(function test_put_override_content_type() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.setHeader("Content-Type", "application/lolcat");
   request.put("O HAI!!1!", function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
@@ -524,17 +521,17 @@ add_test(function test_put_override_cont
 
 /**
  * HTTP POST with a custom Content-Type header.
  */
 add_test(function test_post_override_content_type() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.setHeader("Content-Type", "application/lolcat");
   request.post("O HAI!!1!", function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.status, this.COMPLETED);
     do_check_true(this.response.success);
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
@@ -554,17 +551,17 @@ add_test(function test_get_no_headers() 
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   let ignore_headers = ["host", "user-agent", "accept", "accept-language",
                         "accept-encoding", "accept-charset", "keep-alive",
                         "connection", "pragma", "cache-control",
                         "content-length"];
 
-  new RESTRequest(TEST_RESOURCE_URL).get(function (error) {
+  new RESTRequest(server.baseURI + "/resource").get(function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
 
     let server_headers = handler.request.headers;
     while (server_headers.hasMoreElements()) {
       let header = server_headers.getNext().toString();
@@ -579,33 +576,33 @@ add_test(function test_get_no_headers() 
 
 /**
  * Test changing the URI after having created the request.
  */
 add_test(function test_changing_uri() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest("http://localhost:8080/the-wrong-resource");
-  request.uri = CommonUtils.makeURI(TEST_RESOURCE_URL);
+  let request = new RESTRequest("http://localhost:1234/the-wrong-resource");
+  request.uri = CommonUtils.makeURI(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     server.stop(run_next_test);
   });
 });
 
 /**
  * Test setting HTTP request headers.
  */
 add_test(function test_request_setHeader() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
 
   request.setHeader("X-What-Is-Weave", "awesome");
   request.setHeader("X-WHAT-is-Weave", "more awesomer");
   request.setHeader("Another-Header", "Hello World");
 
   request.get(function (error) {
     do_check_eq(error, null);
 
@@ -624,17 +621,17 @@ add_test(function test_request_setHeader
  */
 add_test(function test_response_headers() {
   function handler(request, response) {
     response.setHeader("X-What-Is-Weave", "awesome");
     response.setHeader("Another-Header", "Hello World");
     response.setStatusLine(request.httpVersion, 200, "OK");
   }
   let server = httpd_setup({"/resource": handler});
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
 
   request.get(function (error) {
     do_check_eq(error, null);
 
     do_check_eq(this.response.status, 200);
     do_check_eq(this.response.body, "");
 
     do_check_eq(this.response.headers["x-what-is-weave"], "awesome");
@@ -644,17 +641,17 @@ add_test(function test_response_headers(
   });
 });
 
 /**
  * The onComplete() handler gets called in case of any network errors
  * (e.g. NS_ERROR_CONNECTION_REFUSED).
  */
 add_test(function test_connection_refused() {
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest("http://localhost:1234/resource");
   request.onProgress = function onProgress() {
     do_throw("Shouldn't have called request.onProgress()!");
   };
   request.get(function (error) {
     do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
     do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
     do_check_eq(this.status, this.COMPLETED);
     run_next_test();
@@ -666,17 +663,17 @@ add_test(function test_connection_refuse
  * Abort a request that just sent off.
  */
 add_test(function test_abort() {
   function handler() {
     do_throw("Shouldn't have gotten here!");
   }
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
 
   // Aborting a request that hasn't been sent yet is pointless and will throw.
   do_check_throws(function () {
     request.abort();
   });
 
   request.onProgress = request.onComplete = function () {
     do_throw("Shouldn't have gotten here!");
@@ -704,19 +701,22 @@ add_test(function test_timeout() {
   let server_connection;
   server._handler.handleResponse = function(connection) {
     // This is a handler that doesn't do anything, just keeps the connection
     // open, thereby mimicking a timing out connection. We keep a reference to
     // the open connection for later so it can be properly disposed of. That's
     // why you really only want to make one HTTP request to this server ever.
     server_connection = connection;
   };
-  server.start(8080);
+  server.start();
+  let identity = server.identity;
+  let uri = identity.primaryScheme + "://" + identity.primaryHost + ":" +
+            identity.primaryPort;
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(uri + "/resource");
   request.timeout = 0.1; // 100 milliseconds
   request.get(function (error) {
     do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
     do_check_eq(this.status, this.ABORTED);
 
     _("Closing connection.");
     server_connection.close();
     _("Shutting down server.");
@@ -726,17 +726,17 @@ add_test(function test_timeout() {
 
 /**
  * An exception thrown in 'onProgress' propagates to the 'onComplete' handler.
  */
 add_test(function test_exception_in_onProgress() {
   let handler = httpd_handler(200, "OK", "Foobar");
   let server = httpd_setup({"/resource": handler});
 
-  let request = new RESTRequest(TEST_RESOURCE_URL);
+  let request = new RESTRequest(server.baseURI + "/resource");
   request.onProgress = function onProgress() {
     it.does.not.exist();
   };
   request.get(function onComplete(error) {
     do_check_eq(error, "ReferenceError: it is not defined");
     do_check_eq(this.status, this.ABORTED);
 
     server.stop(run_next_test);
@@ -748,46 +748,48 @@ add_test(function test_new_channel() {
 
   function checkUA(metadata) {
     let ua = metadata.getHeader("User-Agent");
     _("User-Agent is " + ua);
     do_check_eq("foo bar", ua);
   }
 
   let redirectRequested = false;
+  let redirectURL;
   function redirectHandler(metadata, response) {
     checkUA(metadata);
     redirectRequested = true;
 
     let body = "Redirecting";
     response.setStatusLine(metadata.httpVersion, 307, "TEMPORARY REDIRECT");
-    response.setHeader("Location", "http://localhost:8081/resource");
+    response.setHeader("Location", redirectURL);
     response.bodyOutputStream.write(body, body.length);
   }
 
   let resourceRequested = false;
   function resourceHandler(metadata, response) {
     checkUA(metadata);
     resourceRequested = true;
 
     let body = "Test";
     response.setHeader("Content-Type", "text/plain");
     response.bodyOutputStream.write(body, body.length);
   }
 
-  let server1 = httpd_setup({"/redirect": redirectHandler}, 8080);
-  let server2 = httpd_setup({"/resource": resourceHandler}, 8081);
+  let server1 = httpd_setup({"/redirect": redirectHandler});
+  let server2 = httpd_setup({"/resource": resourceHandler});
+  redirectURL = server2.baseURI + "/resource";
 
   function advance() {
     server1.stop(function () {
       server2.stop(run_next_test);
     });
   }
 
-  let request = new RESTRequest("http://localhost:8080/redirect");
+  let request = new RESTRequest(server1.baseURI + "/redirect");
   request.setHeader("User-Agent", "foo bar");
 
   // Swizzle in our own fakery, because this redirect is neither
   // internal nor URI-preserving. RESTRequest's policy is to only
   // copy headers under certain circumstances.
   let protoMethod = request.shouldCopyOnRedirect;
   request.shouldCopyOnRedirect = function wrapped(o, n, f) {
     // Check the default policy.
@@ -813,19 +815,19 @@ add_test(function test_not_sending_cooki
     response.setStatusLine(metadata.httpVersion, 200, "OK");
     response.bodyOutputStream.write(body, body.length);
     do_check_false(metadata.hasHeader("Cookie"));
   }
   let server = httpd_setup({"/test": handler});
 
   let cookieSer = Cc["@mozilla.org/cookieService;1"]
                     .getService(Ci.nsICookieService);
-  let uri = CommonUtils.makeURI("http://localhost:8080");
+  let uri = CommonUtils.makeURI(server.baseURI);
   cookieSer.setCookieString(uri, null, "test=test; path=/;", null);
 
-  let res = new RESTRequest("http://localhost:8080/test");
+  let res = new RESTRequest(server.baseURI + "/test");
   res.get(function (error) {
     do_check_null(error);
     do_check_true(this.response.success);
     do_check_eq("COOKIE!", this.response.body);
     server.stop(run_next_test);
   });
 });
--- a/services/common/tests/unit/test_storage_server.js
+++ b/services/common/tests/unit/test_storage_server.js
@@ -1,26 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://testing-common/services-common/storageserver.js");
 
-const PORT = 8080;
 const DEFAULT_USER = "123";
 const DEFAULT_PASSWORD = "password";
 
 /**
  * Helper function to prepare a RESTRequest against the server.
  */
-function localRequest(path, user=DEFAULT_USER, password=DEFAULT_PASSWORD) {
+function localRequest(server, path, user=DEFAULT_USER, password=DEFAULT_PASSWORD) {
   _("localRequest: " + path);
-  let url = "http://127.0.0.1:" + PORT + path;
+  let identity = server.server.identity;
+  let url = identity.primaryScheme + "://" + identity.primaryHost + ":" +
+            identity.primaryPort + path;
   _("url: " + url);
   let req = new RESTRequest(url);
 
   let header = basic_auth_header(user, password);
   req.setHeader("Authorization", header);
   req.setHeader("Accept", "application/json");
 
   return req;
@@ -115,28 +116,27 @@ add_test(function test_creation() {
   _("Ensure a simple server can be created.");
 
   // Explicit callback for this one.
   let server = new StorageServer({
     __proto__: StorageServerCallback,
   });
   do_check_true(!!server);
 
-  server.start(PORT, function () {
+  server.start(-1, function () {
     _("Started on " + server.port);
-    do_check_eq(server.port, PORT);
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_synchronous_start() {
   _("Ensure starting using startSynchronous works.");
 
   let server = new StorageServer();
-  server.startSynchronous(PORT);
+  server.startSynchronous();
   server.stop(run_next_test);
 });
 
 add_test(function test_url_parsing() {
   _("Ensure server parses URLs properly.");
 
   let server = new StorageServer();
 
@@ -179,46 +179,46 @@ add_test(function test_url_parsing() {
 
   run_next_test();
 });
 
 add_test(function test_basic_http() {
   let server = new StorageServer();
   server.registerUser("345", "password");
   do_check_true(server.userExists("345"));
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   _("Started on " + server.port);
   do_check_eq(server.requestCount, 0);
-  let req = localRequest("/2.0/storage/crypto/keys");
+  let req = localRequest(server, "/2.0/storage/crypto/keys");
   _("req is " + req);
   req.get(function (err) {
     do_check_eq(null, err);
     do_check_eq(server.requestCount, 1);
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_info_collections() {
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let path = "/2.0/123/info/collections";
 
   _("info/collections on empty server should be empty object.");
-  let request = localRequest(path, "123", "password");
+  let request = localRequest(server, path, "123", "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 200);
   do_check_eq(request.response.body, "{}");
 
   _("Creating an empty collection should result in collection appearing.");
   let coll = server.createCollection("123", "col1");
-  let request = localRequest(path, "123", "password");
+  let request = localRequest(server, path, "123", "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 200);
   let info = JSON.parse(request.response.body);
   do_check_attribute_count(info, 1);
   do_check_true("col1" in info);
   do_check_eq(info.col1, coll.timestamp);
 
@@ -228,21 +228,22 @@ add_test(function test_info_collections(
 add_test(function test_bso_get_existing() {
   _("Ensure that BSO retrieval works.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {"bso": {"foo": "bar"}}
   });
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").collection("test");
 
-  let request = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/bso", "123",
+                             "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 200);
   do_check_eq(request.response.headers["content-type"], "application/json");
   let bso = JSON.parse(request.response.body);
   do_check_attribute_count(bso, 3);
   do_check_eq(bso.id, "bso");
   do_check_eq(bso.modified, coll.bso("bso").modified);
@@ -253,24 +254,24 @@ add_test(function test_bso_get_existing(
   server.stop(run_next_test);
 });
 
 add_test(function test_percent_decoding() {
   _("Ensure query string arguments with percent encoded are handled.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").createCollection("test");
   coll.insert("001", {foo: "bar"});
   coll.insert("002", {bar: "foo"});
 
-  let request = localRequest("/2.0/123/storage/test?ids=001%2C002", "123",
-                             "password");
+  let request = localRequest(server, "/2.0/123/storage/test?ids=001%2C002",
+                             "123", "password");
   let error = doGetRequest(request);
   do_check_null(error);
   do_check_eq(request.response.status, 200);
   let items = JSON.parse(request.response.body).items;
   do_check_attribute_count(items, 2);
 
   server.stop(run_next_test);
 });
@@ -278,19 +279,19 @@ add_test(function test_percent_decoding(
 add_test(function test_bso_404() {
   _("Ensure the server responds with a 404 if a BSO does not exist.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {}
   });
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
-  let request = localRequest("/2.0/123/storage/test/foo");
+  let request = localRequest(server, "/2.0/123/storage/test/foo");
   let error = doGetRequest(request);
   do_check_eq(error, null);
 
   do_check_eq(request.response.status, 404);
   do_check_false("content-type" in request.response.headers);
 
   server.stop(run_next_test);
 });
@@ -298,34 +299,36 @@ add_test(function test_bso_404() {
 add_test(function test_bso_if_modified_since_304() {
   _("Ensure the server responds properly to X-If-Modified-Since for BSOs.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {bso: {foo: "bar"}}
   });
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").collection("test");
   do_check_neq(coll, null);
 
   // Rewind clock just in case.
   coll.timestamp -= 10000;
   coll.bso("bso").modified -= 10000;
 
-  let request = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/bso",
+                             "123", "password");
   request.setHeader("X-If-Modified-Since", "" + server.serverTime());
   let error = doGetRequest(request);
   do_check_eq(null, error);
 
   do_check_eq(request.response.status, 304);
   do_check_false("content-type" in request.response.headers);
 
-  let request = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/bso",
+                             "123", "password");
   request.setHeader("X-If-Modified-Since", "" + (server.serverTime() - 20000));
   let error = doGetRequest(request);
   do_check_eq(null, error);
   do_check_eq(request.response.status, 200);
   do_check_eq(request.response.headers["content-type"], "application/json");
 
   server.stop(run_next_test);
 });
@@ -333,220 +336,228 @@ add_test(function test_bso_if_modified_s
 add_test(function test_bso_if_unmodified_since() {
   _("Ensure X-If-Unmodified-Since works properly on BSOs.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {bso: {foo: "bar"}}
   });
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").collection("test");
   do_check_neq(coll, null);
 
   let time = coll.timestamp;
 
   _("Ensure we get a 412 for specified times older than server time.");
-  let request = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/bso",
+                             "123", "password");
   request.setHeader("X-If-Unmodified-Since", time - 5000);
   request.setHeader("Content-Type", "application/json");
   let payload = JSON.stringify({"payload": "foobar"});
   let error = doPutRequest(request, payload);
   do_check_eq(null, error);
   do_check_eq(request.response.status, 412);
 
   _("Ensure we get a 204 if update goes through.");
-  let request = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/bso",
+                             "123", "password");
   request.setHeader("Content-Type", "application/json");
   request.setHeader("X-If-Unmodified-Since", time + 1);
   let error = doPutRequest(request, payload);
   do_check_eq(null, error);
   do_check_eq(request.response.status, 204);
   do_check_true(coll.timestamp > time);
 
   // Not sure why a client would send X-If-Unmodified-Since if a BSO doesn't
   // exist. But, why not test it?
   _("Ensure we get a 201 if creation goes through.");
-  let request = localRequest("/2.0/123/storage/test/none", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/none",
+                             "123", "password");
   request.setHeader("Content-Type", "application/json");
   request.setHeader("X-If-Unmodified-Since", time);
   let error = doPutRequest(request, payload);
   do_check_eq(null, error);
   do_check_eq(request.response.status, 201);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_bso_delete_not_exist() {
   _("Ensure server behaves properly when deleting a BSO that does not exist.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.user("123").createCollection("empty");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   server.callback.onItemDeleted = function onItemDeleted(username, collection,
                                                          id) {
     do_throw("onItemDeleted should not have been called.");
   };
 
-  let request = localRequest("/2.0/123/storage/empty/nada", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/empty/nada",
+                             "123", "password");
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 404);
   do_check_false("content-type" in request.response.headers);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_bso_delete_exists() {
   _("Ensure proper semantics when deleting a BSO that exists.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").createCollection("test");
   let bso = coll.insert("myid", {foo: "bar"});
   let timestamp = coll.timestamp;
 
   server.callback.onItemDeleted = function onDeleted(username, collection, id) {
     delete server.callback.onItemDeleted;
     do_check_eq(username, "123");
     do_check_eq(collection, "test");
     do_check_eq(id, "myid");
   };
 
-  let request = localRequest("/2.0/123/storage/test/myid", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/myid",
+                             "123", "password");
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 204);
   do_check_eq(coll.bsos().length, 0);
   do_check_true(coll.timestamp > timestamp);
 
   _("On next request the BSO should not exist.");
-  let request = localRequest("/2.0/123/storage/test/myid", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/test/myid",
+                             "123", "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 404);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_bso_delete_unmodified() {
   _("Ensure X-If-Unmodified-Since works when deleting BSOs.");
 
   let server = new StorageServer();
-  server.startSynchronous(PORT);
+  server.startSynchronous();
   server.registerUser("123", "password");
   let coll = server.user("123").createCollection("test");
   let bso = coll.insert("myid", {foo: "bar"});
 
   let modified = bso.modified;
 
   _("Issuing a DELETE with an older time should fail.");
   let path = "/2.0/123/storage/test/myid";
-  let request = localRequest(path, "123", "password");
+  let request = localRequest(server, path, "123", "password");
   request.setHeader("X-If-Unmodified-Since", modified - 1000);
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 412);
   do_check_false("content-type" in request.response.headers);
   do_check_neq(coll.bso("myid"), null);
 
   _("Issuing a DELETE with a newer time should work.");
-  let request = localRequest(path, "123", "password");
+  let request = localRequest(server, path, "123", "password");
   request.setHeader("X-If-Unmodified-Since", modified + 1000);
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 204);
   do_check_true(coll.bso("myid").deleted);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_collection_get_unmodified_since() {
   _("Ensure conditional unmodified get on collection works when it should.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
   let collection = server.user("123").createCollection("testcoll");
   collection.insert("bso0", {foo: "bar"});
 
   let serverModified = collection.timestamp;
 
-  let request1 = localRequest("/2.0/123/storage/testcoll", "123", "password");
+  let request1 = localRequest(server, "/2.0/123/storage/testcoll",
+                              "123", "password");
   request1.setHeader("X-If-Unmodified-Since", serverModified);
   let error = doGetRequest(request1);
   do_check_null(error);
   do_check_eq(request1.response.status, 200);
 
-  let request2 = localRequest("/2.0/123/storage/testcoll", "123", "password");
+  let request2 = localRequest(server, "/2.0/123/storage/testcoll",
+                              "123", "password");
   request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
   let error = doGetRequest(request2);
   do_check_null(error);
   do_check_eq(request2.response.status, 412);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_bso_get_unmodified_since() {
   _("Ensure conditional unmodified get on BSO works appropriately.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
   let collection = server.user("123").createCollection("testcoll");
   let bso = collection.insert("bso0", {foo: "bar"});
 
   let serverModified = bso.modified;
 
-  let request1 = localRequest("/2.0/123/storage/testcoll/bso0", "123",
-                              "password");
+  let request1 = localRequest(server, "/2.0/123/storage/testcoll/bso0",
+                              "123", "password");
   request1.setHeader("X-If-Unmodified-Since", serverModified);
   let error = doGetRequest(request1);
   do_check_null(error);
   do_check_eq(request1.response.status, 200);
 
-  let request2 = localRequest("/2.0/123/storage/testcoll/bso0", "123",
-                              "password");
+  let request2 = localRequest(server, "/2.0/123/storage/testcoll/bso0",
+                              "123", "password");
   request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
   let error = doGetRequest(request2);
   do_check_null(error);
   do_check_eq(request2.response.status, 412);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_missing_collection_404() {
   _("Ensure a missing collection returns a 404.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
-  let request = localRequest("/2.0/123/storage/none", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage/none", "123", "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 404);
   do_check_false("content-type" in request.response.headers);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_get_storage_405() {
   _("Ensure that a GET on /storage results in a 405.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
-  let request = localRequest("/2.0/123/storage", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage", "123", "password");
   let error = doGetRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 405);
   do_check_eq(request.response.headers["allow"], "DELETE");
 
   server.stop(run_next_test);
 });
 
@@ -555,108 +566,108 @@ add_test(function test_delete_storage() 
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     foo: {a: {foo: "bar"}, b: {bar: "foo"}},
     baz: {c: {bob: "law"}, blah: {law: "blog"}}
   });
 
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
-  let request = localRequest("/2.0/123/storage", "123", "password");
+  let request = localRequest(server, "/2.0/123/storage", "123", "password");
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 204);
   do_check_attribute_count(server.users["123"].collections, 0);
 
   server.stop(run_next_test);
 });
 
 add_test(function test_x_num_records() {
   let server = new StorageServer();
   server.registerUser("123", "password");
 
   server.createContents("123", {
     crypto: {foos: {foo: "bar"},
              bars: {foo: "baz"}}
   });
-  server.startSynchronous(PORT);
-  let bso = localRequest("/2.0/123/storage/crypto/foos");
+  server.startSynchronous();
+  let bso = localRequest(server, "/2.0/123/storage/crypto/foos");
   bso.get(function (err) {
     // BSO fetches don't have one.
     do_check_false("x-num-records" in this.response.headers);
-    let col = localRequest("/2.0/123/storage/crypto");
+    let col = localRequest(server, "/2.0/123/storage/crypto");
     col.get(function (err) {
       // Collection fetches do.
       do_check_eq(this.response.headers["x-num-records"], "2");
       server.stop(run_next_test);
     });
   });
 });
 
 add_test(function test_put_delete_put() {
   _("Bug 790397: Ensure BSO deleted flag is reset on PUT.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {bso: {foo: "bar"}}
   });
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   _("Ensure we can PUT an existing record.");
-  let request1 = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request1 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   request1.setHeader("Content-Type", "application/json");
   let payload1 = JSON.stringify({"payload": "foobar"});
   let error1 = doPutRequest(request1, payload1);
   do_check_eq(null, error1);
   do_check_eq(request1.response.status, 204);
 
   _("Ensure we can DELETE it.");
-  let request2 = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request2 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   let error2 = doDeleteRequest(request2);
   do_check_eq(error2, null);
   do_check_eq(request2.response.status, 204);
   do_check_false("content-type" in request2.response.headers);
 
   _("Ensure we can PUT a previously deleted record.");
-  let request3 = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request3 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   request3.setHeader("Content-Type", "application/json");
   let payload3 = JSON.stringify({"payload": "foobar"});
   let error3 = doPutRequest(request3, payload3);
   do_check_eq(null, error3);
   do_check_eq(request3.response.status, 201);
 
   _("Ensure we can GET the re-uploaded record.");
-  let request4 = localRequest("/2.0/123/storage/test/bso", "123", "password");
+  let request4 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   let error4 = doGetRequest(request4);
   do_check_eq(error4, null);
   do_check_eq(request4.response.status, 200);
   do_check_eq(request4.response.headers["content-type"], "application/json");
 
   server.stop(run_next_test);
 });
 
 add_test(function test_collection_get_newer() {
   _("Ensure get with newer argument on collection works.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
-  server.startSynchronous(PORT);
+  server.startSynchronous();
 
   let coll = server.user("123").createCollection("test");
   let bso1 = coll.insert("001", {foo: "bar"});
   let bso2 = coll.insert("002", {bar: "foo"});
 
   // Don't want both records to have the same timestamp.
   bso2.modified = bso1.modified + 1000;
 
   function newerRequest(newer) {
-    return localRequest("/2.0/123/storage/test?newer=" + newer,
+    return localRequest(server, "/2.0/123/storage/test?newer=" + newer,
                         "123", "password");
   }
 
   let request1 = newerRequest(0);
   let error1 = doGetRequest(request1);
   do_check_null(error1);
   do_check_eq(request1.response.status, 200);
   let items1 = JSON.parse(request1.response.body).items;
@@ -672,9 +683,9 @@ add_test(function test_collection_get_ne
   let request3 = newerRequest(bso2.modified + 1);
   let error3 = doGetRequest(request3);
   do_check_null(error3);
   do_check_eq(request3.response.status, 200);
   let items3 = JSON.parse(request3.response.body).items;
   do_check_attribute_count(items3, 0);
 
   server.stop(run_next_test);
-});
\ No newline at end of file
+});
--- a/services/common/tests/unit/test_storageservice_client.js
+++ b/services/common/tests/unit/test_storageservice_client.js
@@ -1,16 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/storageservice.js");
 Cu.import("resource://testing-common/services-common/storageserver.js");
 
-const BASE_URI = "http://localhost:8080/2.0";
-
 function run_test() {
   initTestLogging("Trace");
 
   run_next_test();
 }
 
 function getRandomUser() {
   return "" + (Math.floor(Math.random() * 100000) + 1);
@@ -22,56 +20,59 @@ function getEmptyServer(user=getRandomUs
 
   return storageServerForUsers(users, {
     meta:    {},
     clients: {},
     crypto:  {},
   });
 }
 
-function getClient(user=getRandomUser(), password="password") {
-  let client = new StorageServiceClient(BASE_URI + "/" + user);
+function getClient(server, user=getRandomUser(), password="password") {
+  let identity = server.server.identity;
+  let url = identity.primaryScheme + "://" + identity.primaryHost + ":" +
+            identity.primaryPort + "/2.0/" + user;
+  let client = new StorageServiceClient(url);
   client.addListener({
     onDispatch: function onDispatch(request) {
       let up = user + ":" + password;
       request.request.setHeader("authorization", "Basic " + btoa(up));
     }
   });
 
   return client;
 }
 
 function getServerAndClient(user=getRandomUser(), password="password") {
   let server = getEmptyServer(user, password);
-  let client = getClient(user, password);
+  let client = getClient(server, user, password);
 
   return [server, client, user, password];
 }
 
 add_test(function test_auth_failure_listener() {
   _("Ensure the onAuthFailure listener is invoked.");
 
   let server = getEmptyServer();
-  let client = getClient("324", "INVALID");
+  let client = getClient(server, "324", "INVALID");
   client.addListener({
     onAuthFailure: function onAuthFailure(client, request) {
       _("onAuthFailure");
       server.stop(run_next_test);
     }
   });
 
   let request = client.getCollectionInfo();
   request.dispatch();
 });
 
 add_test(function test_duplicate_listeners() {
   _("Ensure that duplicate listeners aren't installed multiple times.");
 
   let server = getEmptyServer();
-  let client = getClient("1234567", "BAD_PASSWORD");
+  let client = getClient(server, "1234567", "BAD_PASSWORD");
 
   let invokeCount = 0;
   let listener = {
     onAuthFailure: function onAuthFailure() {
       invokeCount++;
     }
   };
 
@@ -581,18 +582,19 @@ add_test(function test_set_bso_condition
 
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_set_bso_argument_errors() {
   _("Ensure BSO set detects invalid arguments.");
 
+  let server = getEmptyServer();
   let bso = new BasicStorageObject();
-  let client = getClient();
+  let client = getClient(server);
 
   let threw = false;
   try {
     client.setBSO(bso);
   } catch (ex) {
     threw = true;
     do_check_eq(ex.name, "Error");
     do_check_neq(ex.message.indexOf("does not have collection defined"), -1);
@@ -620,17 +622,17 @@ add_test(function test_set_bso_argument_
     threw = true;
     do_check_eq(ex.name, "Error");
     do_check_neq(ex.message.indexOf("does not have ID defined"), -1);
   } finally {
     do_check_true(threw);
     threw = false;
   }
 
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 add_test(function test_set_bsos_simple() {
   _("Ensure setBSOs with basic options works.");
 
   let [server, client, username] = getServerAndClient();
   let user = server.user(username);
 
@@ -654,17 +656,18 @@ add_test(function test_set_bsos_simple()
 
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_set_bsos_invalid_bso() {
   _("Ensure that adding an invalid BSO throws.");
 
-  let client = getClient();
+  let server = getEmptyServer();
+  let client = getClient(server);
   let request = client.setBSOs("testcoll");
 
   let threw = false;
 
   // Empty argument is invalid.
   try {
     request.addBSO(null);
   } catch (ex) {
@@ -679,17 +682,17 @@ add_test(function test_set_bsos_invalid_
     request.addBSO(bso);
   } catch (ex) {
     threw = true;
   } finally {
     do_check_true(threw);
     threw = false;
   }
 
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 add_test(function test_set_bsos_newline() {
   _("Ensure that newlines in BSO payloads are formatted properly.");
 
   let [server, client, username] = getServerAndClient();
   let user = server.user(username);
 
--- a/services/common/tests/unit/test_tokenauthenticatedrequest.js
+++ b/services/common/tests/unit/test_tokenauthenticatedrequest.js
@@ -16,36 +16,37 @@ add_test(function test_authenticated_req
 
   let message = "Great Success!";
 
   // TODO: We use a preset key here, but use getTokenFromBrowserIDAssertion()
   // from TokenServerClient to get a real one when possible. (Bug 745800)
   let id = "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x";
   let key = "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=";
   let method = "GET";
-  let uri = CommonUtils.makeURI(TEST_SERVER_URL + "foo");
 
   let nonce = btoa(CryptoUtils.generateRandomBytes(16));
   let ts = Math.floor(Date.now() / 1000);
   let extra = {ts: ts, nonce: nonce};
 
-  let sig = CryptoUtils.computeHTTPMACSHA1(id, key, method, uri, extra);
-  let auth = sig.getHeader();
+  let auth;
 
   let server = httpd_setup({"/foo": function(request, response) {
       do_check_true(request.hasHeader("Authorization"));
       do_check_eq(auth, request.getHeader("Authorization"));
 
       response.setStatusLine(request.httpVersion, 200, "OK");
       response.bodyOutputStream.write(message, message.length);
     }
   });
+  let uri = CommonUtils.makeURI(server.baseURI + "/foo");
+  let sig = CryptoUtils.computeHTTPMACSHA1(id, key, method, uri, extra);
+  auth = sig.getHeader();
 
   let req = new TokenAuthenticatedRESTRequest(uri, {id: id, key: key}, extra);
   let cb = Async.makeSpinningCallback();
   req.get(cb);
   let result = cb.wait();
 
   do_check_eq(null, result);
   do_check_eq(message, req.response.body);
 
   server.stop(run_next_test);
-});
\ No newline at end of file
+});
--- a/services/common/tests/unit/test_tokenserverclient.js
+++ b/services/common/tests/unit/test_tokenserverclient.js
@@ -31,17 +31,17 @@ add_test(function test_working_bid_excha
         uid:          "uid",
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
   let cb = Async.makeSpinningCallback();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
   client.getTokenFromBrowserIDAssertion(url, "assertion", cb);
   let result = cb.wait();
   do_check_eq("object", typeof(result));
   do_check_attribute_count(result, 4);
   do_check_eq(service, result.endpoint);
   do_check_eq("id", result.id);
   do_check_eq("key", result.key);
   do_check_eq("uid", result.uid);
@@ -88,17 +88,17 @@ add_test(function test_conditions_requir
         errors: [{description: description, location: "body", name: ""}],
         urls: {tos: tosURL}
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
 
   function onResponse(error, token) {
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "conditions-required");
     do_check_null(token);
 
     do_check_eq(error.urls.tos, tosURL);
 
@@ -120,17 +120,17 @@ add_test(function test_invalid_403_no_co
         errors: [{description: "irrelevant", location: "body", name: ""}],
         urls: {foo: "http://bar"}
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
 
   function onResponse(error, token) {
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "malformed-response");
     do_check_null(token);
 
     do_check_null(error.urls);
 
@@ -151,17 +151,17 @@ add_test(function test_invalid_403_bad_j
       let body = JSON.stringify({
         foo: "bar"
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
 
   function onResponse(error, token) {
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "malformed-response");
     do_check_null(token);
     do_check_null(error.urls);
 
     server.stop(run_next_test);
@@ -179,17 +179,17 @@ add_test(function test_403_no_urls() {
       response.setHeader("Content-Type", "application/json; charset=utf-8");
 
       let body = "{}";
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
 
   client.getTokenFromBrowserIDAssertion(url, "assertion",
                                         function onResponse(error, result) {
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "malformed-response");
     do_check_null(result);
 
     server.stop(run_next_test);
@@ -214,17 +214,17 @@ add_test(function test_send_conditions_a
         api_endpoint: "http://example.com/",
         uid:          "uid",
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
 
   function onResponse(error, token) {
     do_check_null(error);
 
     // Other tests validate other things.
 
     server.stop(run_next_test);
   }
@@ -233,17 +233,17 @@ add_test(function test_send_conditions_a
 });
 
 add_test(function test_error_404_empty() {
   _("Ensure that 404 responses without proper response are handled properly.");
 
   let server = httpd_setup();
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "foo";
+  let url = server.baseURI + "/foo";
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "malformed-response");
 
     do_check_neq(null, error.response);
     do_check_null(r);
 
     server.stop(run_next_test);
@@ -271,17 +271,17 @@ add_test(function test_error_404_proper_
     do_check_true(error instanceof TokenServerClientServerError);
     do_check_eq(error.cause, "unknown-service");
     do_check_null(token);
 
     server.stop(run_next_test);
   }
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
   client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse);
 });
 
 add_test(function test_bad_json() {
   _("Ensure that malformed JSON is handled properly.");
 
   let server = httpd_setup({
     "/1.0/foo/1.0": function(request, response) {
@@ -289,17 +289,17 @@ add_test(function test_bad_json() {
       response.setHeader("Content-Type", "application/json");
 
       let body = '{"id": "id", baz}'
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_neq(null, error);
     do_check_eq("TokenServerClientServerError", error.name);
     do_check_eq(error.cause, "malformed-response");
     do_check_neq(null, error.response);
     do_check_eq(null, r);
 
     server.stop(run_next_test);
@@ -315,17 +315,17 @@ add_test(function test_400_response() {
       response.setHeader("Content-Type", "application/json; charset=utf-8");
 
       let body = "{}"; // Actual content may not be used.
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
   let client = new TokenServerClient();
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_neq(null, error);
     do_check_eq("TokenServerClientServerError", error.name);
     do_check_neq(null, error.response);
     do_check_eq(error.cause, "malformed-request");
 
     server.stop(run_next_test);
   });
@@ -339,17 +339,17 @@ add_test(function test_unhandled_media_t
       response.setStatusLine(request.httpVersion, 200, "OK");
       response.setHeader("Content-Type", "text/plain");
 
       let body = "hello, world";
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
-  let url = TEST_SERVER_URL + "1.0/foo/1.0";
+  let url = server.baseURI + "/1.0/foo/1.0";
   let client = new TokenServerClient();
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_neq(null, error);
     do_check_eq("TokenServerClientServerError", error.name);
     do_check_neq(null, error.response);
     do_check_eq(null, r);
 
     server.stop(run_next_test);
@@ -369,17 +369,17 @@ add_test(function test_rich_media_types(
         key:          "key",
         api_endpoint: "foo",
         uid:          "uid",
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
-  let url = TEST_SERVER_URL + "foo";
+  let url = server.baseURI + "/foo";
   let client = new TokenServerClient();
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_eq(null, error);
 
     server.stop(run_next_test);
   });
 });
 
@@ -396,17 +396,17 @@ add_test(function test_exception_during_
         key:          "key",
         api_endpoint: "foo",
         uid:          "uid",
       });
       response.bodyOutputStream.write(body, body.length);
     }
   });
 
-  let url = TEST_SERVER_URL + "foo";
+  let url = server.baseURI + "/foo";
   let client = new TokenServerClient();
   let cb = Async.makeSpinningCallback();
   let callbackCount = 0;
 
   client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) {
     do_check_eq(null, error);
 
     cb();
--- a/services/healthreport/tests/xpcshell/test_healthreporter.js
+++ b/services/healthreport/tests/xpcshell/test_healthreporter.js
@@ -17,35 +17,33 @@ Cu.import("resource://gre/modules/servic
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://testing-common/services-common/bagheeraserver.js");
 Cu.import("resource://testing-common/services/metrics/mocks.jsm");
 Cu.import("resource://testing-common/services/healthreport/utils.jsm");
 
 
-const SERVER_HOSTNAME = "localhost";
-const SERVER_PORT = 8080;
-const SERVER_URI = "http://" + SERVER_HOSTNAME + ":" + SERVER_PORT;
+const DUMMY_URI = "http://localhost:62013/";
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
 const HealthReporterState = bsp.HealthReporterState;
 
 
 function defineNow(policy, now) {
   print("Adjusting fake system clock to " + now);
   Object.defineProperty(policy, "now", {
     value: function customNow() {
       return now;
     },
     writable: true,
   });
 }
 
-function getJustReporter(name, uri=SERVER_URI, inspected=false) {
+function getJustReporter(name, uri=DUMMY_URI, inspected=false) {
   let branch = "healthreport.testing." + name + ".";
 
   let prefs = new Preferences(branch + "healthreport.");
   prefs.set("documentServerURI", uri);
   prefs.set("dbName", name);
 
   let reporter;
 
@@ -79,23 +77,22 @@ function getReporter(name, uri, inspecte
       HealthReportProvider);
 
     throw new Task.Result(reporter);
   });
 }
 
 function getReporterAndServer(name, namespace="test") {
   return Task.spawn(function get() {
-    let reporter = yield getReporter(name, SERVER_URI);
-    reporter.serverNamespace = namespace;
+    let server = new BagheeraServer();
+    server.createNamespace(namespace);
+    server.start();
 
-    let server = new BagheeraServer(SERVER_URI);
-    server.createNamespace(namespace);
-
-    server.start(SERVER_PORT);
+    let reporter = yield getReporter(name, server.serverURI);
+    reporter.serverNamespace = namespace;
 
     throw new Task.Result([reporter, server]);
   });
 }
 
 function shutdownServer(server) {
   let deferred = Promise.defer();
   server.stop(deferred.resolve.bind(deferred));
@@ -162,17 +159,17 @@ add_task(function test_shutdown_normal()
 
   // We can't send "quit-application" notification because the xpcshell runner
   // will shut down!
   reporter._initiateShutdown();
   reporter._waitForShutdown();
 });
 
 add_task(function test_shutdown_storage_in_progress() {
-  let reporter = yield getJustReporter("shutdown_storage_in_progress", SERVER_URI, true);
+  let reporter = yield getJustReporter("shutdown_storage_in_progress", DUMMY_URI, true);
 
   reporter.onStorageCreated = function () {
     print("Faking shutdown during storage initialization.");
     reporter._initiateShutdown();
   };
 
   reporter.init();
 
@@ -180,17 +177,17 @@ add_task(function test_shutdown_storage_
   do_check_eq(reporter.providerManagerShutdownCount, 0);
   do_check_eq(reporter.storageCloseCount, 1);
 });
 
 // Ensure that a shutdown triggered while provider manager is initializing
 // results in shutdown and storage closure.
 add_task(function test_shutdown_provider_manager_in_progress() {
   let reporter = yield getJustReporter("shutdown_provider_manager_in_progress",
-                                       SERVER_URI, true);
+                                       DUMMY_URI, true);
 
   reporter.onProviderManagerInitialized = function () {
     print("Faking shutdown during provider manager initialization.");
     reporter._initiateShutdown();
   };
 
   reporter.init();
 
@@ -198,17 +195,17 @@ add_task(function test_shutdown_provider
   reporter._waitForShutdown();
   do_check_eq(reporter.providerManagerShutdownCount, 1);
   do_check_eq(reporter.storageCloseCount, 1);
 });
 
 // Simulates an error during provider manager initialization and verifies we shut down.
 add_task(function test_shutdown_when_provider_manager_errors() {
   let reporter = yield getJustReporter("shutdown_when_provider_manager_errors",
-                                       SERVER_URI, true);
+                                       DUMMY_URI, true);
 
   reporter.onInitializeProviderManagerFinished = function () {
     print("Throwing fake error.");
     throw new Error("Fake error during provider manager initialization.");
   };
 
   reporter.init();
 
@@ -581,17 +578,17 @@ add_task(function test_idle_daily() {
   } finally {
     reporter._shutdown();
   }
 });
 
 add_task(function test_data_submission_transport_failure() {
   let reporter = yield getReporter("data_submission_transport_failure");
   try {
-    reporter.serverURI = "http://localhost:8080/";
+    reporter.serverURI = DUMMY_URI;
     reporter.serverNamespace = "test00";
 
     let deferred = Promise.defer();
     let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000));
     reporter.requestDataUpload(request);
 
     yield deferred.promise;
     do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT);
@@ -868,20 +865,20 @@ add_task(function test_failure_if_not_in
     error = false;
   }
 
   // getJSONPayload always works (to facilitate error upload).
   yield reporter.getJSONPayload();
 });
 
 add_task(function test_upload_on_init_failure() {
-  let reporter = yield getJustReporter("upload_on_init_failure", SERVER_URI, true);
-  let server = new BagheeraServer(SERVER_URI);
+  let server = new BagheeraServer();
+  server.start();
+  let reporter = yield getJustReporter("upload_on_init_failure", server.serverURI, true);
   server.createNamespace(reporter.serverNamespace);
-  server.start(SERVER_PORT);
 
   reporter.onInitializeProviderManagerFinished = function () {
     throw new Error("Fake error during provider manager initialization.");
   };
 
   let deferred = Promise.defer();
 
   let oldOnResult = reporter._onBagheeraResult;
--- a/services/sync/modules-testing/utils.js
+++ b/services/sync/modules-testing/utils.js
@@ -1,34 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
-  "TEST_CLUSTER_URL",
-  "TEST_SERVER_URL",
   "btoa", // It comes from a module import.
   "encryptPayload",
   "setBasicCredentials",
   "SyncTestingInfrastructure",
   "waitForZeroTimer",
 ];
 
 const {utils: Cu} = Components;
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://testing-common/services-common/logging.js");
 Cu.import("resource://testing-common/services/sync/fakeservices.js");
 
-this.TEST_SERVER_URL = "http://localhost:8080/";
-this.TEST_CLUSTER_URL = TEST_SERVER_URL;
-
 /**
  * First wait >100ms (nsITimers can take up to that much time to fire, so
  * we can account for the timer in delayedAutoconnect) and then two event
  * loop ticks (to account for the Utils.nextTick() in autoConnect).
  */
 this.waitForZeroTimer = function waitForZeroTimer(callback) {
   let ticks = 2;
   function wait() {
@@ -48,28 +43,31 @@ this.setBasicCredentials =
   Cu.import("resource://services-sync/service.js", ns);
 
   let auth = ns.Service.identity;
   auth.username = username;
   auth.basicPassword = password;
   auth.syncKey = syncKey;
 }
 
-this.SyncTestingInfrastructure =
- function SyncTestingInfrastructure(username, password, syncKey) {
+this.SyncTestingInfrastructure = function (server, username, password, syncKey) {
   let ns = {};
   Cu.import("resource://services-sync/service.js", ns);
 
   let auth = ns.Service.identity;
   auth.account = username || "foo";
   auth.basicPassword = password || "password";
   auth.syncKey = syncKey || "abcdeabcdeabcdeabcdeabcdea";
 
-  ns.Service.serverURL = TEST_SERVER_URL;
-  ns.Service.clusterURL = TEST_CLUSTER_URL;
+  let i = server.identity;
+  let uri = i.primaryScheme + "://" + i.primaryHost + ":" +
+            i.primaryPort + "/";
+
+  ns.Service.serverURL = uri;
+  ns.Service.clusterURL = uri;
 
   this.logStats = initTestLogging();
   this.fakeFilesystem = new FakeFilesystemService({});
   this.fakeGUIDService = new FakeGUIDService();
   this.fakeCryptoService = new FakeCryptoService();
 }
 
 /**
--- a/services/sync/tests/unit/head_http_server.js
+++ b/services/sync/tests/unit/head_http_server.js
@@ -530,52 +530,53 @@ function SyncServer(callback) {
   this._log     = Log4Moz.repository.getLogger(SYNC_HTTP_LOGGER);
 
   // Install our own default handler. This allows us to mess around with the
   // whole URL space.
   let handler = this.server._handler;
   handler._handleDefault = this.handleDefault.bind(this, handler);
 }
 SyncServer.prototype = {
-  port:   8080,
   server: null,    // HttpServer.
   users:  null,    // Map of username => {collections, password}.
 
   /**
    * Start the SyncServer's underlying HTTP server.
    *
    * @param port
    *        The numeric port on which to start. A falsy value implies the
-   *        default (8080).
+   *        default, a randomly chosen port.
    * @param cb
    *        A callback function (of no arguments) which is invoked after
    *        startup.
    */
   start: function start(port, cb) {
     if (this.started) {
       this._log.warn("Warning: server already started on " + this.port);
       return;
     }
-    if (port) {
-      this.port = port;
-    }
     try {
-      this.server.start(this.port);
+      this.server.start(port);
+      let i = this.server.identity;
+      this.port = i.primaryPort;
+      this.baseURI = i.primaryScheme + "://" + i.primaryHost + ":" +
+                     i.primaryPort + "/";
       this.started = true;
       if (cb) {
         cb();
       }
     } catch (ex) {
       _("==========================================");
-      _("Got exception starting Sync HTTP server on port " + this.port);
+      _("Got exception starting Sync HTTP server.");
       _("Error: " + Utils.exceptionStr(ex));
-      _("Is there a process already listening on port " + this.port + "?");
+      _("Is there a process already listening on port " + port + "?");
       _("==========================================");
       do_throw(ex);
     }
+
   },
 
   /**
    * Stop the SyncServer's HTTP server.
    *
    * @param cb
    *        A callback function. Invoked after the server has been stopped.
    *
--- a/services/sync/tests/unit/test_addons_engine.js
+++ b/services/sync/tests/unit/test_addons_engine.js
@@ -153,31 +153,31 @@ add_test(function test_disabled_install_
 
   Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
 
   const USER       = "foo";
   const PASSWORD   = "password";
   const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";
   const ADDON_ID   = "addon1@tests.mozilla.org";
 
-  new SyncTestingInfrastructure(USER, PASSWORD, PASSPHRASE);
+  let server = new SyncServer();
+  server.start();
+  new SyncTestingInfrastructure(server.server, USER, PASSWORD, PASSPHRASE);
 
   generateNewKeys(Service.collectionKeys);
 
   let contents = {
     meta: {global: {engines: {addons: {version: engine.version,
                                       syncID:  engine.syncID}}}},
     crypto: {},
     addons: {}
   };
 
-  let server = new SyncServer();
   server.registerUser(USER, "password");
   server.createContents(USER, contents);
-  server.start();
 
   let amoServer = new HttpServer();
   amoServer.registerFile("/search/guid:addon1%40tests.mozilla.org",
                          do_get_file("addon1-search.xml"));
 
   let installXPI = ExtensionsTestPath("/addons/test_install1.xpi");
   amoServer.registerFile("/addon1.xpi", do_get_file(installXPI));
   amoServer.start(8888);
@@ -242,18 +242,16 @@ function run_test() {
   Log4Moz.repository.getLogger("Sync.Engine.Addons").level =
     Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Store.Addons").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.Tracker.Addons").level =
     Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.AddonsRepository").level =
     Log4Moz.Level.Trace;
 
-  new SyncTestingInfrastructure();
-
   reconciler.startListening();
 
   // Don't flush to disk in the middle of an event listener!
   // This causes test hangs on WinXP.
   reconciler._shouldPersist = false;
 
   advance_test();
 }
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -8,20 +8,21 @@ Cu.import("resource://services-common/lo
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 Service.engineManager.register(BookmarksEngine);
-var syncTesting = new SyncTestingInfrastructure();
 
 add_test(function bad_record_allIDs() {
-  let syncTesting = new SyncTestingInfrastructure();
+  let server = new SyncServer();
+  server.start();
+  let syncTesting = new SyncTestingInfrastructure(server.server);
 
   _("Ensure that bad Places queries don't cause an error in getAllIDs.");
   let engine = new BookmarksEngine(Service);
   let store = engine._store;
   let badRecordID = PlacesUtils.bookmarks.insertBookmark(
       PlacesUtils.bookmarks.toolbarFolder,
       Utils.makeURI("place:folder=1138"),
       PlacesUtils.bookmarks.DEFAULT_INDEX,
@@ -38,21 +39,23 @@ add_test(function bad_record_allIDs() {
   let all = store.getAllIDs();
 
   _("All IDs: " + JSON.stringify(all));
   do_check_true("menu" in all);
   do_check_true("toolbar" in all);
 
   _("Clean up.");
   PlacesUtils.bookmarks.removeItem(badRecordID);
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 add_test(function test_ID_caching() {
-  let syncTesting = new SyncTestingInfrastructure();
+  let server = new SyncServer();
+  server.start();
+  let syncTesting = new SyncTestingInfrastructure(server.server);
 
   _("Ensure that Places IDs are not cached.");
   let engine = new BookmarksEngine(Service);
   let store = engine._store;
   _("All IDs: " + JSON.stringify(store.getAllIDs()));
 
   let mobileID = store.idForGUID("mobile");
   _("Change the GUID for that item, and drop the mobile anno.");
@@ -78,34 +81,34 @@ add_test(function test_ID_caching() {
   _("New mobile ID: " + newMobileID);
   do_check_true(!!newMobileID);
   do_check_neq(newMobileID, mobileID);
 
   // And it's repeatable, even with creation enabled.
   do_check_eq(newMobileID, store.idForGUID("mobile", false));
 
   do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl");
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 function serverForFoo(engine) {
   return serverForUsers({"foo": "password"}, {
     meta: {global: {engines: {bookmarks: {version: engine.version,
                                           syncID: engine.syncID}}}},
     bookmarks: {}
   });
 }
 
 add_test(function test_processIncoming_error_orderChildren() {
   _("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
-  new SyncTestingInfrastructure();
 
   let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
 
   let collection = server.user("foo").collection("bookmarks");
 
   try {
 
     let folder1_id = PlacesUtils.bookmarks.createFolder(
       PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
     let folder1_guid = store.GUIDForId(folder1_id);
@@ -161,21 +164,20 @@ add_test(function test_processIncoming_e
     Svc.Prefs.resetBranch("");
     Service.recordManager.clearCache();
     server.stop(run_next_test);
   }
 });
 
 add_task(function test_restorePromptsReupload() {
   _("Ensure that restoring from a backup will reupload all records.");
-  new SyncTestingInfrastructure();
-
   let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
 
   let collection = server.user("foo").collection("bookmarks");
 
   Svc.Obs.notify("weave:engine:start-tracking");   // We skip usual startup...
 
   try {
 
     let folder1_id = PlacesUtils.bookmarks.createFolder(
@@ -328,21 +330,20 @@ add_test(function test_mismatched_types(
     "description":null,
     "children":
       ["HCRq40Rnxhrd", "YeyWCV1RVsYw", "GCceVZMhvMbP", "sYi2hevdArlF",
        "vjbZlPlSyGY8", "UtjUhVyrpeG6", "rVq8WMG2wfZI", "Lx0tcy43ZKhZ",
        "oT74WwV8_j4P", "IztsItWVSo3-"],
     "parentid": "toolbar"
   };
 
-  new SyncTestingInfrastructure();
-
   let engine = new BookmarksEngine(Service);
   let store  = engine._store;
   let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
 
   _("GUID: " + store.GUIDForId(6, true));
 
   try {
     let bms = PlacesUtils.bookmarks;
     let oldR = new FakeRecord(BookmarkFolder, oldRecord);
     let newR = new FakeRecord(Livemark, newRecord);
     oldR._parent = PlacesUtils.bookmarks.toolbarFolder;
@@ -371,24 +372,23 @@ add_test(function test_mismatched_types(
     Service.recordManager.clearCache();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_bookmark_guidMap_fail() {
   _("Ensure that failures building the GUID map cause early death.");
 
-  new SyncTestingInfrastructure();
-
   let engine = new BookmarksEngine(Service);
   let store = engine._store;
 
   let store  = engine._store;
   let server = serverForFoo(engine);
   let coll   = server.user("foo").collection("bookmarks");
+  new SyncTestingInfrastructure(server.server);
 
   // Add one item to the server.
   let itemID = PlacesUtils.bookmarks.createFolder(
     PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
   let itemGUID    = store.GUIDForId(itemID);
   let itemPayload = store.createRecord(itemGUID).cleartext;
   coll.insert(itemGUID, encryptPayload(itemPayload));
 
@@ -470,20 +470,19 @@ add_test(function test_bookmark_tag_but_
   store.update(record);
 
   run_next_test();
 });
 
 add_test(function test_misreconciled_root() {
   _("Ensure that we don't reconcile an arbitrary record with a root.");
 
-  new SyncTestingInfrastructure();
-
   let engine = new BookmarksEngine(Service);
   let store = engine._store;
+  let server = serverForFoo(engine);
 
   // Log real hard for this test.
   store._log.trace = store._log.debug;
   engine._log.trace = engine._log.debug;
 
   engine._syncStartup();
 
   // Let's find out where the toolbar is right now.
@@ -529,16 +528,16 @@ add_test(function test_misreconciled_roo
   // the real GUID, instead using a generated one. Sync does the translation.
   let toolbarAfter = store.createRecord("toolbar", "bookmarks");
   let parentGUIDAfter = toolbarAfter.parentid;
   let parentIDAfter = store.idForGUID(parentGUIDAfter);
   do_check_eq(store.GUIDForId(toolbarIDBefore), "toolbar");
   do_check_eq(parentGUIDBefore, parentGUIDAfter);
   do_check_eq(parentIDBefore, parentIDAfter);
 
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 function run_test() {
   initTestLogging("Trace");
   generateNewKeys(Service.collectionKeys);
   run_next_test();
 }
--- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
+++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
@@ -17,18 +17,16 @@ var IOService = Cc["@mozilla.org/network
 
 Service.engineManager.register(BookmarksEngine);
 let engine = Service.engineManager.get("bookmarks");
 let store = engine._store;
 
 // Clean up after other tests. Only necessary in XULRunner.
 store.wipe();
 
-var syncTesting = new SyncTestingInfrastructure();
-
 function newSmartBookmark(parent, uri, position, title, queryID) {
   let id = PlacesUtils.bookmarks.insertBookmark(parent, uri, position, title);
   PlacesUtils.annotations.setItemAnnotation(id, SMART_BOOKMARKS_ANNO,
                                             queryID, 0,
                                             PlacesUtils.annotations.EXPIRE_NEVER);
   return id;
 }
 
@@ -55,17 +53,18 @@ function serverForFoo(engine) {
                                           syncID: engine.syncID}}}},
     bookmarks: {}
   });
 }
 
 // Verify that Places smart bookmarks have their annotation uploaded and
 // handled locally.
 add_test(function test_annotation_uploaded() {
-  new SyncTestingInfrastructure();
+  let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
 
   let startCount = smartBookmarkCount();
 
   _("Start count is " + startCount);
 
   if (startCount > 0) {
     // This can happen in XULRunner.
     clearBookmarks();
@@ -103,17 +102,16 @@ add_test(function test_annotation_upload
 
   _("Make sure the new record carries with it the annotation.");
   do_check_eq("MostVisited", record.queryId);
 
   _("Our count has increased since we started.");
   do_check_eq(smartBookmarkCount(), startCount + 1);
 
   _("Sync record to the server.");
-  let server = serverForFoo(engine);
   let collection = server.user("foo").collection("bookmarks");
 
   try {
     engine.sync();
     let wbos = collection.keys(function (id) {
                  return ["menu", "toolbar", "mobile"].indexOf(id) == -1;
                });
     do_check_eq(wbos.length, 1);
@@ -170,31 +168,31 @@ add_test(function test_annotation_upload
     store.wipe();
     Svc.Prefs.resetBranch("");
     Service.recordManager.clearCache();
     server.stop(run_next_test);
   }
 });
 
 add_test(function test_smart_bookmarks_duped() {
-  new SyncTestingInfrastructure();
+  let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
 
   let parent = PlacesUtils.toolbarFolderId;
   let uri =
     Utils.makeURI("place:sort=" +
                   Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
                   "&maxResults=10");
   let title = "Most Visited";
   let mostVisitedID = newSmartBookmark(parent, uri, -1, title, "MostVisited");
   let mostVisitedGUID = store.GUIDForId(mostVisitedID);
 
   let record = store.createRecord(mostVisitedGUID);
 
   _("Prepare sync.");
-  let server = serverForFoo(engine);
   let collection = server.user("foo").collection("bookmarks");
 
   try {
     engine._syncStartup();
 
     _("Verify that mapDupe uses the anno, discovering a dupe regardless of URI.");
     do_check_eq(mostVisitedGUID, engine._mapDupe(record));
 
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -53,18 +53,17 @@ add_test(function test_bad_hmac() {
     generateNewKeys(Service.collectionKeys);
     let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
     serverKeys.encrypt(Service.identity.syncKeyBundle);
     do_check_true(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success);
   }
 
   try {
     let passphrase     = "abcdeabcdeabcdeabcdeabcdea";
-    Service.serverURL  = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL  = server.baseURI;
     Service.login("foo", "ilovejane", passphrase);
 
     generateNewKeys(Service.collectionKeys);
 
     _("First sync, client record is uploaded");
     do_check_eq(engine.lastRecordUpload, 0);
     check_clients_count(0);
     engine._sync();
@@ -164,28 +163,28 @@ add_test(function test_properties() {
     Svc.Prefs.resetBranch("");
     run_next_test();
   }
 });
 
 add_test(function test_sync() {
   _("Ensure that Clients engine uploads a new client record once a week.");
 
-  new SyncTestingInfrastructure();
-  generateNewKeys(Service.collectionKeys);
-
   let contents = {
     meta: {global: {engines: {clients: {version: engine.version,
                                         syncID: engine.syncID}}}},
     clients: {},
     crypto: {}
   };
   let server = serverForUsers({"foo": "password"}, contents);
   let user   = server.user("foo");
 
+  new SyncTestingInfrastructure(server.server);
+  generateNewKeys(Service.collectionKeys);
+
   function clientWBO() {
     return user.collection("clients").wbo(engine.localID);
   }
 
   try {
 
     _("First sync. Client record is uploaded.");
     do_check_eq(clientWBO(), undefined);
@@ -402,28 +401,28 @@ add_test(function test_process_incoming_
 
   // logout command causes processIncomingCommands to return explicit false.
   do_check_false(engine.processIncomingCommands());
 });
 
 add_test(function test_command_sync() {
   _("Ensure that commands are synced across clients.");
 
-  new SyncTestingInfrastructure();
-
   engine._store.wipe();
   generateNewKeys(Service.collectionKeys);
 
   let contents = {
     meta: {global: {engines: {clients: {version: engine.version,
                                         syncID: engine.syncID}}}},
     clients: {},
     crypto: {}
   };
   let server   = serverForUsers({"foo": "password"}, contents);
+  new SyncTestingInfrastructure(server.server);
+
   let user     = server.user("foo");
   let remoteId = Utils.makeGUID();
 
   function clientWBO(id) {
     return user.collection("clients").wbo(id);
   }
 
   _("Create remote client record");
--- a/services/sync/tests/unit/test_collections_recovery.js
+++ b/services/sync/tests/unit/test_collections_recovery.js
@@ -20,31 +20,30 @@ add_test(function test_missing_crypto_co
         response.bodyOutputStream.write(body, body.length);
       } else {
         handler(request, response);
       }
     };
   }
 
   setBasicCredentials("johndoe", "ilovejane", "a-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
 
   let handlers = {
     "/1.1/johndoe/info/collections": maybe_empty(johnHelper.handler),
     "/1.1/johndoe/storage/crypto/keys": johnU("crypto", new ServerWBO("keys").handler()),
     "/1.1/johndoe/storage/meta/global": johnU("meta",   new ServerWBO("global").handler())
   };
   let collections = ["clients", "bookmarks", "forms", "history",
                      "passwords", "prefs", "tabs"];
   for each (let coll in collections) {
     handlers["/1.1/johndoe/storage/" + coll] =
       johnU(coll, new ServerCollection({}, true).handler());
   }
   let server = httpd_setup(handlers);
+  Service.serverURL = server.baseURI;
 
   try {
     let fresh = 0;
     let orig  = Service._freshStart;
     Service._freshStart = function() {
       _("Called _freshStart.");
       orig.call(Service);
       fresh++;
--- a/services/sync/tests/unit/test_corrupt_keys.js
+++ b/services/sync/tests/unit/test_corrupt_keys.js
@@ -51,18 +51,18 @@ add_task(function test_locally_changed_k
                             weaveLastUsed: 1
                           }}]}]};
     delete Svc.Session;
     Svc.Session = {
       getBrowserState: function () JSON.stringify(myTabs)
     };
 
     setBasicCredentials("johndoe", "password", passphrase);
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI;
+    Service.clusterURL = server.baseURI;
 
     Service.engineManager.register(HistoryEngine);
 
     function corrupt_local_keys() {
       Service.collectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
                                                  Svc.Crypto.generateRandomKey()];
     }
 
--- a/services/sync/tests/unit/test_engine_abort.js
+++ b/services/sync/tests/unit/test_engine_abort.js
@@ -5,36 +5,35 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_test(function test_processIncoming_abort() {
   _("An abort exception, raised in applyIncoming, will abort _processIncoming.");
-  new SyncTestingInfrastructure();
-  generateNewKeys(Service.collectionKeys);
-
   let engine = new RotaryEngine(Service);
 
-  _("Create some server data.");
-  let meta_global = Service.recordManager.set(engine.metaURL,
-                                              new WBORecord(engine.metaURL));
-  meta_global.payload.engines = {rotary: {version: engine.version,
-                                          syncID: engine.syncID}};
-
   let collection = new ServerCollection();
   let id = Utils.makeGUID();
   let payload = encryptPayload({id: id, denomination: "Record No. " + id});
   collection.insert(id, payload);
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  new SyncTestingInfrastructure(server);
+  generateNewKeys(Service.collectionKeys);
+
+  _("Create some server data.");
+  let meta_global = Service.recordManager.set(engine.metaURL,
+                                              new WBORecord(engine.metaURL));
+  meta_global.payload.engines = {rotary: {version: engine.version,
+                                          syncID: engine.syncID}};
   _("Fake applyIncoming to abort.");
   engine._store.applyIncoming = function (record) {
     let ex = {code: Engine.prototype.eEngineAbortApplyIncoming,
               cause: "Nooo"};
     _("Throwing: " + JSON.stringify(ex));
     throw ex;
   };
 
--- a/services/sync/tests/unit/test_errorhandler.js
+++ b/services/sync/tests/unit/test_errorhandler.js
@@ -6,17 +6,17 @@ Cu.import("resource://services-sync/cons
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
-const TEST_MAINTENANCE_URL = "http://localhost:8080/maintenance/";
+const FAKE_SERVER_URL = "http://dummy:9000/";
 const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
 const LOG_PREFIX_SUCCESS = "success-";
 const LOG_PREFIX_ERROR   = "error-";
 
 const PROLONGED_ERROR_DURATION =
   (Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
 
 const NON_PROLONGED_ERROR_DURATION =
@@ -120,20 +120,20 @@ function sync_httpd_setup() {
     "/maintenance/1.1/broken.wipe/storage/crypto/keys":
       upd("crypto", (new ServerWBO("keys")).handler()),
     "/maintenance/1.1/broken.wipe/storage": service_unavailable,
     "/maintenance/1.1/broken.wipe/storage/clients": upd("clients", clientsColl.handler()),
     "/maintenance/1.1/broken.wipe/storage/catapult": service_unavailable
   });
 }
 
-function setUp() {
+function setUp(server) {
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   return generateAndUploadKeys();
 }
 
 function generateAndUploadKeys() {
   generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.identity.syncKeyBundle);
@@ -143,17 +143,17 @@ function generateAndUploadKeys() {
 function clean() {
   Service.startOver();
   Status.resetSync();
   Status.resetBackoff();
 }
 
 add_test(function test_401_logout() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   Svc.Obs.add("weave:service:sync:error", onSyncError);
   function onSyncError() {
@@ -183,17 +183,17 @@ add_test(function test_401_logout() {
 
   _("Starting first sync.");
   Service.sync();
   _("First sync done.");
 });
 
 add_test(function test_credentials_changed_logout() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
   Service.sync();
@@ -223,18 +223,18 @@ add_test(function test_no_lastSync_pref(
 });
 
 add_test(function test_shouldReportError() {
   Status.login = MASTER_PASSWORD_LOCKED;
   do_check_false(errorHandler.shouldReportError());
 
   // Give ourselves a clusterURL so that the temporary 401 no-error situation
   // doesn't come into play.
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = FAKE_SERVER_URL;
+  Service.clusterURL = FAKE_SERVER_URL;
 
   // Test dontIgnoreErrors, non-network, non-prolonged, login error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.login = LOGIN_FAILED_NO_PASSWORD;
   do_check_true(errorHandler.shouldReportError());
 
@@ -402,17 +402,17 @@ add_test(function test_shouldReportError
   do_check_true(errorHandler.shouldReportError());
 
   run_next_test();
 });
 
 add_test(function test_shouldReportError_master_password() {
   _("Test error ignored due to locked master password");
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Monkey patch Service.verifyLogin to imitate
   // master password being locked.
   Service._verifyLogin = Service.verifyLogin;
   Service.verifyLogin = function () {
     Status.login = MASTER_PASSWORD_LOCKED;
     return false;
   };
@@ -426,17 +426,17 @@ add_test(function test_shouldReportError
   clean();
   server.stop(run_next_test);
 });
 
 add_test(function test_login_syncAndReportErrors_non_network_error() {
   // Test non-network errors are reported
   // when calling syncAndReportErrors
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
   Service.identity.basicPassword = null;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
     clean();
     server.stop(run_next_test);
@@ -445,17 +445,17 @@ add_test(function test_login_syncAndRepo
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_non_network_error() {
   // Test non-network errors are reported
   // when calling syncAndReportErrors
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
@@ -470,17 +470,17 @@ add_test(function test_sync_syncAndRepor
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_prolonged_non_network_error() {
   // Test prolonged, non-network errors are
   // reported when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
   Service.identity.basicPassword = null;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
     clean();
     server.stop(run_next_test);
@@ -489,17 +489,17 @@ add_test(function test_login_syncAndRepo
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_prolonged_non_network_error() {
   // Test prolonged, non-network errors are
   // reported when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
@@ -513,18 +513,18 @@ add_test(function test_sync_syncAndRepor
 
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_network_error() {
   // Test network errors are reported when calling syncAndReportErrors.
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = FAKE_SERVER_URL;
+  Service.clusterURL = FAKE_SERVER_URL;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
     clean();
     run_next_test();
   });
@@ -550,18 +550,18 @@ add_test(function test_sync_syncAndRepor
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_prolonged_network_error() {
   // Test prolonged, network errors are reported
   // when calling syncAndReportErrors.
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = FAKE_SERVER_URL;
+  Service.clusterURL = FAKE_SERVER_URL;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
     clean();
     run_next_test();
   });
@@ -586,17 +586,17 @@ add_test(function test_sync_syncAndRepor
 
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_prolonged_non_network_error() {
   // Test prolonged, non-network errors are reported
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
   Service.identity.basicPassword = null;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
     clean();
     server.stop(run_next_test);
@@ -604,17 +604,17 @@ add_test(function test_login_prolonged_n
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_prolonged_non_network_error() {
   // Test prolonged, non-network errors are reported
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
@@ -628,18 +628,18 @@ add_test(function test_sync_prolonged_no
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_prolonged_network_error() {
   // Test prolonged, network errors are reported
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = FAKE_SERVER_URL;
+  Service.clusterURL = FAKE_SERVER_URL;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
     clean();
     run_next_test();
   });
@@ -663,17 +663,17 @@ add_test(function test_sync_prolonged_ne
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_non_network_error() {
   // Test non-network errors are reported
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
   Service.identity.basicPassword = null;
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
     clean();
     server.stop(run_next_test);
@@ -681,17 +681,17 @@ add_test(function test_login_non_network
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_non_network_error() {
   // Test non-network errors are reported
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
@@ -704,18 +704,18 @@ add_test(function test_sync_non_network_
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_network_error() {
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL  = FAKE_SERVER_URL;
+  Service.clusterURL = FAKE_SERVER_URL;
 
   // Test network errors are not reported.
   Svc.Obs.add("weave:ui:clear-error", function onClearError() {
     Svc.Obs.remove("weave:ui:clear-error", onClearError);
 
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
     Services.io.offline = false;
@@ -742,17 +742,17 @@ add_test(function test_sync_network_erro
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_server_maintenance_error() {
   // Test server maintenance errors are not reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   const BACKOFF = 42;
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   function onSyncError() {
@@ -775,22 +775,22 @@ add_test(function test_sync_server_maint
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_info_collections_login_server_maintenance_error() {
   // Test info/collections server maintenance errors are not reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   Service.username = "broken.info";
   setBasicCredentials("broken.info", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   function onUIUpdate() {
@@ -816,21 +816,21 @@ add_test(function test_info_collections_
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_meta_global_login_server_maintenance_error() {
   // Test meta/global server maintenance errors are not reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   function onUIUpdate() {
@@ -856,21 +856,21 @@ add_test(function test_meta_global_login
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_crypto_keys_login_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are not reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   // Force re-download of keys
   Service.collectionKeys.clear();
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
@@ -899,17 +899,17 @@ add_test(function test_crypto_keys_login
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   const BACKOFF = 42;
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
@@ -925,21 +925,21 @@ add_test(function test_sync_prolonged_se
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_info_collections_login_prolonged_server_maintenance_error(){
   // Test info/collections prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.info", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -958,21 +958,21 @@ add_test(function test_info_collections_
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
   // Test meta/global prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -991,21 +991,21 @@ add_test(function test_meta_global_login
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_download_crypto_keys_login_prolonged_server_maintenance_error(){
   // Test crypto/keys prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
   // Force re-download of keys
   Service.collectionKeys.clear();
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
@@ -1029,18 +1029,18 @@ add_test(function test_download_crypto_k
 });
 
 add_test(function test_upload_crypto_keys_login_prolonged_server_maintenance_error(){
   // Test crypto/keys prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1063,18 +1063,18 @@ add_test(function test_upload_crypto_key
 
 add_test(function test_wipeServer_login_prolonged_server_maintenance_error(){
   // Test that we report prolonged server maintenance errors that occur whilst
   // wiping the server.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1097,18 +1097,18 @@ add_test(function test_wipeServer_login_
 
 add_test(function test_wipeRemote_prolonged_server_maintenance_error(){
   // Test that we report prolonged server maintenance errors that occur whilst
   // wiping all remote devices.
   let server = sync_httpd_setup();
 
   server.registerPathHandler("/1.1/broken.wipe/storage/catapult", service_unavailable);
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
   generateAndUploadKeys();
 
   let engine = engineManager.get("catapult");
   engine.exception = null;
   engine.enabled = true;
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
@@ -1135,17 +1135,17 @@ add_test(function test_wipeRemote_prolon
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_syncAndReportErrors_server_maintenance_error() {
   // Test server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   const BACKOFF = 42;
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
@@ -1162,21 +1162,21 @@ add_test(function test_sync_syncAndRepor
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_info_collections_login_syncAndReportErrors_server_maintenance_error() {
   // Test info/collections server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.info", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1196,21 +1196,21 @@ add_test(function test_info_collections_
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_error() {
   // Test meta/global server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1230,21 +1230,21 @@ add_test(function test_meta_global_login
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_download_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
   // Force re-download of keys
   Service.collectionKeys.clear();
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
@@ -1269,18 +1269,18 @@ add_test(function test_download_crypto_k
 
 add_test(function test_upload_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1303,18 +1303,18 @@ add_test(function test_upload_crypto_key
 
 add_test(function test_wipeServer_login_syncAndReportErrors_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1336,18 +1336,18 @@ add_test(function test_wipeServer_login_
 });
 
 add_test(function test_wipeRemote_syncAndReportErrors_server_maintenance_error(){
   // Test that we report prolonged server maintenance errors that occur whilst
   // wiping all remote devices.
   let server = sync_httpd_setup();
 
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
   generateAndUploadKeys();
 
   let engine = engineManager.get("catapult");
   engine.exception = null;
   engine.enabled = true;
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
@@ -1374,17 +1374,17 @@ add_test(function test_wipeRemote_syncAn
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are
   // reported when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   const BACKOFF = 42;
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
@@ -1401,21 +1401,21 @@ add_test(function test_sync_syncAndRepor
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_info_collections_login_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test info/collections server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.info", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1435,21 +1435,21 @@ add_test(function test_info_collections_
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test meta/global server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1469,21 +1469,21 @@ add_test(function test_meta_global_login
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.syncAndReportErrors();
 });
 
 add_test(function test_download_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
   // Force re-download of keys
   Service.collectionKeys.clear();
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
@@ -1508,18 +1508,18 @@ add_test(function test_download_crypto_k
 
 add_test(function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1542,18 +1542,18 @@ add_test(function test_upload_crypto_key
 
 add_test(function test_wipeServer_login_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test crypto/keys server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
 
   // Start off with an empty account, do not upload a key.
   setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_MAINTENANCE_URL;
-  Service.clusterURL = TEST_MAINTENANCE_URL;
+  Service.serverURL = server.baseURI + "/maintenance/";
+  Service.clusterURL = server.baseURI + "/maintenance/";
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
     Svc.Obs.remove("weave:service:backoff:interval", observe);
     backoffInterval = subject;
   });
 
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
@@ -1610,17 +1610,17 @@ add_test(function test_sync_engine_gener
       do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
                   LOG_PREFIX_ERROR);
 
       clean();
       server.stop(run_next_test);
     });
   });
 
-  do_check_true(setUp());
+  do_check_true(setUp(server));
   Service.sync();
 });
 
 add_test(function test_logs_on_sync_error_despite_shouldReportError() {
   _("Ensure that an error is still logged when weave:service:sync:error " +
     "is notified, despite shouldReportError returning false.");
 
   let log = Log4Moz.repository.getLogger("Sync.ErrorHandler");
@@ -1703,11 +1703,11 @@ add_test(function test_engine_applyFaile
     do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
                 LOG_PREFIX_ERROR);
 
     clean();
     server.stop(run_next_test);
   });
 
   do_check_eq(Status.engines["catapult"], undefined);
-  do_check_true(setUp());
+  do_check_true(setUp(server));
   Service.sync();
 });
--- a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
+++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
@@ -48,79 +48,79 @@ function sync_httpd_setup() {
     "/1.1/johndoe/info/collections":    collectionsHelper.handler,
     "/1.1/johndoe/storage/meta/global": upd("meta",    globalWBO.handler()),
     "/1.1/johndoe/storage/clients":     upd("clients", clientsColl.handler()),
     "/1.1/johndoe/storage/crypto/keys": upd("crypto",  keysWBO.handler())
   };
   return httpd_setup(handlers);
 }
 
-function setUp() {
+function setUp(server) {
   setBasicCredentials("johndoe", "ilovejane", "aabcdeabcdeabcdeabcdeabcde");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
   new FakeCryptoService();
 }
 
-function generateAndUploadKeys() {
+function generateAndUploadKeys(server) {
   generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.identity.syncKeyBundle);
-  let res = Service.resource("http://localhost:8080/1.1/johndoe/storage/crypto/keys");
+  let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys");
   return serverKeys.upload(res).success;
 }
 
 
 add_test(function test_backoff500() {
   _("Test: HTTP 500 sets backoff status.");
-  setUp();
   let server = sync_httpd_setup();
+  setUp(server);
 
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 500};
 
   try {
     do_check_false(Status.enforceBackoff);
 
     // Forcibly create and upload keys here -- otherwise we don't get to the 500!
-    do_check_true(generateAndUploadKeys());
+    do_check_true(generateAndUploadKeys(server));
 
     Service.login();
     Service.sync();
     do_check_true(Status.enforceBackoff);
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
   } finally {
     Status.resetBackoff();
     Service.startOver();
   }
   server.stop(run_next_test);
 });
 
 add_test(function test_backoff503() {
   _("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status.");
-  setUp();
   let server = sync_httpd_setup();
+  setUp(server);
 
   const BACKOFF = 42;
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function (subject) {
     backoffInterval = subject;
   });
 
   try {
     do_check_false(Status.enforceBackoff);
 
-    do_check_true(generateAndUploadKeys());
+    do_check_true(generateAndUploadKeys(server));
 
     Service.login();
     Service.sync();
 
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, BACKOFF);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
@@ -129,124 +129,130 @@ add_test(function test_backoff503() {
     Status.resetSync();
     Service.startOver();
   }
   server.stop(run_next_test);
 });
 
 add_test(function test_overQuota() {
   _("Test: HTTP 400 with body error code 14 means over quota.");
-  setUp();
   let server = sync_httpd_setup();
+  setUp(server);
 
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 400,
                       toString: function() "14"};
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-    do_check_true(generateAndUploadKeys());
+    do_check_true(generateAndUploadKeys(server));
 
     Service.login();
     Service.sync();
 
     do_check_eq(Status.sync, OVER_QUOTA);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
   } finally {
     Status.resetSync();
     Service.startOver();
   }
   server.stop(run_next_test);
 });
 
 add_test(function test_service_networkError() {
   _("Test: Connection refused error from Service.sync() leads to the right status code.");
-  setUp();
-  // Provoke connection refused.
-  Service.clusterURL = "http://localhost:12345/";
+  let server = sync_httpd_setup();
+  setUp(server);
+  server.stop(() => {
+    // Provoke connection refused.
+    Service.clusterURL = "http://localhost:12345/";
 
-  try {
-    do_check_eq(Status.sync, SYNC_SUCCEEDED);
+    try {
+      do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-    Service._loggedIn = true;
-    Service.sync();
+      Service._loggedIn = true;
+      Service.sync();
 
-    do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
-    do_check_eq(Status.service, SYNC_FAILED);
-  } finally {
-    Status.resetSync();
-    Service.startOver();
-  }
-  run_next_test();
+      do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
+      do_check_eq(Status.service, SYNC_FAILED);
+    } finally {
+      Status.resetSync();
+      Service.startOver();
+    }
+    run_next_test();
+  });
 });
 
 add_test(function test_service_offline() {
   _("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count.");
-  setUp();
-  Services.io.offline = true;
+  let server = sync_httpd_setup();
+  setUp(server);
+  server.stop(() => {
+    Services.io.offline = true;
 
-  try {
-    do_check_eq(Status.sync, SYNC_SUCCEEDED);
-
-    Service._loggedIn = true;
-    Service.sync();
+    try {
+      do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-    do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
-    do_check_eq(Status.service, SYNC_FAILED);
-  } finally {
-    Status.resetSync();
-    Service.startOver();
-  }
-  Services.io.offline = false;
-  run_next_test();
+      Service._loggedIn = true;
+      Service.sync();
+
+      do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
+      do_check_eq(Status.service, SYNC_FAILED);
+    } finally {
+      Status.resetSync();
+      Service.startOver();
+    }
+    Services.io.offline = false;
+    run_next_test();
+  });
 });
 
 add_test(function test_engine_networkError() {
   _("Test: Network related exceptions from engine.sync() lead to the right status code.");
-  setUp();
   let server = sync_httpd_setup();
+  setUp(server);
 
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST",
                                           Cr.NS_ERROR_UNKNOWN_HOST);
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-    do_check_true(generateAndUploadKeys());
+    do_check_true(generateAndUploadKeys(server));
 
     Service.login();
     Service.sync();
 
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
   } finally {
     Status.resetSync();
     Service.startOver();
   }
   server.stop(run_next_test);
 });
 
 add_test(function test_resource_timeout() {
-  setUp();
   let server = sync_httpd_setup();
+  setUp(server);
 
   let engine = engineManager.get("catapult");
   engine.enabled = true;
   // Resource throws this when it encounters a timeout.
   engine.exception = Components.Exception("Aborting due to channel inactivity.",
                                           Cr.NS_ERROR_NET_TIMEOUT);
 
   try {
     do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-    do_check_true(generateAndUploadKeys());
+    do_check_true(generateAndUploadKeys(server));
 
     Service.login();
     Service.sync();
 
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
   } finally {
     Status.resetSync();
--- a/services/sync/tests/unit/test_history_engine.js
+++ b/services/sync/tests/unit/test_history_engine.js
@@ -12,31 +12,35 @@ Cu.import("resource://testing-common/ser
 
 Service.engineManager.clear();
 
 add_test(function test_processIncoming_mobile_history_batched() {
   _("SyncEngine._processIncoming works on history engine.");
 
   let FAKE_DOWNLOAD_LIMIT = 100;
 
-  new SyncTestingInfrastructure();
-
   Svc.Prefs.set("client.type", "mobile");
   PlacesUtils.history.removeAllPages();
   Service.engineManager.register(HistoryEngine);
 
   // A collection that logs each GET
   let collection = new ServerCollection();
   collection.get_log = [];
   collection._get = collection.get;
   collection.get = function (options) {
     this.get_log.push(options);
     return this._get(options);
   };
 
+  let server = sync_httpd_setup({
+    "/1.1/foo/storage/history": collection.handler()
+  });
+
+  new SyncTestingInfrastructure(server);
+
   // Let's create some 234 server side history records. They're all at least
   // 10 minutes old.
   let visitType = Ci.nsINavHistoryService.TRANSITION_LINK;
   for (var i = 0; i < 234; i++) {
     let id = 'record-no' + ("00" + i).slice(-3);
     let modified = Date.now()/1000 - 60*(i+10);
     let payload = encryptPayload({
       id: id,
@@ -46,20 +50,16 @@ add_test(function test_processIncoming_m
         visits: [{date: (modified - 5) * 1000000, type: visitType}],
         deleted: false});
 
     let wbo = new ServerWBO(id, payload);
     wbo.modified = modified;
     collection.insertWBO(wbo);
   }
 
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/history": collection.handler()
-  });
-
   let engine = Service.engineManager.get("history");
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {history: {version: engine.version,
                                            syncID: engine.syncID}};
 
   try {
 
--- a/services/sync/tests/unit/test_hmac_error.js
+++ b/services/sync/tests/unit/test_hmac_error.js
@@ -17,18 +17,16 @@ let hmacErrorCount = 0;
   };
 })();
 
 function shared_setup() {
   hmacErrorCount = 0;
 
   // Do not instantiate SyncTestingInfrastructure; we need real crypto.
   setBasicCredentials("foo", "foo", "aabcdeabcdeabcdeabcdeabcde");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
 
   // Make sure RotaryEngine is the only one we sync.
   Service.engineManager._engines = {};
   Service.engineManager.register(RotaryEngine);
   let engine = Service.engineManager.get("rotary");
   engine.enabled = true;
   engine.lastSync = 123; // Needs to be non-zero so that tracker is queried.
   engine._store.items = {flying: "LNER Class A3 4472",
@@ -75,16 +73,17 @@ add_test(function hmac_error_during_404(
     "/1.1/foo/info/collections": collectionsHelper.handler,
     "/1.1/foo/storage/meta/global": upd("meta", global.handler()),
     "/1.1/foo/storage/crypto/keys": upd("crypto", keys404Handler),
     "/1.1/foo/storage/clients": upd("clients", clientsColl.handler()),
     "/1.1/foo/storage/rotary": upd("rotary", rotaryColl.handler())
   };
 
   let server = sync_httpd_setup(handlers);
+  Service.serverURL = server.baseURI;
 
   try {
     _("Syncing.");
     Service.sync();
     _("Partially resetting client, as if after a restart, and forcing redownload.");
     Service.collectionKeys.clear();
     engine.lastSync = 0;        // So that we redownload records.
     key404Counter = 1;
@@ -150,16 +149,17 @@ add_test(function hmac_error_during_node
     "/1.1/foo/info/collections":    collectionsHelper.handler,
     "/1.1/foo/storage/meta/global": upd("meta", global.handler()),
     "/1.1/foo/storage/crypto/keys": upd("crypto", keysWBO.handler()),
     "/1.1/foo/storage/clients":     upd401("clients", clientsColl.handler()),
     "/1.1/foo/storage/rotary":      upd("rotary", rotaryColl.handler())
   };
 
   let server = sync_httpd_setup(handlers);
+  Service.serverURL = server.baseURI;
   _("Syncing.");
   // First hit of clients will 401. This will happen after meta/global and
   // keys -- i.e., in the middle of the sync, but before RotaryEngine.
   should401 = true;
 
   // Use observers to perform actions when our sync finishes.
   // This allows us to observe the automatic next-tick sync that occurs after
   // an abort.
--- a/services/sync/tests/unit/test_httpd_sync_server.js
+++ b/services/sync/tests/unit/test_httpd_sync_server.js
@@ -60,32 +60,31 @@ add_test(function test_url_parsing() {
   let [all, storage, collection, id] = parts;
   do_check_eq(all, "storage");
   do_check_eq(collection, undefined);
 
   run_next_test();
 });
 
 Cu.import("resource://services-common/rest.js");
-function localRequest(path) {
+function localRequest(server, path) {
   _("localRequest: " + path);
-  let url = "http://127.0.0.1:8080" + path;
+  let url = server.baseURI.substr(0, server.baseURI.length - 1) + path;
   _("url: " + url);
   return new RESTRequest(url);
 }
 
 add_test(function test_basic_http() {
   let server = new SyncServer();
   server.registerUser("john", "password");
   do_check_true(server.userExists("john"));
-  server.start(8080, function () {
+  server.start(null, function () {
     _("Started on " + server.port);
-    do_check_eq(server.port, 8080);
     Utils.nextTick(function () {
-      let req = localRequest("/1.1/john/storage/crypto/keys");
+      let req = localRequest(server, "/1.1/john/storage/crypto/keys");
       _("req is " + req);
       req.get(function (err) {
         do_check_eq(null, err);
         Utils.nextTick(function () {
           server.stop(run_next_test);
         });
       });
     });
@@ -98,51 +97,50 @@ add_test(function test_info_collections(
   });
   function responseHasCorrectHeaders(r) {
     do_check_eq(r.status, 200);
     do_check_eq(r.headers["content-type"], "application/json");
     do_check_true("x-weave-timestamp" in r.headers);
   }
 
   server.registerUser("john", "password");
-  server.start(8080, function () {
-    do_check_eq(server.port, 8080);
+  server.start(null, function () {
     Utils.nextTick(function () {
-      let req = localRequest("/1.1/john/info/collections");
+      let req = localRequest(server, "/1.1/john/info/collections");
       req.get(function (err) {
         // Initial info/collections fetch is empty.
         do_check_eq(null, err);
         responseHasCorrectHeaders(this.response);
 
         do_check_eq(this.response.body, "{}");
         Utils.nextTick(function () {
           // When we PUT something to crypto/keys, "crypto" appears in the response.
           function cb(err) {
             do_check_eq(null, err);
             responseHasCorrectHeaders(this.response);
             let putResponseBody = this.response.body;
             _("PUT response body: " + JSON.stringify(putResponseBody));
 
-            req = localRequest("/1.1/john/info/collections");
+            req = localRequest(server, "/1.1/john/info/collections");
             req.get(function (err) {
               do_check_eq(null, err);
               responseHasCorrectHeaders(this.response);
               let expectedColl = server.getCollection("john", "crypto");
               do_check_true(!!expectedColl);
               let modified = expectedColl.timestamp;
               do_check_true(modified > 0);
               do_check_eq(putResponseBody, modified);
               do_check_eq(JSON.parse(this.response.body).crypto, modified);
               Utils.nextTick(function () {
                 server.stop(run_next_test);
               });
             });
           }
           let payload = JSON.stringify({foo: "bar"});
-          localRequest("/1.1/john/storage/crypto/keys").put(payload, cb);
+          localRequest(server, "/1.1/john/storage/crypto/keys").put(payload, cb);
         });
       });
     });
   });
 });
 
 add_test(function test_storage_request() {
   let keysURL = "/1.1/john/storage/crypto/keys?foo=bar";
@@ -158,54 +156,54 @@ add_test(function test_storage_request()
   });
   let coll = server.user("john").collection("crypto");
   do_check_true(!!coll);
 
   _("We're tracking timestamps.");
   do_check_true(coll.timestamp >= creation);
 
   function retrieveWBONotExists(next) {
-    let req = localRequest(keysURL);
+    let req = localRequest(server, keysURL);
     req.get(function (err) {
       _("Body is " + this.response.body);
       _("Modified is " + this.response.newModified);
       do_check_eq(null, err);
       do_check_eq(this.response.status, 404);
       do_check_eq(this.response.body, "Not found");
       Utils.nextTick(next);
     });
   }
   function retrieveWBOExists(next) {
-    let req = localRequest(foosURL);
+    let req = localRequest(server, foosURL);
     req.get(function (err) {
       _("Body is " + this.response.body);
       _("Modified is " + this.response.newModified);
       let parsedBody = JSON.parse(this.response.body);
       do_check_eq(parsedBody.id, "foos");
       do_check_eq(parsedBody.modified, coll.wbo("foos").modified);
       do_check_eq(JSON.parse(parsedBody.payload).foo, "bar");
       Utils.nextTick(next);
     });
   }
   function deleteWBONotExists(next) {
-    let req = localRequest(keysURL);
+    let req = localRequest(server, keysURL);
     server.callback.onItemDeleted = function (username, collection, wboID) {
       do_throw("onItemDeleted should not have been called.");
     };
 
     req.delete(function (err) {
       _("Body is " + this.response.body);
       _("Modified is " + this.response.newModified);
       do_check_eq(this.response.status, 200);
       delete server.callback.onItemDeleted;
       Utils.nextTick(next);
     });
   }
   function deleteWBOExists(next) {
-    let req = localRequest(foosURL);
+    let req = localRequest(server, foosURL);
     server.callback.onItemDeleted = function (username, collection, wboID) {
       _("onItemDeleted called for " + collection + "/" + wboID);
       delete server.callback.onItemDeleted;
       do_check_eq(username, "john");
       do_check_eq(collection, "crypto");
       do_check_eq(wboID, "foos");
       Utils.nextTick(next);
     };
@@ -215,45 +213,45 @@ add_test(function test_storage_request()
       _("Modified is " + this.response.newModified);
       do_check_eq(this.response.status, 200);
     });
   }
   function deleteStorage(next) {
     _("Testing DELETE on /storage.");
     let now = server.timestamp();
     _("Timestamp: " + now);
-    let req = localRequest(storageURL);
+    let req = localRequest(server, storageURL);
     req.delete(function (err) {
       _("Body is " + this.response.body);
       _("Modified is " + this.response.newModified);
       let parsedBody = JSON.parse(this.response.body);
       do_check_true(parsedBody >= now);
       do_check_empty(server.users["john"].collections);
       Utils.nextTick(next);
     });
   }
   function getStorageFails(next) {
     _("Testing that GET on /storage fails.");
-    let req = localRequest(storageURL);
+    let req = localRequest(server, storageURL);
     req.get(function (err) {
       do_check_eq(this.response.status, 405);
       do_check_eq(this.response.headers["allow"], "DELETE");
       Utils.nextTick(next);
     });
   }
   function getMissingCollectionWBO(next) {
     _("Testing that fetching a WBO from an on-existent collection 404s.");
-    let req = localRequest(storageURL + "/foobar/baz");
+    let req = localRequest(server, storageURL + "/foobar/baz");
     req.get(function (err) {
       do_check_eq(this.response.status, 404);
       Utils.nextTick(next);
     });
   }
 
-  server.start(8080,
+  server.start(null,
     Async.chain(
       retrieveWBONotExists,
       retrieveWBOExists,
       deleteWBOExists,
       deleteWBONotExists,
       getStorageFails,
       getMissingCollectionWBO,
       deleteStorage,
@@ -265,22 +263,22 @@ add_test(function test_storage_request()
 add_test(function test_x_weave_records() {
   let server = new SyncServer();
   server.registerUser("john", "password");
 
   server.createContents("john", {
     crypto: {foos: {foo: "bar"},
              bars: {foo: "baz"}}
   });
-  server.start(8080, function () {
-    let wbo = localRequest("/1.1/john/storage/crypto/foos");
+  server.start(null, function () {
+    let wbo = localRequest(server, "/1.1/john/storage/crypto/foos");
     wbo.get(function (err) {
       // WBO fetches don't have one.
       do_check_false("x-weave-records" in this.response.headers);
-      let col = localRequest("/1.1/john/storage/crypto");
+      let col = localRequest(server, "/1.1/john/storage/crypto");
       col.get(function (err) {
         // Collection fetches do.
         do_check_eq(this.response.headers["x-weave-records"], "2");
         server.stop(run_next_test);
       });
     });
   });
 });
--- a/services/sync/tests/unit/test_interval_triggers.js
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -30,20 +30,20 @@ function sync_httpd_setup() {
     "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
     "/1.1/johndoe/info/collections": collectionsHelper.handler,
     "/1.1/johndoe/storage/crypto/keys":
       upd("crypto", (new ServerWBO("keys")).handler()),
     "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler())
   });
 }
 
-function setUp() {
+function setUp(server) {
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.identity.syncKeyBundle);
   return serverKeys.upload(Service.resource(Service.cryptoKeysURL));
 }
 
 function run_test() {
@@ -60,17 +60,17 @@ add_test(function test_successful_sync_a
   let syncSuccesses = 0;
   function onSyncFinish() {
     _("Sync success.");
     syncSuccesses++;
   };
   Svc.Obs.add("weave:service:sync:finish", onSyncFinish);
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Confirm defaults
   do_check_false(scheduler.idle);
   do_check_false(scheduler.numClients > 1);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.hasIncomingItems);
 
   _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
@@ -164,17 +164,17 @@ add_test(function test_unsuccessful_sync
   }
   Svc.Obs.add("weave:service:sync:error", onSyncError);
 
   _("Test unsuccessful sync calls adjustSyncInterval");
   // Force sync to fail.
   Svc.Prefs.set("firstSync", "notReady");
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Confirm defaults
   do_check_false(scheduler.idle);
   do_check_false(scheduler.numClients > 1);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.hasIncomingItems);
 
   _("Test as long as numClients <= 1 our sync interval is SINGLE_USER.");
@@ -256,17 +256,17 @@ add_test(function test_unsuccessful_sync
 
   Service.startOver();
   Svc.Obs.remove("weave:service:sync:error", onSyncError);
   server.stop(run_next_test);
 });
 
 add_test(function test_back_triggers_sync() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Single device: no sync triggered.
   scheduler.idle = true;
   scheduler.observe(null, "back", Svc.Prefs.get("scheduler.idleTime"));
   do_check_false(scheduler.idle);
 
   // Multiple devices: sync is triggered.
   clientsEngine._store.create({id: "foo", cleartext: "bar"});
@@ -286,17 +286,17 @@ add_test(function test_back_triggers_syn
 
   scheduler.idle = true;
   scheduler.observe(null, "back", Svc.Prefs.get("scheduler.idleTime"));
   do_check_false(scheduler.idle);
 });
 
 add_test(function test_adjust_interval_on_sync_error() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   let syncFailures = 0;
   function onSyncError() {
     _("Sync error.");
     syncFailures++;
   }
   Svc.Obs.add("weave:service:sync:error", onSyncError);
 
@@ -322,17 +322,17 @@ add_test(function test_adjust_interval_o
 
 add_test(function test_bug671378_scenario() {
   // Test scenario similar to bug 671378. This bug appeared when a score
   // update occurred that wasn't large enough to trigger a sync so
   // scheduleNextSync() was called without a time interval parameter,
   // setting nextSync to a non-zero value and preventing the timer from
   // being adjusted in the next call to scheduleNextSync().
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   let syncSuccesses = 0;
   function onSyncFinish() {
     _("Sync success.");
     syncSuccesses++;
   };
   Svc.Obs.add("weave:service:sync:finish", onSyncFinish);
 
--- a/services/sync/tests/unit/test_jpakeclient.js
+++ b/services/sync/tests/unit/test_jpakeclient.js
@@ -164,36 +164,35 @@ let BaseController = {
   }
 };
 
 
 const DATA = {"msg": "eggstreamly sekrit"};
 const POLLINTERVAL = 50;
 
 function run_test() {
-  Svc.Prefs.set("jpake.serverURL", TEST_SERVER_URL);
+  server = httpd_setup({"/new_channel": server_new_channel,
+                        "/report":      server_report});
+  Svc.Prefs.set("jpake.serverURL", server.baseURI + "/");
   Svc.Prefs.set("jpake.pollInterval", POLLINTERVAL);
   Svc.Prefs.set("jpake.maxTries", 2);
   Svc.Prefs.set("jpake.firstMsgMaxTries", 5);
   Svc.Prefs.set("jpake.lastMsgMaxTries", 5);
   // Ensure clean up
   Svc.Obs.add("profile-before-change", function() {
     Svc.Prefs.resetBranch("");
   });
 
   // Ensure PSM is initialized.
   Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
 
   // Simulate Sync setup with credentials in place. We want to make
   // sure the J-PAKE requests don't include those data.
   setBasicCredentials("johndoe", "ilovejane");
 
-  server = httpd_setup({"/new_channel": server_new_channel,
-                        "/report":      server_report});
-
   initTestLogging("Trace");
   Log4Moz.repository.getLogger("Sync.JPAKEClient").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Common.RESTRequest").level =
     Log4Moz.Level.Trace;
   run_next_test();
 }
 
 
--- a/services/sync/tests/unit/test_node_reassignment.js
+++ b/services/sync/tests/unit/test_node_reassignment.js
@@ -54,39 +54,39 @@ function handleReassign(handler, req, re
   resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
   resp.setHeader("Content-Type", "application/json");
   resp.bodyOutputStream.write(reassignBody, reassignBody.length);
 }
 
 /**
  * A node assignment handler.
  */
-const newNodeBody = "http://localhost:8080/";
 function installNodeHandler(server, next) {
+  let newNodeBody = server.baseURI;
   function handleNodeRequest(req, resp) {
     _("Client made a request for a node reassignment.");
     resp.setStatusLine(req.httpVersion, 200, "OK");
     resp.setHeader("Content-Type", "text/plain");
     resp.bodyOutputStream.write(newNodeBody, newNodeBody.length);
     Utils.nextTick(next);
   }
   let nodePath = "/user/1.0/johndoe/node/weave";
   server.server.registerPathHandler(nodePath, handleNodeRequest);
   _("Registered node handler at " + nodePath);
 }
 
 function prepareServer() {
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
 
-  do_check_eq(Service.userAPIURI, "http://localhost:8080/user/1.0/");
   let server = new SyncServer();
   server.registerUser("johndoe");
   server.start();
+  Service.serverURL = server.baseURI;
+  Service.clusterURL = server.baseURI;
+  do_check_eq(Service.userAPIURI, server.baseURI + "user/1.0/");
   return server;
 }
 
 function getReassigned() {
   try {
     return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
   } catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
     return false;
--- a/services/sync/tests/unit/test_records_wbo.js
+++ b/services/sync/tests/unit/test_records_wbo.js
@@ -46,25 +46,25 @@ function test_fetch() {
     "/record2": httpd_handler(200, "OK", JSON.stringify(record2)),
     "/coll":    httpd_handler(200, "OK", JSON.stringify(coll))
   });
   do_test_pending();
 
   try {
     _("Fetching a WBO record");
     let rec = new WBORecord("coll", "record");
-    rec.fetch(Service.resource("http://localhost:8080/record"));
+    rec.fetch(Service.resource(server.baseURI + "/record"));
     do_check_eq(rec.id, "asdf-1234-asdf-1234"); // NOT "record"!
 
     do_check_eq(rec.modified, 2454725.98283);
     do_check_eq(typeof(rec.payload), "object");
     do_check_eq(rec.payload.cheese, "roquefort");
 
     _("Fetching a WBO record using the record manager");
-    let rec2 = Service.recordManager.get("http://localhost:8080/record2");
+    let rec2 = Service.recordManager.get(server.baseURI + "/record2");
     do_check_eq(rec2.id, "record2");
     do_check_eq(rec2.modified, 2454725.98284);
     do_check_eq(typeof(rec2.payload), "object");
     do_check_eq(rec2.payload.cheese, "gruyere");
     do_check_eq(Service.recordManager.response.status, 200);
 
     // Testing collection extraction.
     _("Extracting collection.");
--- a/services/sync/tests/unit/test_resource.js
+++ b/services/sync/tests/unit/test_resource.js
@@ -171,31 +171,31 @@ function run_test() {
     "/quota-error": server_quota_error
   });
 
   Svc.Prefs.set("network.numRetries", 1); // speed up test
 
   // This apparently has to come first in order for our PAC URL to be hit.
   // Don't put any other HTTP requests earlier in the file!
   _("Testing handling of proxy auth redirection.");
-  PACSystemSettings.PACURI = "http://localhost:8080/pac1";
+  PACSystemSettings.PACURI = server.baseURI + "/pac1";
   installFakePAC();
-  let proxiedRes = new Resource("http://localhost:8080/open");
+  let proxiedRes = new Resource(server.baseURI + "/open");
   let content = proxiedRes.get();
   do_check_true(pacFetched);
   do_check_true(fetched);
   do_check_eq(content, "This path exists");
   pacFetched = fetched = false;
   uninstallFakePAC();
 
   _("Resource object members");
-  let res = new Resource("http://localhost:8080/open");
+  let res = new Resource(server.baseURI + "/open");
   do_check_true(res.uri instanceof Ci.nsIURI);
-  do_check_eq(res.uri.spec, "http://localhost:8080/open");
-  do_check_eq(res.spec, "http://localhost:8080/open");
+  do_check_eq(res.uri.spec, server.baseURI + "/open");
+  do_check_eq(res.spec, server.baseURI + "/open");
   do_check_eq(typeof res.headers, "object");
   do_check_eq(typeof res.authenticator, "object");
   // Initially res.data is null since we haven't performed a GET or
   // PUT/POST request yet.
   do_check_eq(res.data, null);
 
   _("GET a non-password-protected resource");
   content = res.get();
@@ -225,52 +225,52 @@ function run_test() {
   }
   do_check_true(didThrow);
   do_check_eq(debugMessages.length, 1);
   do_check_eq(debugMessages[0],
               "Parse fail: Response body starts: \"\"This path exists\"\".");
   logger.debug = dbg;
 
   _("Test that the BasicAuthenticator doesn't screw up header case.");
-  let res1 = new Resource("http://localhost:8080/foo");
+  let res1 = new Resource(server.baseURI + "/foo");
   res1.setHeader("Authorization", "Basic foobar");
   do_check_eq(res1.headers["authorization"], "Basic foobar");
 
   _("GET a password protected resource (test that it'll fail w/o pass, no throw)");
-  let res2 = new Resource("http://localhost:8080/protected");
+  let res2 = new Resource(server.baseURI + "/protected");
   content = res2.get();
   do_check_eq(content, "This path exists and is protected - failed");
   do_check_eq(content.status, 401);
   do_check_false(content.success);
 
   _("GET a password protected resource");
-  let res3 = new Resource("http://localhost:8080/protected");
+  let res3 = new Resource(server.baseURI + "/protected");
   let identity = new IdentityManager();
   let auth = identity.getBasicResourceAuthenticator("guest", "guest");
   res3.authenticator = auth;
   do_check_eq(res3.authenticator, auth);
   content = res3.get();
   do_check_eq(content, "This path exists and is protected");
   do_check_eq(content.status, 200);
   do_check_true(content.success);
 
   _("GET a non-existent resource (test that it'll fail, but not throw)");
-  let res4 = new Resource("http://localhost:8080/404");
+  let res4 = new Resource(server.baseURI + "/404");
   content = res4.get();
   do_check_eq(content, "File not found");
   do_check_eq(content.status, 404);
   do_check_false(content.success);
 
   // Check some headers of the 404 response
   do_check_eq(content.headers.connection, "close");
   do_check_eq(content.headers.server, "httpd.js");
   do_check_eq(content.headers["content-length"], 14);
 
   _("PUT to a resource (string)");
-  let res5 = new Resource("http://localhost:8080/upload");
+  let res5 = new Resource(server.baseURI + "/upload");
   content = res5.put(JSON.stringify(sample_data));
   do_check_eq(content, "Valid data upload via PUT");
   do_check_eq(content.status, 200);
   do_check_eq(res5.data, content);
 
   _("PUT to a resource (object)");
   content = res5.put(sample_data);
   do_check_eq(content, "Valid data upload via PUT");
@@ -313,38 +313,38 @@ function run_test() {
   _("POST without data arg (uses resource.data) (object)");
   res5.data = sample_data;
   content = res5.post();
   do_check_eq(content, "Valid data upload via POST");
   do_check_eq(content.status, 200);
   do_check_eq(res5.data, content);
 
   _("DELETE a resource");
-  let res6 = new Resource("http://localhost:8080/delete");
+  let res6 = new Resource(server.baseURI + "/delete");
   content = res6.delete();
   do_check_eq(content, "This resource has been deleted")
   do_check_eq(content.status, 200);
 
   _("JSON conversion of response body");
-  let res7 = new Resource("http://localhost:8080/json");
+  let res7 = new Resource(server.baseURI + "/json");
   content = res7.get();
   do_check_eq(content, JSON.stringify(sample_data));
   do_check_eq(content.status, 200);
   do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
 
   _("X-Weave-Timestamp header updates AsyncResource.serverTime");
   // Before having received any response containing the
   // X-Weave-Timestamp header, AsyncResource.serverTime is null.
   do_check_eq(AsyncResource.serverTime, null);
-  let res8 = new Resource("http://localhost:8080/timestamp");
+  let res8 = new Resource(server.baseURI + "/timestamp");
   content = res8.get();
   do_check_eq(AsyncResource.serverTime, TIMESTAMP);
 
   _("GET: no special request headers");
-  let res9 = new Resource("http://localhost:8080/headers");
+  let res9 = new Resource(server.baseURI + "/headers");
   content = res9.get();
   do_check_eq(content, '{}');
 
   _("PUT: Content-Type defaults to text/plain");
   content = res9.put('data');
   do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
 
   _("POST: Content-Type defaults to text/plain");
@@ -382,34 +382,34 @@ function run_test() {
 
   _("X-Weave-Backoff header notifies observer");
   let backoffInterval;
   function onBackoff(subject, data) {
     backoffInterval = subject;
   }
   Observers.add("weave:service:backoff:interval", onBackoff);
 
-  let res10 = new Resource("http://localhost:8080/backoff");
+  let res10 = new Resource(server.baseURI + "/backoff");
   content = res10.get();
   do_check_eq(backoffInterval, 600);
 
 
   _("X-Weave-Quota-Remaining header notifies observer on successful requests.");
   let quotaValue;
   function onQuota(subject, data) {
     quotaValue = subject;
   }
   Observers.add("weave:service:quota:remaining", onQuota);
 
-  res10 = new Resource("http://localhost:8080/quota-error");
+  res10 = new Resource(server.baseURI + "/quota-error");
   content = res10.get();
   do_check_eq(content.status, 400);
   do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
 
-  res10 = new Resource("http://localhost:8080/quota-notice");
+  res10 = new Resource(server.baseURI + "/quota-notice");
   content = res10.get();
   do_check_eq(content.status, 200);
   do_check_eq(quotaValue, 1048576);
 
 
   _("Error handling in _request() preserves exception information");
   let error;
   let res11 = new Resource("http://localhost:12345/does/not/exist");
@@ -418,17 +418,17 @@ function run_test() {
   } catch(ex) {
     error = ex;
   }
   do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
   do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
   do_check_eq(typeof error.stack, "string");
 
   _("Checking handling of errors in onProgress.");
-  let res18 = new Resource("http://localhost:8080/json");
+  let res18 = new Resource(server.baseURI + "/json");
   let onProgress = function(rec) {
     // Provoke an XPC exception without a Javascript wrapper.
     Services.io.newURI("::::::::", null, null);
   };
   res18._onProgress = onProgress;
   let oldWarn = res18._log.warn;
   let warnings = [];
   res18._log.warn = function(msg) { warnings.push(msg) };
@@ -439,20 +439,20 @@ function run_test() {
     error = ex;
   }
 
   // It throws and logs.
   do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
   do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
   do_check_eq(warnings.pop(),
               "Got exception calling onProgress handler during fetch of " +
-              "http://localhost:8080/json");
+              server.baseURI + "/json");
 
   // And this is what happens if JS throws an exception.
-  res18 = new Resource("http://localhost:8080/json");
+  res18 = new Resource(server.baseURI + "/json");
   onProgress = function(rec) {
     throw "BOO!";
   };
   res18._onProgress = onProgress;
   oldWarn = res18._log.warn;
   warnings = [];
   res18._log.warn = function(msg) { warnings.push(msg) };
   error = undefined;
@@ -462,21 +462,21 @@ function run_test() {
     error = ex;
   }
 
   // It throws and logs.
   do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
   do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
   do_check_eq(warnings.pop(),
               "Got exception calling onProgress handler during fetch of " +
-              "http://localhost:8080/json");
+              server.baseURI + "/json");
 
 
   _("Ensure channel timeouts are thrown appropriately.");
-  let res19 = new Resource("http://localhost:8080/json");
+  let res19 = new Resource(server.baseURI + "/json");
   res19.ABORT_TIMEOUT = 0;
   error = undefined;
   try {
     content = res19.get();
   } catch (ex) {
     error = ex;
   }
   do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
--- a/services/sync/tests/unit/test_resource_async.js
+++ b/services/sync/tests/unit/test_resource_async.js
@@ -2,19 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-common/observers.js");
 Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
 
-const RES_UPLOAD_URL = "http://localhost:8080/upload";
-const RES_HEADERS_URL = "http://localhost:8080/headers";
-
 let logger;
 
 let fetched = false;
 function server_open(metadata, response) {
   let body;
   if (metadata.method == "GET") {
     fetched = true;
     body = "This path exists";
@@ -168,19 +165,19 @@ function run_test() {
 add_test(function test_proxy_auth_redirect() {
   _("Ensure that a proxy auth redirect (which switches out our channel) " +
     "doesn't break AsyncResource.");
   let server = httpd_setup({
     "/open": server_open,
     "/pac2": server_pac
   });
 
-  PACSystemSettings.PACURI = "http://localhost:8080/pac2";
+  PACSystemSettings.PACURI = server.baseURI + "/pac2";
   installFakePAC();
-  let res = new AsyncResource("http://localhost:8080/open");
+  let res = new AsyncResource(server.baseURI + "/open");
   res.get(function (error, result) {
     do_check_true(!error);
     do_check_true(pacFetched);
     do_check_true(fetched);
     do_check_eq("This path exists", result);
     pacFetched = fetched = false;
     uninstallFakePAC();
     server.stop(run_next_test);
@@ -194,28 +191,29 @@ add_test(function test_new_channel() {
   function resourceHandler(metadata, response) {
     resourceRequested = true;
 
     let body = "Test";
     response.setHeader("Content-Type", "text/plain");
     response.bodyOutputStream.write(body, body.length);
   }
 
+  let locationURL;
   function redirectHandler(metadata, response) {
     let body = "Redirecting";
     response.setStatusLine(metadata.httpVersion, 307, "TEMPORARY REDIRECT");
-    response.setHeader("Location", "http://localhost:8080/resource");
+    response.setHeader("Location", locationURL);
     response.bodyOutputStream.write(body, body.length);
   }
 
   let server = httpd_setup({"/resource": resourceHandler,
-                            "/redirect": redirectHandler},
-                            8080);
+                            "/redirect": redirectHandler});
+  locationURL = server.baseURI + "/resource";
 
-  let request = new AsyncResource("http://localhost:8080/redirect");
+  let request = new AsyncResource(server.baseURI + "/redirect");
   request.get(function onRequest(error, content) {
     do_check_null(error);
     do_check_true(resourceRequested);
     do_check_eq(200, content.status);
     do_check_true("content-type" in content.headers);
     do_check_eq("text/plain", content.headers["content-type"]);
 
     server.stop(run_next_test);
@@ -241,32 +239,33 @@ add_test(function setup() {
     "/quota-error": server_quota_error
   });
 
   run_next_test();
 });
 
 add_test(function test_members() {
   _("Resource object members");
-  let res = new AsyncResource("http://localhost:8080/open");
+  let uri = server.baseURI + "/open";
+  let res = new AsyncResource(uri);
   do_check_true(res.uri instanceof Ci.nsIURI);
-  do_check_eq(res.uri.spec, "http://localhost:8080/open");
-  do_check_eq(res.spec, "http://localhost:8080/open");
+  do_check_eq(res.uri.spec, uri);
+  do_check_eq(res.spec, uri);
   do_check_eq(typeof res.headers, "object");
   do_check_eq(typeof res.authenticator, "object");
   // Initially res.data is null since we haven't performed a GET or
   // PUT/POST request yet.
   do_check_eq(res.data, null);
 
   run_next_test();
 });
 
 add_test(function test_get() {
   _("GET a non-password-protected resource");
-  let res = new AsyncResource("http://localhost:8080/open");
+  let res = new AsyncResource(server.baseURI + "/open");
   res.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "This path exists");
     do_check_eq(content.status, 200);
     do_check_true(content.success);
     // res.data has been updated with the result from the request
     do_check_eq(res.data, content);
 
@@ -294,55 +293,55 @@ add_test(function test_get() {
     logger.debug = dbg;
 
     run_next_test();
   });
 });
 
 add_test(function test_basicauth() {
   _("Test that the BasicAuthenticator doesn't screw up header case.");
-  let res1 = new AsyncResource("http://localhost:8080/foo");
+  let res1 = new AsyncResource(server.baseURI + "/foo");
   res1.setHeader("Authorization", "Basic foobar");
   do_check_eq(res1._headers["authorization"], "Basic foobar");
   do_check_eq(res1.headers["authorization"], "Basic foobar");
 
   run_next_test();
 });
 
 add_test(function test_get_protected_fail() {
   _("GET a password protected resource (test that it'll fail w/o pass, no throw)");
-  let res2 = new AsyncResource("http://localhost:8080/protected");
+  let res2 = new AsyncResource(server.baseURI + "/protected");
   res2.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "This path exists and is protected - failed");
     do_check_eq(content.status, 401);
     do_check_false(content.success);
     run_next_test();
   });
 });
 
 add_test(function test_get_protected_success() {
   _("GET a password protected resource");
   let identity = new IdentityManager();
   let auth = identity.getBasicResourceAuthenticator("guest", "guest");
-  let res3 = new AsyncResource("http://localhost:8080/protected");
+  let res3 = new AsyncResource(server.baseURI + "/protected");
   res3.authenticator = auth;
   do_check_eq(res3.authenticator, auth);
   res3.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "This path exists and is protected");
     do_check_eq(content.status, 200);
     do_check_true(content.success);
     run_next_test();
   });
 });
 
 add_test(function test_get_404() {
   _("GET a non-existent resource (test that it'll fail, but not throw)");
-  let res4 = new AsyncResource("http://localhost:8080/404");
+  let res4 = new AsyncResource(server.baseURI + "/404");
   res4.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "File not found");
     do_check_eq(content.status, 404);
     do_check_false(content.success);
 
     // Check some headers of the 404 response
     do_check_eq(content.headers.connection, "close");
@@ -350,271 +349,271 @@ add_test(function test_get_404() {
     do_check_eq(content.headers["content-length"], 14);
 
     run_next_test();
   });
 });
 
 add_test(function test_put_string() {
   _("PUT to a resource (string)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.put(JSON.stringify(sample_data), function(error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via PUT");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_put_object() {
   _("PUT to a resource (object)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.put(sample_data, function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via PUT");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_put_data_string() {
   _("PUT without data arg (uses resource.data) (string)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.data = JSON.stringify(sample_data);
   res_upload.put(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via PUT");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_put_data_object() {
   _("PUT without data arg (uses resource.data) (object)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.data = sample_data;
   res_upload.put(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via PUT");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_post_string() {
   _("POST to a resource (string)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.post(JSON.stringify(sample_data), function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via POST");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_post_object() {
   _("POST to a resource (object)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.post(sample_data, function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via POST");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_post_data_string() {
   _("POST without data arg (uses resource.data) (string)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.data = JSON.stringify(sample_data);
   res_upload.post(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via POST");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_post_data_object() {
   _("POST without data arg (uses resource.data) (object)");
-  let res_upload = new AsyncResource(RES_UPLOAD_URL);
+  let res_upload = new AsyncResource(server.baseURI + "/upload");
   res_upload.data = sample_data;
   res_upload.post(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "Valid data upload via POST");
     do_check_eq(content.status, 200);
     do_check_eq(res_upload.data, content);
     run_next_test();
   });
 });
 
 add_test(function test_delete() {
   _("DELETE a resource");
-  let res6 = new AsyncResource("http://localhost:8080/delete");
+  let res6 = new AsyncResource(server.baseURI + "/delete");
   res6.delete(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "This resource has been deleted");
     do_check_eq(content.status, 200);
     run_next_test();
   });
 });
 
 add_test(function test_json_body() {
   _("JSON conversion of response body");
-  let res7 = new AsyncResource("http://localhost:8080/json");
+  let res7 = new AsyncResource(server.baseURI + "/json");
   res7.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify(sample_data));
     do_check_eq(content.status, 200);
     do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
     run_next_test();
   });
 });
 
 add_test(function test_weave_timestamp() {
   _("X-Weave-Timestamp header updates AsyncResource.serverTime");
   // Before having received any response containing the
   // X-Weave-Timestamp header, AsyncResource.serverTime is null.
   do_check_eq(AsyncResource.serverTime, null);
-  let res8 = new AsyncResource("http://localhost:8080/timestamp");
+  let res8 = new AsyncResource(server.baseURI + "/timestamp");
   res8.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(AsyncResource.serverTime, TIMESTAMP);
     run_next_test();
   });
 });
 
 add_test(function test_get_no_headers() {
   _("GET: no special request headers");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, '{}');
     run_next_test();
   });
 });
 
 add_test(function test_put_default_content_type() {
   _("PUT: Content-Type defaults to text/plain");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.put('data', function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
     run_next_test();
   });
 });
 
 add_test(function test_post_default_content_type() {
   _("POST: Content-Type defaults to text/plain");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.post('data', function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
     run_next_test();
   });
 });
 
 add_test(function test_setHeader() {
   _("setHeader(): setting simple header");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.setHeader('X-What-Is-Weave', 'awesome');
   do_check_eq(res_headers.headers['x-what-is-weave'], 'awesome');
   res_headers.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
     run_next_test();
   });
 });
 
 add_test(function test_setHeader_overwrite() {
   _("setHeader(): setting multiple headers, overwriting existing header");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.setHeader('X-WHAT-is-Weave', 'more awesomer');
   res_headers.setHeader('X-Another-Header', 'hello world');
   do_check_eq(res_headers.headers['x-what-is-weave'], 'more awesomer');
   do_check_eq(res_headers.headers['x-another-header'], 'hello world');
   res_headers.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
                                          "x-what-is-weave": "more awesomer"}));
 
     run_next_test();
   });
 });
 
 add_test(function test_headers_object() {
   _("Setting headers object");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.headers = {};
   res_headers.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, "{}");
     run_next_test();
   });
 });
 
 add_test(function test_put_override_content_type() {
   _("PUT: override default Content-Type");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.setHeader('Content-Type', 'application/foobar');
   do_check_eq(res_headers.headers['content-type'], 'application/foobar');
   res_headers.put('data', function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
     run_next_test();
   });
 });
 
 add_test(function test_post_override_content_type() {
   _("POST: override default Content-Type");
-  let res_headers = new AsyncResource(RES_HEADERS_URL);
+  let res_headers = new AsyncResource(server.baseURI + "/headers");
   res_headers.setHeader('Content-Type', 'application/foobar');
   res_headers.post('data', function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
     run_next_test();
   });
 });
 
 add_test(function test_weave_backoff() {
   _("X-Weave-Backoff header notifies observer");
   let backoffInterval;
   function onBackoff(subject, data) {
     backoffInterval = subject;
   }
   Observers.add("weave:service:backoff:interval", onBackoff);
 
-  let res10 = new AsyncResource("http://localhost:8080/backoff");
+  let res10 = new AsyncResource(server.baseURI + "/backoff");
   res10.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(backoffInterval, 600);
     run_next_test();
   });
 });
 
 add_test(function test_quota_error() {
   _("X-Weave-Quota-Remaining header notifies observer on successful requests.");
-  let res10 = new AsyncResource("http://localhost:8080/quota-error");
+  let res10 = new AsyncResource(server.baseURI + "/quota-error");
   res10.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content.status, 400);
     do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
     run_next_test();
   });
 });
 
 add_test(function test_quota_notice() {
-  let res10 = new AsyncResource("http://localhost:8080/quota-notice");
+  let res10 = new AsyncResource(server.baseURI + "/quota-notice");
   res10.get(function (error, content) {
     do_check_eq(error, null);
     do_check_eq(content.status, 200);
     do_check_eq(quotaValue, 1048576);
     run_next_test();
   });
 });
 
@@ -626,60 +625,60 @@ add_test(function test_preserve_exceptio
     do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
     do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
     run_next_test();
   });
 });
 
 add_test(function test_xpc_exception_handling() {
   _("Exception handling inside fetches.");
-  let res14 = new AsyncResource("http://localhost:8080/json");
+  let res14 = new AsyncResource(server.baseURI + "/json");
   res14._onProgress = function(rec) {
     // Provoke an XPC exception without a Javascript wrapper.
     Services.io.newURI("::::::::", null, null);
   };
   let warnings = [];
   res14._log.warn = function(msg) { warnings.push(msg); };
 
   res14.get(function (error, content) {
     do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
     do_check_eq(error.message, "NS_ERROR_MALFORMED_URI");
     do_check_eq(content, null);
     do_check_eq(warnings.pop(),
                 "Got exception calling onProgress handler during fetch of " +
-                "http://localhost:8080/json");
+                server.baseURI + "/json");
 
     run_next_test();
   });
 });
 
 add_test(function test_js_exception_handling() {
   _("JS exception handling inside fetches.");
-  let res15 = new AsyncResource("http://localhost:8080/json");
+  let res15 = new AsyncResource(server.baseURI + "/json");
   res15._onProgress = function(rec) {
     throw "BOO!";
   };
   let warnings = [];
   res15._log.warn = function(msg) { warnings.push(msg); };
 
   res15.get(function (error, content) {
     do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
     do_check_eq(error.message, "NS_ERROR_XPC_JS_THREW_STRING");
     do_check_eq(content, null);
     do_check_eq(warnings.pop(),
                 "Got exception calling onProgress handler during fetch of " +
-                "http://localhost:8080/json");
+                server.baseURI + "/json");
 
     run_next_test();
   });
 });
 
 add_test(function test_timeout() {
   _("Ensure channel timeouts are thrown appropriately.");
-  let res19 = new AsyncResource("http://localhost:8080/json");
+  let res19 = new AsyncResource(server.baseURI + "/json");
   res19.ABORT_TIMEOUT = 0;
   res19.get(function (error, content) {
     do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
     run_next_test();
   });
 });
 
 add_test(function test_uri_construction() {
@@ -705,20 +704,20 @@ add_test(function test_not_sending_cooki
   function handler(metadata, response) {
     let body = "COOKIE!";
     response.setStatusLine(metadata.httpVersion, 200, "OK");
     response.bodyOutputStream.write(body, body.length);
     do_check_false(metadata.hasHeader("Cookie"));
   }
   let cookieSer = Cc["@mozilla.org/cookieService;1"]
                     .getService(Ci.nsICookieService);
-  let uri = CommonUtils.makeURI("http://localhost:8080");
+  let uri = CommonUtils.makeURI(server.baseURI);
   cookieSer.setCookieString(uri, null, "test=test; path=/;", null);
 
-  let res = new AsyncResource("http://localhost:8080/test");
+  let res = new AsyncResource(server.baseURI + "/test");
   res.get(function (error) {
     do_check_null(error);
     do_check_true(this.response.success);
     do_check_eq("COOKIE!", this.response.body);
     server.stop(run_next_test);
   });
 });
 
--- a/services/sync/tests/unit/test_resource_ua.js
+++ b/services/sync/tests/unit/test_resource_ua.js
@@ -2,18 +2,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
-const TEST_GET_URL = "http://localhost:8080/1.1/johndoe/storage/meta/global";
-
 // Tracking info/collections.
 let collectionsHelper = track_collections_helper();
 let collections = collectionsHelper.collections;
 
 let meta_global;
 let server;
 
 let expectedUA;
@@ -21,25 +19,27 @@ let ua;
 function uaHandler(f) {
   return function(request, response) {
     ua = request.getHeader("User-Agent");
     return f(request, response);
   };
 }
 
 function run_test() {
+  Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
   meta_global = new ServerWBO('global');
   server = httpd_setup({
     "/1.1/johndoe/info/collections": uaHandler(collectionsHelper.handler),
     "/1.1/johndoe/storage/meta/global": uaHandler(meta_global.handler()),
   });
 
   setBasicCredentials("johndoe", "ilovejane");
-  Service.serverURL  = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
+  _("Server URL: " + server.baseURI);
 
   expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
                " FxSync/" + WEAVE_VERSION + "." +
                Services.appinfo.appBuildID;
 
   run_next_test();
 }
 
@@ -49,41 +49,41 @@ add_test(function test_fetchInfo() {
   _("User-Agent: " + ua);
   do_check_eq(ua, expectedUA + ".desktop");
   ua = "";
   run_next_test();
 });
 
 add_test(function test_desktop_post() {
   _("Testing direct Resource POST.");
-  let r = new AsyncResource(TEST_GET_URL);
+  let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
   r.post("foo=bar", function (error, content) {
     _("User-Agent: " + ua);
     do_check_eq(ua, expectedUA + ".desktop");
     ua = "";
     run_next_test();
   });
 });
 
 add_test(function test_desktop_get() {
   _("Testing async.");
   Svc.Prefs.set("client.type", "desktop");
-  let r = new AsyncResource(TEST_GET_URL);
+  let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
   r.get(function(error, content) {
     _("User-Agent: " + ua);
     do_check_eq(ua, expectedUA + ".desktop");
     ua = "";
     run_next_test();
   });
 });
 
 add_test(function test_mobile_get() {
   _("Testing mobile.");
   Svc.Prefs.set("client.type", "mobile");
-  let r = new AsyncResource(TEST_GET_URL);
+  let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
   r.get(function (error, content) {
     _("User-Agent: " + ua);
     do_check_eq(ua, expectedUA + ".mobile");
     ua = "";
     run_next_test();
   });
 });
 
--- a/services/sync/tests/unit/test_score_triggers.js
+++ b/services/sync/tests/unit/test_score_triggers.js
@@ -38,18 +38,18 @@ function sync_httpd_setup() {
 
   let cl = new ServerCollection();
   handlers["/1.1/johndoe/storage/clients"] =
     upd("clients", cl.handler());
 
   return httpd_setup(handlers);
 }
 
-function setUp() {
-  new SyncTestingInfrastructure("johndoe", "ilovejane", "sekrit");
+function setUp(server) {
+  new SyncTestingInfrastructure(server, "johndoe", "ilovejane", "sekrit");
 }
 
 function run_test() {
   initTestLogging("Trace");
 
   Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
 
   run_next_test();
@@ -75,17 +75,17 @@ add_test(function test_tracker_score_upd
     Svc.Obs.remove("weave:engine:score:updated", onScoreUpdated);
     tracker.resetScore();
     run_next_test();
   }
 });
 
 add_test(function test_sync_triggered() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   Service.login();
 
   Service.scheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
     _("Sync completed!");
     server.stop(run_next_test);
@@ -98,17 +98,17 @@ add_test(function test_sync_triggered() 
 add_test(function test_clients_engine_sync_triggered() {
   _("Ensure that client engine score changes trigger a sync.");
 
   // The clients engine is not registered like other engines. Therefore,
   // it needs special treatment throughout the code. Here, we verify the
   // global score tracker gives it that treatment. See bug 676042 for more.
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
   Service.login();
 
   const TOPIC = "weave:service:sync:finish";
   Svc.Obs.add(TOPIC, function onSyncFinish() {
     Svc.Obs.remove(TOPIC, onSyncFinish);
     _("Sync due to clients engine change completed.");
     server.stop(run_next_test);
   });
@@ -116,17 +116,17 @@ add_test(function test_clients_engine_sy
   Service.scheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
   do_check_eq(Status.login, LOGIN_SUCCEEDED);
   Service.clientsEngine._tracker.score += SCORE_INCREMENT_XLARGE;
 });
 
 add_test(function test_incorrect_credentials_sync_not_triggered() {
   _("Ensure that score changes don't trigger a sync if Status.login != LOGIN_SUCCEEDED.");
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Ensure we don't actually try to sync.
   function onSyncStart() {
     do_throw("Should not get here!");
   }
   Svc.Obs.add("weave:service:sync:start", onSyncStart);
 
   // First wait >100ms (nsITimers can take up to that much time to fire, so
--- a/services/sync/tests/unit/test_service_changePassword.js
+++ b/services/sync/tests/unit/test_service_changePassword.js
@@ -24,31 +24,31 @@ add_test(function test_change_password()
     return function(request, response) {
       requestBody = readBytesFromInputStream(request.bodyInputStream);
       response.setStatusLine(request.httpVersion, statusCode, status);
       response.bodyOutputStream.write(body, body.length);
     };
   }
 
   try {
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.baseURI = "http://localhost:9999/";
     setBasicCredentials("johndoe", "ilovejane");
 
     _("changePassword() returns false for a network error, the password won't change.");
     let res = Service.changePassword("ILoveJane83");
     do_check_false(res);
     do_check_eq(Service.identity.basicPassword, "ilovejane");
 
     _("Let's fire up the server and actually change the password.");
     server = httpd_setup({
       "/user/1.0/johndoe/password": send(200, "OK", ""),
       "/user/1.0/janedoe/password": send(401, "Unauthorized", "Forbidden!")
     });
 
+    Service.serverURL = server.baseURI;
     res = Service.changePassword("ILoveJane83");
     do_check_true(res);
     do_check_eq(Service.identity.basicPassword, "ILoveJane83");
     do_check_eq(requestBody, "ILoveJane83");
 
     _("Make sure the password has been persisted in the login manager.");
     let logins = Services.logins.findLogins({}, PWDMGR_HOST, null,
                                             PWDMGR_PASSWORD_REALM);
--- a/services/sync/tests/unit/test_service_checkAccount.js
+++ b/services/sync/tests/unit/test_service_checkAccount.js
@@ -11,17 +11,17 @@ function run_test() {
     "/user/1.0/johndoe": httpd_handler(200, "OK", "1"),
     "/user/1.0/janedoe": httpd_handler(200, "OK", "0"),
     // john@doe.com
     "/user/1.0/7wohs32cngzuqt466q3ge7indszva4of": httpd_handler(200, "OK", "0"),
     // jane@doe.com
     "/user/1.0/vuuf3eqgloxpxmzph27f5a6ve7gzlrms": httpd_handler(200, "OK", "1")
   });
   try {
-    Service.serverURL = TEST_SERVER_URL;
+    Service.serverURL = server.baseURI;
 
     _("A 404 will be recorded as 'generic-server-error'");
     do_check_eq(Service.checkAccount("jimdoe"), "generic-server-error");
 
     _("Account that's available.");
     do_check_eq(Service.checkAccount("john@doe.com"), "available");
 
     _("Account that's not available.");
--- a/services/sync/tests/unit/test_service_cluster.js
+++ b/services/sync/tests/unit/test_service_cluster.js
@@ -14,32 +14,34 @@ function do_check_throws(func) {
   }
   do_check_true(raised);
 }
 
 add_test(function test_findCluster() {
   _("Test Service._findCluster()");
   let server;
   try {
-    Service.serverURL = TEST_SERVER_URL;
-    Service.identity.account = "johndoe";
-
     _("_findCluster() throws on network errors (e.g. connection refused).");
     do_check_throws(function() {
+      Service.serverURL = "http://dummy:9000/";
+      Service.identify.account = "johndoe";
       Service._clusterManager._findCluster();
     });
 
     server = httpd_setup({
       "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://weave.user.node/"),
       "/user/1.0/jimdoe/node/weave": httpd_handler(200, "OK", "null"),
       "/user/1.0/janedoe/node/weave": httpd_handler(404, "Not Found", "Not Found"),
       "/user/1.0/juliadoe/node/weave": httpd_handler(400, "Bad Request", "Bad Request"),
       "/user/1.0/joedoe/node/weave": httpd_handler(500, "Server Error", "Server Error")
     });
 
+    Service.serverURL = server.baseURI;
+    Service.identity.account = "johndoe";
+
     _("_findCluster() returns the user's cluster node");
     let cluster = Service._clusterManager._findCluster();
     do_check_eq(cluster, "http://weave.user.node/");
 
     _("A 'null' response is converted to null.");
     Service.identity.account = "jimdoe";
     cluster = Service._clusterManager._findCluster();
     do_check_eq(cluster, null);
@@ -71,17 +73,17 @@ add_test(function test_findCluster() {
 
 add_test(function test_setCluster() {
   _("Test Service._setCluster()");
   let server = httpd_setup({
     "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://weave.user.node/"),
     "/user/1.0/jimdoe/node/weave": httpd_handler(200, "OK", "null")
   });
   try {
-    Service.serverURL = TEST_SERVER_URL;
+    Service.serverURL = server.baseURI;
     Service.identity.account = "johndoe";
 
     _("Check initial state.");
     do_check_eq(Service.clusterURL, "");
 
     _("Set the cluster URL.");
     do_check_true(Service._clusterManager.setCluster());
     do_check_eq(Service.clusterURL, "http://weave.user.node/");
--- a/services/sync/tests/unit/test_service_createAccount.js
+++ b/services/sync/tests/unit/test_service_createAccount.js
@@ -27,17 +27,17 @@ function run_test() {
     // john@doe.com
     "/user/1.0/7wohs32cngzuqt466q3ge7indszva4of": send(200, "OK", "0"),
     // jane@doe.com
     "/user/1.0/vuuf3eqgloxpxmzph27f5a6ve7gzlrms": send(400, "Bad Request", "2"),
     // jim@doe.com
     "/user/1.0/vz6fhecgw5t3sgx3a4cektoiokyczkqd": send(500, "Server Error", "Server Error")
   });
   try {
-    Service.serverURL = TEST_SERVER_URL;
+    Service.serverURL = server.baseURI;
 
     _("Create an account.");
     let res = Service.createAccount("john@doe.com", "mysecretpw",
                                     "challenge", "response");
     do_check_eq(res, null);
     let payload = JSON.parse(requestBody);
     do_check_eq(payload.password, "mysecretpw");
     do_check_eq(payload.email, "john@doe.com");
--- a/services/sync/tests/unit/test_service_detect_upgrade.js
+++ b/services/sync/tests/unit/test_service_detect_upgrade.js
@@ -61,18 +61,17 @@ add_test(function v4_upgrade() {
     delete Svc.Session;
     Svc.Session = {
       getBrowserState: function () JSON.stringify(myTabs)
     };
 
     Service.status.resetSync();
 
     _("Logging in.");
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI;
 
     Service.login("johndoe", "ilovejane", passphrase);
     do_check_true(Service.isLoggedIn);
     Service.verifyAndFetchSymmetricKeys();
     do_check_true(Service._remoteSetup());
 
     function test_out_of_date() {
       _("Old meta/global: " + JSON.stringify(meta_global));
@@ -97,18 +96,17 @@ add_test(function v4_upgrade() {
     _("Syncing after server has been upgraded and wiped.");
     Service.wipeServer();
     test_out_of_date();
 
     // Now's a great time to test what happens when keys get replaced.
     _("Syncing afresh...");
     Service.logout();
     Service.collectionKeys.clear();
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI;
     meta_global.payload = JSON.stringify({"syncID": "foooooooooooooobbbbbbbbbbbb",
                                           "storageVersion": STORAGE_VERSION});
     collections.meta = Date.now() / 1000;
     Service.recordManager.set(Service.metaURL, meta_global);
     Service.login("johndoe", "ilovejane", passphrase);
     do_check_true(Service.isLoggedIn);
     Service.sync();
     do_check_true(Service.isLoggedIn);
@@ -234,18 +232,18 @@ add_test(function v5_upgrade() {
     delete Svc.Session;
     Svc.Session = {
       getBrowserState: function () JSON.stringify(myTabs)
     };
 
     Service.status.resetSync();
 
     setBasicCredentials("johndoe", "ilovejane", passphrase);
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI + "/";
+    Service.clusterURL = server.baseURI + "/";
 
     // Test an upgrade where the contents of the server would cause us to error
     // -- keys decrypted with a different sync key, for example.
     _("Testing v4 -> v5 (or similar) upgrade.");
     function update_server_keys(syncKeyBundle, wboName, collWBO) {
       generateNewKeys(Service.collectionKeys);
       serverKeys = Service.collectionKeys.asWBO("crypto", wboName);
       serverKeys.encrypt(syncKeyBundle);
--- a/services/sync/tests/unit/test_service_getStorageInfo.js
+++ b/services/sync/tests/unit/test_service_getStorageInfo.js
@@ -7,30 +7,30 @@ Cu.import("resource://services-sync/serv
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 let collections = {steam:  65.11328,
                    petrol: 82.488281,
                    diesel: 2.25488281};
 
 function run_test() {
-  setBasicCredentials("johndoe", "ilovejane");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-
   Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
   Log4Moz.repository.getLogger("Sync.StorageRequest").level = Log4Moz.Level.Trace;
   initTestLogging();
 
+  setBasicCredentials("johndoe", "ilovejane");
+
   run_next_test();
 }
 
 add_test(function test_success() {
   let handler = httpd_handler(200, "OK", JSON.stringify(collections));
   let server = httpd_setup({"/1.1/johndoe/info/collections": handler});
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   let request = Service.getStorageInfo("collections", function (error, info) {
     do_check_eq(error, null);
     do_check_true(Utils.deepEquals(info, collections));
 
     // Ensure that the request is sent off with the right bits.
     do_check_true(basic_auth_matches(handler.request,
                                      Service.identity.username,
@@ -60,27 +60,31 @@ add_test(function test_network_error() {
     do_check_eq(info, null);
     run_next_test();
   });
 });
 
 add_test(function test_http_error() {
   let handler = httpd_handler(500, "Oh noez", "Something went wrong!");
   let server = httpd_setup({"/1.1/johndoe/info/collections": handler});
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   let request = Service.getStorageInfo(INFO_COLLECTIONS, function (error, info) {
     do_check_eq(error.status, 500);
     do_check_eq(info, null);
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_invalid_json() {
   let handler = httpd_handler(200, "OK", "Invalid JSON");
   let server = httpd_setup({"/1.1/johndoe/info/collections": handler});
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   let request = Service.getStorageInfo(INFO_COLLECTIONS, function (error, info) {
     do_check_eq(error.name, "SyntaxError");
     do_check_eq(error.message, "JSON.parse: unexpected character");
     do_check_eq(info, null);
     server.stop(run_next_test);
   });
 });
--- a/services/sync/tests/unit/test_service_login.js
+++ b/services/sync/tests/unit/test_service_login.js
@@ -38,38 +38,38 @@ add_test(function test_offline() {
     Services.io.offline = false;
   } finally {
     Svc.Prefs.resetBranch("");
     run_next_test();
   }
 });
 
 function setup() {
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-
   let janeHelper = track_collections_helper();
   let janeU      = janeHelper.with_updated_collection;
   let janeColls  = janeHelper.collections;
   let johnHelper = track_collections_helper();
   let johnU      = johnHelper.with_updated_collection;
   let johnColls  = johnHelper.collections;
 
-  return httpd_setup({
+  let server = httpd_setup({
     "/1.1/johndoe/info/collections": login_handling(johnHelper.handler),
     "/1.1/janedoe/info/collections": login_handling(janeHelper.handler),
 
     // We need these handlers because we test login, and login
     // is where keys are generated or fetched.
     // TODO: have Jane fetch her keys, not generate them...
     "/1.1/johndoe/storage/crypto/keys": johnU("crypto", new ServerWBO("keys").handler()),
     "/1.1/johndoe/storage/meta/global": johnU("meta",   new ServerWBO("global").handler()),
     "/1.1/janedoe/storage/crypto/keys": janeU("crypto", new ServerWBO("keys").handler()),
     "/1.1/janedoe/storage/meta/global": janeU("meta",   new ServerWBO("global").handler())
   });
+
+  Service.serverURL = server.baseURI;
+  return server;
 }
 
 add_test(function test_login_logout() {
   let server = setup();
 
   try {
     _("Force the initial state.");
     Service.status.service = STATUS_OK;
--- a/services/sync/tests/unit/test_service_passwordUTF8.js
+++ b/services/sync/tests/unit/test_service_passwordUTF8.js
@@ -62,17 +62,17 @@ function run_test() {
   let server = httpd_setup({
     "/1.1/johndoe/info/collections":    login_handling(collectionsHelper.handler),
     "/1.1/johndoe/storage/meta/global": upd("meta",   new ServerWBO("global").handler()),
     "/1.1/johndoe/storage/crypto/keys": upd("crypto", new ServerWBO("keys").handler()),
     "/user/1.0/johndoe/password":       change_password
   });
 
   setBasicCredentials("johndoe", JAPANESE, "irrelevant");
-  Service.serverURL = TEST_SERVER_URL;
+  Service.serverURL = server.baseURI;
 
   try {
     _("Try to log in with the password.");
     server_password = "foobar";
     do_check_false(Service.verifyLogin());
     do_check_eq(server_password, "foobar");
 
     _("Make the server password the low byte version of our password.");
--- a/services/sync/tests/unit/test_service_startOver.js
+++ b/services/sync/tests/unit/test_service_startOver.js
@@ -68,18 +68,18 @@ add_test(function test_resetLocalData() 
 add_test(function test_removeClientData() {
   let engine = Service.engineManager.get("bla");
 
   // No cluster URL = no removal.
   do_check_false(engine.removed);
   Service.startOver();
   do_check_false(engine.removed);
 
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = "https://localhost/";
+  Service.clusterURL = Service.serverURL;
 
   do_check_false(engine.removed);
   Service.startOver();
   do_check_true(engine.removed);
 
   run_next_test();
 });
 
--- a/services/sync/tests/unit/test_service_startup.js
+++ b/services/sync/tests/unit/test_service_startup.js
@@ -8,18 +8,16 @@ Cu.import("resource://testing-common/ser
 
 Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
 Cu.import("resource://services-sync/service.js");
 
 function run_test() {
   _("When imported, Service.onStartup is called");
   initTestLogging("Trace");
 
-  new SyncTestingInfrastructure();
-
   // Test fixtures
   Service.identity.username = "johndoe";
 
   Cu.import("resource://services-sync/service.js");
 
   _("Service is enabled.");
   do_check_eq(Service.enabled, true);
 
--- a/services/sync/tests/unit/test_service_sync_401.js
+++ b/services/sync/tests/unit/test_service_sync_401.js
@@ -33,17 +33,17 @@ function run_test() {
     "/1.1/johndoe/storage/meta/global": upd("meta",   new ServerWBO("global").handler()),
     "/1.1/johndoe/info/collections":    login_handling(collectionsHelper.handler)
   });
 
   const GLOBAL_SCORE = 42;
 
   try {
     _("Set up test fixtures.");
-    new SyncTestingInfrastructure("johndoe", "ilovejane", "foo");
+    new SyncTestingInfrastructure(server, "johndoe", "ilovejane", "foo");
     Service.scheduler.globalScore = GLOBAL_SCORE;
     // Avoid daily ping
     Svc.Prefs.set("lastPing", Math.floor(Date.now() / 1000));
 
     let threw = false;
     Svc.Obs.add("weave:service:sync:error", function (subject, data) {
       threw = true;
     });
--- a/services/sync/tests/unit/test_service_sync_remoteSetup.js
+++ b/services/sync/tests/unit/test_service_sync_remoteSetup.js
@@ -60,34 +60,32 @@ function run_test() {
     "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
     "/1.1/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
     "/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
     "/1.1/johndoe/info/collections": collectionsHelper.handler
   });
 
   try {
     _("Log in.");
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI;
 
     _("Checking Status.sync with no credentials.");
     Service.verifyAndFetchSymmetricKeys();
     do_check_eq(Service.status.sync, CREDENTIALS_CHANGED);
     do_check_eq(Service.status.login, LOGIN_FAILED_NO_PASSPHRASE);
 
     _("Log in with an old secret phrase, is upgraded to Sync Key.");
     Service.login("johndoe", "ilovejane", "my old secret phrase!!1!");
     _("End of login");
     do_check_true(Service.isLoggedIn);
     do_check_true(Utils.isPassphrase(Service.identity.syncKey));
     let syncKey = Service.identity.syncKey;
     Service.startOver();
 
-    Service.serverURL = TEST_SERVER_URL;
-    Service.clusterURL = TEST_CLUSTER_URL;
+    Service.serverURL = server.baseURI;
     Service.login("johndoe", "ilovejane", syncKey);
     do_check_true(Service.isLoggedIn);
 
     _("Checking that remoteSetup returns true when credentials have changed.");
     Service.recordManager.get(Service.metaURL).payload.syncID = "foobar";
     do_check_true(Service._remoteSetup());
 
     _("Do an initial sync.");
--- a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
+++ b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
@@ -61,18 +61,18 @@ function sync_httpd_setup(handlers) {
 
   let cl = new ServerCollection();
   handlers["/1.1/johndoe/storage/clients"] =
     upd("clients", cl.handler());
 
   return httpd_setup(handlers);
 }
 
-function setUp() {
-  new SyncTestingInfrastructure("johndoe", "ilovejane",
+function setUp(server) {
+  new SyncTestingInfrastructure(server, "johndoe", "ilovejane",
                                 "abcdeabcdeabcdeabcdeabcdea");
   // Ensure that the server has valid keys so that logging in will work and not
   // result in a server wipe, rendering many of these tests useless.
   generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.identity.syncKeyBundle);
   return serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
 }
@@ -90,17 +90,17 @@ function run_test() {
 
 add_test(function test_newAccount() {
   _("Test: New account does not disable locally enabled engines.");
   let engine = Service.engineManager.get("steam");
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Engine is enabled from the beginning.");
     Service._ignorePrefObserver = true;
     engine.enabled = true;
     Service._ignorePrefObserver = false;
 
     _("Sync.");
@@ -120,17 +120,17 @@ add_test(function test_enabledLocally() 
   let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Enable engine locally.");
     engine.enabled = true;
 
     _("Sync.");
     Service.sync();
 
@@ -156,17 +156,17 @@ add_test(function test_disabledLocally()
                       version: engine.version}}
   });
   let steamCollection = new ServerWBO("steam", PAYLOAD);
 
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": steamCollection.handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Disable engine locally.");
     Service._ignorePrefObserver = true;
     engine.enabled = true;
     Service._ignorePrefObserver = false;
     engine.enabled = false;
 
@@ -205,17 +205,17 @@ add_test(function test_disabledLocally_w
     response.setHeader("Retry-After", "23");
     response.bodyOutputStream.write(body, body.length);
   }
 
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": service_unavailable
   });
-  setUp();
+  setUp(server);
 
   _("Disable engine locally.");
   Service._ignorePrefObserver = true;
   engine.enabled = true;
   Service._ignorePrefObserver = false;
   engine.enabled = false;
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
@@ -243,17 +243,17 @@ add_test(function test_enabledRemotely()
   });
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global":
     upd("meta", metaWBO.handler()),
 
     "/1.1/johndoe/storage/steam":
     upd("steam", new ServerWBO("steam", {}).handler())
   });
-  setUp();
+  setUp(server);
 
   // We need to be very careful how we do this, so that we don't trigger a
   // fresh start!
   try {
     _("Upload some keys to avoid a fresh start.");
     let wbo = Service.collectionKeys.generateNewKeysWBO();
     wbo.encrypt(Service.identity.syncKeyBundle);
     do_check_eq(200, wbo.upload(Service.resource(Service.cryptoKeysURL)).status);
@@ -284,17 +284,17 @@ add_test(function test_disabledRemotelyT
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global":
     upd("meta", metaWBO.handler()),
 
     "/1.1/johndoe/storage/steam":
     upd("steam", new ServerWBO("steam", {}).handler())
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Enable engine locally.");
     Service._ignorePrefObserver = true;
     engine.enabled = true;
     Service._ignorePrefObserver = false;
 
     _("Sync.");
@@ -325,17 +325,17 @@ add_test(function test_disabledRemotely(
   let engine = Service.engineManager.get("steam");
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Enable engine locally.");
     Service._ignorePrefObserver = true;
     engine.enabled = true;
     Service._ignorePrefObserver = false;
 
     _("Sync.");
@@ -358,17 +358,17 @@ add_test(function test_dependentEnginesE
   let metaWBO = new ServerWBO("global", {syncID: Service.syncID,
                                          storageVersion: STORAGE_VERSION,
                                          engines: {}});
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global": metaWBO.handler(),
     "/1.1/johndoe/storage/steam": new ServerWBO("steam", {}).handler(),
     "/1.1/johndoe/storage/stirling": new ServerWBO("stirling", {}).handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Enable engine locally. Doing it on one is enough.");
     steamEngine.enabled = true;
 
     _("Sync.");
     Service.sync();
 
@@ -402,17 +402,17 @@ add_test(function test_dependentEnginesD
   let steamCollection = new ServerWBO("steam", PAYLOAD);
   let stirlingCollection = new ServerWBO("stirling", PAYLOAD);
 
   let server = sync_httpd_setup({
     "/1.1/johndoe/storage/meta/global":     metaWBO.handler(),
     "/1.1/johndoe/storage/steam":           steamCollection.handler(),
     "/1.1/johndoe/storage/stirling":        stirlingCollection.handler()
   });
-  setUp();
+  setUp(server);
 
   try {
     _("Disable engines locally. Doing it on one is enough.");
     Service._ignorePrefObserver = true;
     steamEngine.enabled = true;
     do_check_true(stirlingEngine.enabled);
     Service._ignorePrefObserver = false;
     steamEngine.enabled = false;
--- a/services/sync/tests/unit/test_service_verifyLogin.js
+++ b/services/sync/tests/unit/test_service_verifyLogin.js
@@ -32,27 +32,35 @@ function run_test() {
 
   // This test expects a clean slate -- no saved passphrase.
   Services.logins.removeAllLogins();
   let johnHelper = track_collections_helper();
   let johnU      = johnHelper.with_updated_collection;
   let johnColls  = johnHelper.collections;
 
   do_test_pending();
-  let server = httpd_setup({
+
+  let server;
+  function weaveHandler (request, response) {
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    let body = server.baseURI + "/api/";
+    response.bodyOutputStream.write(body, body.length);
+  }
+
+  server = httpd_setup({
     "/api/1.1/johndoe/info/collections": login_handling(johnHelper.handler),
     "/api/1.1/janedoe/info/collections": service_unavailable,
 
     "/api/1.1/johndoe/storage/crypto/keys": johnU("crypto", new ServerWBO("keys").handler()),
     "/api/1.1/johndoe/storage/meta/global": johnU("meta",   new ServerWBO("global").handler()),
-    "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://localhost:8080/api/")
+    "/user/1.0/johndoe/node/weave": weaveHandler,
   });
 
   try {
-    Service.serverURL = TEST_SERVER_URL;
+    Service.serverURL = server.baseURI;
 
     _("Force the initial state.");
     Service.status.service = STATUS_OK;
     do_check_eq(Service.status.service, STATUS_OK);
 
     _("Credentials won't check out because we're not configured yet.");
     Service.status.resetSync();
     do_check_false(Service.verifyLogin());
@@ -62,17 +70,17 @@ function run_test() {
     _("Try again with username and password set.");
     Service.status.resetSync();
     setBasicCredentials("johndoe", "ilovejane", null);
     do_check_false(Service.verifyLogin());
     do_check_eq(Service.status.service, CLIENT_NOT_CONFIGURED);
     do_check_eq(Service.status.login, LOGIN_FAILED_NO_PASSPHRASE);
 
     _("verifyLogin() has found out the user's cluster URL, though.");
-    do_check_eq(Service.clusterURL, "http://localhost:8080/api/");
+    do_check_eq(Service.clusterURL, server.baseURI + "/api/");
 
     _("Success if passphrase is set.");
     Service.status.resetSync();
     Service.identity.syncKey = "foo";
     do_check_true(Service.verifyLogin());
     do_check_eq(Service.status.service, STATUS_OK);
     do_check_eq(Service.status.login, LOGIN_SUCCEEDED);
 
--- a/services/sync/tests/unit/test_service_wipeClient.js
+++ b/services/sync/tests/unit/test_service_wipeClient.js
@@ -77,17 +77,17 @@ add_test(function test_startOver_clears_
 
   run_next_test();
 });
 
 add_test(function test_credentials_preserved() {
   _("Ensure that credentials are preserved if client is wiped.");
 
   // Required for wipeClient().
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.clusterURL = "http://dummy:9000/";
   Service.identity.account = "testaccount";
   Service.identity.basicPassword = "testpassword";
   let key = Utils.generatePassphrase();
   Service.identity.syncKey = key;
   Service.identity.persistCredentials();
 
   // Simulate passwords engine wipe without all the overhead. To do this
   // properly would require extra test infrastructure.
--- a/services/sync/tests/unit/test_service_wipeServer.js
+++ b/services/sync/tests/unit/test_service_wipeServer.js
@@ -23,21 +23,21 @@ FakeCollection.prototype = {
       }
       response.setHeader("X-Weave-Timestamp", timestamp);
       response.setStatusLine(request.httpVersion, 200, "OK");
       response.bodyOutputStream.write(body, body.length);
     };
   }
 };
 
-function setUpTestFixtures() {
+function setUpTestFixtures(server) {
   let cryptoService = new FakeCryptoService();
 
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = server.baseURI + "/";
 
   setBasicCredentials("johndoe", null, "aabcdeabcdeabcdeabcdeabcde");
 }
 
 
 function run_test() {
   initTestLogging("Trace");
   run_next_test();
@@ -51,18 +51,18 @@ add_test(function test_wipeServer_list_s
 
   let server = httpd_setup({
     "/1.1/johndoe/storage/steam": steam_coll.handler(),
     "/1.1/johndoe/storage/diesel": diesel_coll.handler(),
     "/1.1/johndoe/storage/petrol": httpd_handler(404, "Not Found")
   });
 
   try {
-    setUpTestFixtures();
-    new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
+    setUpTestFixtures(server);
+    new SyncTestingInfrastructure(server, "johndoe", "irrelevant", "irrelevant");
 
     _("Confirm initial environment.");
     do_check_false(steam_coll.deleted);
     do_check_false(diesel_coll.deleted);
 
     _("wipeServer() will happily ignore the non-existent collection and use the timestamp of the last DELETE that was successful.");
     let timestamp = Service.wipeServer(["steam", "diesel", "petrol"]);
     do_check_eq(timestamp, diesel_coll.timestamp);
@@ -85,18 +85,18 @@ add_test(function test_wipeServer_list_5
 
   let server = httpd_setup({
     "/1.1/johndoe/storage/steam": steam_coll.handler(),
     "/1.1/johndoe/storage/petrol": httpd_handler(503, "Service Unavailable"),
     "/1.1/johndoe/storage/diesel": diesel_coll.handler()
   });
 
   try {
-    setUpTestFixtures();
-    new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
+    setUpTestFixtures(server);
+    new SyncTestingInfrastructure(server, "johndoe", "irrelevant", "irrelevant");
 
     _("Confirm initial environment.");
     do_check_false(steam_coll.deleted);
     do_check_false(diesel_coll.deleted);
 
     _("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection.");
     let error;
     try {
@@ -131,20 +131,20 @@ add_test(function test_wipeServer_all_su
     do_check_true(request.hasHeader("X-Confirm-Delete"));
     deleted = true;
     serverTimestamp = return_timestamp(request, response);
   }
 
   let server = httpd_setup({
     "/1.1/johndoe/storage": storageHandler
   });
-  setUpTestFixtures();
+  setUpTestFixtures(server);
 
   _("Try deletion.");
-  new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
+  new SyncTestingInfrastructure(server, "johndoe", "irrelevant", "irrelevant");
   let returnedTimestamp = Service.wipeServer();
   do_check_true(deleted);
   do_check_eq(returnedTimestamp, serverTimestamp);
 
   server.stop(run_next_test);
   Svc.Prefs.resetBranch("");
 });
 
@@ -163,20 +163,20 @@ add_test(function test_wipeServer_all_40
     serverTimestamp = new_timestamp();
     response.setHeader("X-Weave-Timestamp", "" + serverTimestamp);
     response.setStatusLine(request.httpVersion, 404, "Not Found");
   }
 
   let server = httpd_setup({
     "/1.1/johndoe/storage": storageHandler
   });
-  setUpTestFixtures();
+  setUpTestFixtures(server);
 
   _("Try deletion.");
-  new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
+  new SyncTestingInfrastructure(server, "johndoe", "irrelevant", "irrelevant");
   let returnedTimestamp = Service.wipeServer();
   do_check_true(deleted);
   do_check_eq(returnedTimestamp, serverTimestamp);
 
   server.stop(run_next_test);
   Svc.Prefs.resetBranch("");
 });
 
@@ -190,40 +190,44 @@ add_test(function test_wipeServer_all_50
     do_check_eq("DELETE", request.method);
     do_check_true(request.hasHeader("X-Confirm-Delete"));
     response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
   }
 
   let server = httpd_setup({
     "/1.1/johndoe/storage": storageHandler
   });
-  setUpTestFixtures();
+  setUpTestFixtures(server);
 
   _("Try deletion.");
   let error;
   try {
-    new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
+    new SyncTestingInfrastructure(server, "johndoe", "irrelevant", "irrelevant");
     Service.wipeServer();
     do_throw("Should have thrown!");
   } catch (ex) {
     error = ex;
   }
   do_check_eq(error.status, 503);
 
   server.stop(run_next_test);
   Svc.Prefs.resetBranch("");
 });
 
 add_test(function test_wipeServer_all_connectionRefused() {
   _("Service.wipeServer() throws if it encounters a network problem.");
-  setUpTestFixtures();
+  let server = httpd_setup({});
+  setUpTestFixtures(server);
+
+  Service.serverURL = "http://localhost:4352/";
+  Service.clusterURL = "http://localhost:4352/";
 
   _("Try deletion.");
   try {
     Service.wipeServer();
     do_throw("Should have thrown!");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_CONNECTION_REFUSED);
   }
 
-  run_next_test();
   Svc.Prefs.resetBranch("");
+  server.stop(run_next_test);
 });
--- a/services/sync/tests/unit/test_syncengine.js
+++ b/services/sync/tests/unit/test_syncengine.js
@@ -5,33 +5,35 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 function makeSteamEngine() {
   return new SyncEngine('Steam', Service);
 }
 
+let server;
+
 function test_url_attributes() {
   _("SyncEngine url attributes");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   Service.clusterURL = "https://cluster/";
   let engine = makeSteamEngine();
   try {
     do_check_eq(engine.storageURL, "https://cluster/1.1/foo/storage/");
     do_check_eq(engine.engineURL, "https://cluster/1.1/foo/storage/steam");
     do_check_eq(engine.metaURL, "https://cluster/1.1/foo/storage/meta/global");
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_syncID() {
   _("SyncEngine.syncID corresponds to preference");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeSteamEngine();
   try {
     // Ensure pristine environment
     do_check_eq(Svc.Prefs.get("steam.syncID"), undefined);
 
     // Performing the first get on the attribute will generate a new GUID.
     do_check_eq(engine.syncID, "fake-guid-0");
     do_check_eq(Svc.Prefs.get("steam.syncID"), "fake-guid-0");
@@ -41,17 +43,17 @@ function test_syncID() {
     do_check_eq(engine.syncID, "fake-guid-1");
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_lastSync() {
   _("SyncEngine.lastSync and SyncEngine.lastSyncLocal correspond to preferences");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeSteamEngine();
   try {
     // Ensure pristine environment
     do_check_eq(Svc.Prefs.get("steam.lastSync"), undefined);
     do_check_eq(engine.lastSync, 0);
     do_check_eq(Svc.Prefs.get("steam.lastSyncLocal"), undefined);
     do_check_eq(engine.lastSyncLocal, 0);
 
@@ -71,17 +73,17 @@ function test_lastSync() {
     do_check_eq(Svc.Prefs.get("steam.lastSync"), "0");
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_toFetch() {
   _("SyncEngine.toFetch corresponds to file on disk");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   const filename = "weave/toFetch/steam.json";
   let engine = makeSteamEngine();
   try {
     // Ensure pristine environment
     do_check_eq(engine.toFetch.length, 0);
 
     // Write file to disk
     let toFetch = [Utils.makeGUID(), Utils.makeGUID(), Utils.makeGUID()];
@@ -101,17 +103,17 @@ function test_toFetch() {
     do_check_eq(engine.toFetch[1], toFetch[1]);
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_previousFailed() {
   _("SyncEngine.previousFailed corresponds to file on disk");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   const filename = "weave/failed/steam.json";
   let engine = makeSteamEngine();
   try {
     // Ensure pristine environment
     do_check_eq(engine.previousFailed.length, 0);
 
     // Write file to disk
     let previousFailed = [Utils.makeGUID(), Utils.makeGUID(), Utils.makeGUID()];
@@ -131,17 +133,17 @@ function test_previousFailed() {
     do_check_eq(engine.previousFailed[1], previousFailed[1]);
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_resetClient() {
   _("SyncEngine.resetClient resets lastSync and toFetch");
-  let syncTesting = new SyncTestingInfrastructure();
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeSteamEngine();
   try {
     // Ensure pristine environment
     do_check_eq(Svc.Prefs.get("steam.lastSync"), undefined);
     do_check_eq(Svc.Prefs.get("steam.lastSyncLocal"), undefined);
     do_check_eq(engine.toFetch.length, 0);
 
     engine.lastSync = 123.45;
@@ -156,26 +158,24 @@ function test_resetClient() {
     do_check_eq(engine.previousFailed.length, 0);
   } finally {
     Svc.Prefs.resetBranch("");
   }
 }
 
 function test_wipeServer() {
   _("SyncEngine.wipeServer deletes server data and resets the client.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   let engine = makeSteamEngine();
 
   const PAYLOAD = 42;
   let steamCollection = new ServerWBO("steam", PAYLOAD);
   let server = httpd_setup({
     "/1.1/foo/storage/steam": steamCollection.handler()
   });
+  let syncTesting = new SyncTestingInfrastructure(server);
   do_test_pending();
 
   try {
     // Some data to reset.
     engine.lastSync = 123.45;
     engine.toFetch = [Utils.makeGUID(), Utils.makeGUID(), Utils.makeGUID()];
 
     _("Wipe server data and reset client.");
@@ -186,16 +186,19 @@ function test_wipeServer() {
 
   } finally {
     server.stop(do_test_finished);
     Svc.Prefs.resetBranch("");
   }
 }
 
 function run_test() {
+  server = httpd_setup({});
   test_url_attributes();
   test_syncID();
   test_lastSync();
   test_toFetch();
   test_previousFailed();
   test_resetClient();
   test_wipeServer();
+
+  server.stop(run_next_test);
 }
--- a/services/sync/tests/unit/test_syncengine_sync.js
+++ b/services/sync/tests/unit/test_syncengine_sync.js
@@ -17,43 +17,43 @@ function makeRotaryEngine() {
 
 function cleanAndGo(server) {
   Svc.Prefs.resetBranch("");
   Svc.Prefs.set("log.logger.engine.rotary", "Trace");
   Service.recordManager.clearCache();
   server.stop(run_next_test);
 }
 
-function configureService(username, password) {
-  Service.clusterURL = TEST_CLUSTER_URL;
+function configureService(server, username, password) {
+  Service.clusterURL = server.baseURI;
 
   Service.identity.account = username || "foo";
   Service.identity.basicPassword = password || "password";
 }
 
 function createServerAndConfigureClient() {
   let engine = new RotaryEngine(Service);
 
   let contents = {
     meta: {global: {engines: {rotary: {version: engine.version,
                                        syncID:  engine.syncID}}}},
     crypto: {},
     rotary: {}
   };
 
   const USER = "foo";
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = USER;
-
   let server = new SyncServer();
   server.registerUser(USER, "password");
   server.createContents(USER, contents);
   server.start();
 
+  Service.serverURL = server.baseURI;
+  Service.clusterURL = server.baseURI;
+  Service.identity.username = USER;
+
   return [engine, server, USER];
 }
 
 function run_test() {
   generateNewKeys(Service.collectionKeys);
   Svc.Prefs.set("log.logger.engine.rotary", "Trace");
   run_next_test();
 }
@@ -70,34 +70,32 @@ function run_test() {
  *
  * In the spirit of unit testing, these are tested individually for
  * different scenarios below.
  */
 
 add_test(function test_syncStartup_emptyOrOutdatedGlobalsResetsSync() {
   _("SyncEngine._syncStartup resets sync and wipes server data if there's no or an outdated global record");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = "foo";
-
   // Some server side data that's going to be wiped
   let collection = new ServerCollection();
   collection.insert('flying',
                     encryptPayload({id: 'flying',
                                     denomination: "LNER Class A3 4472"}));
   collection.insert('scotsman',
                     encryptPayload({id: 'scotsman',
                                     denomination: "Flying Scotsman"}));
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+  Service.identity.username = "foo";
+
   let engine = makeRotaryEngine();
   engine._store.items = {rekolok: "Rekonstruktionslokomotive"};
   try {
 
     // Confirm initial environment
     do_check_eq(engine._tracker.changedIDs["rekolok"], undefined);
     let metaGlobal = Service.recordManager.get(engine.metaURL);
     do_check_eq(metaGlobal.payload.engines, undefined);
@@ -124,25 +122,24 @@ add_test(function test_syncStartup_empty
   } finally {
     cleanAndGo(server);
   }
 });
 
 add_test(function test_syncStartup_serverHasNewerVersion() {
   _("SyncEngine._syncStartup ");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = "foo";
   let global = new ServerWBO('global', {engines: {rotary: {version: 23456}}});
   let server = httpd_setup({
       "/1.1/foo/storage/meta/global": global.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+  Service.identity.username = "foo";
+
   let engine = makeRotaryEngine();
   try {
 
     // The server has a newer version of the data and our engine can
     // handle.  That should give us an exception.
     let error;
     try {
       engine._syncStartup();
@@ -155,21 +152,19 @@ add_test(function test_syncStartup_serve
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_syncStartup_syncIDMismatchResetsClient() {
   _("SyncEngine._syncStartup resets sync if syncIDs don't match");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  let server = sync_httpd_setup({});
+  let syncTesting = new SyncTestingInfrastructure(server);
   Service.identity.username = "foo";
-  let server = sync_httpd_setup({});
 
   // global record with a different syncID than our engine has
   let engine = makeRotaryEngine();
   let global = new ServerWBO('global',
                              {engines: {rotary: {version: engine.version,
                                                 syncID: 'foobar'}}});
   server.registerPathHandler("/1.1/foo/storage/meta/global", global.handler());
 
@@ -193,49 +188,40 @@ add_test(function test_syncStartup_syncI
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_emptyServer() {
   _("SyncEngine._processIncoming working with an empty server backend");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = "foo";
   let collection = new ServerCollection();
-
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+  Service.identity.username = "foo";
+
   let engine = makeRotaryEngine();
   try {
 
     // Merely ensure that this code path is run without any errors
     engine._processIncoming();
     do_check_eq(engine.lastSync, 0);
 
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_createFromServer() {
   _("SyncEngine._processIncoming creates new records from server data");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = "foo";
-
-  generateNewKeys(Service.collectionKeys);
-
   // Some server records that will be downloaded
   let collection = new ServerCollection();
   collection.insert('flying',
                     encryptPayload({id: 'flying',
                                     denomination: "LNER Class A3 4472"}));
   collection.insert('scotsman',
                     encryptPayload({id: 'scotsman',
                                     denomination: "Flying Scotsman"}));
@@ -246,16 +232,21 @@ add_test(function test_processIncoming_c
   collection.insert('../pathological', pathologicalPayload);
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler(),
       "/1.1/foo/storage/rotary/flying": collection.wbo("flying").handler(),
       "/1.1/foo/storage/rotary/scotsman": collection.wbo("scotsman").handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+  Service.identity.username = "foo";
+
+  generateNewKeys(Service.collectionKeys);
+
   let engine = makeRotaryEngine();
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
 
   try {
 
@@ -282,20 +273,16 @@ add_test(function test_processIncoming_c
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_reconcile() {
   _("SyncEngine._processIncoming updates local records");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.identity.username = "foo";
   let collection = new ServerCollection();
 
   // This server record is newer than the corresponding client one,
   // so it'll update its data.
   collection.insert('newrecord',
                     encryptPayload({id: 'newrecord',
                                     denomination: "New stuff..."}));
 
@@ -330,16 +317,19 @@ add_test(function test_processIncoming_r
                     encryptPayload({id: 'nukeme',
                                     denomination: "Nuke me!",
                                     deleted: true}));
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+  Service.identity.username = "foo";
+
   let engine = makeRotaryEngine();
   engine._store.items = {newerserver: "New data, but not as new as server!",
                          olderidentical: "Older but identical",
                          updateclient: "Got data?",
                          original: "Original Entry",
                          long_original: "Long Original Entry",
                          nukeme: "Nuke me!"};
   // Make this record 1 min old, thus older than the one on the server
@@ -603,21 +593,18 @@ add_test(function test_processIncoming_r
   let payload = JSON.parse(JSON.parse(wbo.payload).ciphertext);
   do_check_eq("incoming", payload.denomination);
   cleanAndGo(server);
 });
 
 add_test(function test_processIncoming_mobile_batchSize() {
   _("SyncEngine._processIncoming doesn't fetch everything at once on mobile clients");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Svc.Prefs.set("client.type", "mobile");
   Service.identity.username = "foo";
-  Svc.Prefs.set("client.type", "mobile");
 
   // A collection that logs each GET
   let collection = new ServerCollection();
   collection.get_log = [];
   collection._get = collection.get;
   collection.get = function (options) {
     this.get_log.push(options);
     return this._get(options);
@@ -632,16 +619,18 @@ add_test(function test_processIncoming_m
     wbo.modified = Date.now()/1000 - 60*(i+10);
     collection.insertWBO(wbo);
   }
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let engine = makeRotaryEngine();
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
 
   try {
 
@@ -674,19 +663,16 @@ add_test(function test_processIncoming_m
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_store_toFetch() {
   _("If processIncoming fails in the middle of a batch on mobile, state is saved in toFetch and lastSync.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   Svc.Prefs.set("client.type", "mobile");
 
   // A collection that throws at the fourth get.
   let collection = new ServerCollection();
   collection._get_calls = 0;
   collection._get = collection.get;
   collection.get = function() {
@@ -704,24 +690,26 @@ add_test(function test_processIncoming_s
     let wbo = new ServerWBO(id, payload);
     wbo.modified = Date.now()/1000 + 60 * (i - MOBILE_BATCH_SIZE * 3);
     collection.insertWBO(wbo);
   }
 
   let engine = makeRotaryEngine();
   engine.enabled = true;
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
 
     // Confirm initial environment
     do_check_eq(engine.lastSync, 0);
     do_check_empty(engine._store.items);
 
     let error;
     try {
@@ -743,19 +731,16 @@ add_test(function test_processIncoming_s
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_resume_toFetch() {
   _("toFetch and previousFailed items left over from previous syncs are fetched on the next sync, along with new items.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   const LASTSYNC = Date.now() / 1000;
 
   // Server records that will be downloaded
   let collection = new ServerCollection();
   collection.insert('flying',
                     encryptPayload({id: 'flying',
@@ -779,24 +764,26 @@ add_test(function test_processIncoming_r
   collection._wbos.rekolok.modified = LASTSYNC + 10;
 
   // Time travel 10 seconds into the future but still download the above WBOs.
   let engine = makeRotaryEngine();
   engine.lastSync = LASTSYNC;
   engine.toFetch = ["flying", "scotsman"];
   engine.previousFailed = ["failed0", "failed1", "failed2"];
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
 
     // Confirm initial environment
     do_check_eq(engine._store.items.flying, undefined);
     do_check_eq(engine._store.items.scotsman, undefined);
     do_check_eq(engine._store.items.rekolok, undefined);
 
     engine._syncStartup();
@@ -813,19 +800,16 @@ add_test(function test_processIncoming_r
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_applyIncomingBatchSize_smaller() {
   _("Ensure that a number of incoming items less than applyIncomingBatchSize is still applied.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   // Engine that doesn't like the first and last record it's given.
   const APPLY_BATCH_SIZE = 10;
   let engine = makeRotaryEngine();
   engine.applyIncomingBatchSize = APPLY_BATCH_SIZE;
   engine._store._applyIncomingBatch = engine._store.applyIncomingBatch;
   engine._store.applyIncomingBatch = function (records) {
@@ -838,24 +822,26 @@ add_test(function test_processIncoming_a
   // Let's create less than a batch worth of server side records.
   let collection = new ServerCollection();
   for (let i = 0; i < APPLY_BATCH_SIZE - 1; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + id});
     collection.insert(id, payload);
   }
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
 
     // Confirm initial environment
     do_check_empty(engine._store.items);
 
     engine._syncStartup();
     engine._processIncoming();
 
@@ -869,18 +855,16 @@ add_test(function test_processIncoming_a
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_applyIncomingBatchSize_multiple() {
   _("Ensure that incoming items are applied according to applyIncomingBatchSize.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   const APPLY_BATCH_SIZE = 10;
 
   // Engine that applies records in batches.
   let engine = makeRotaryEngine();
   engine.applyIncomingBatchSize = APPLY_BATCH_SIZE;
   let batchCalls = 0;
@@ -894,24 +878,26 @@ add_test(function test_processIncoming_a
   // Let's create three batches worth of server side records.
   let collection = new ServerCollection();
   for (let i = 0; i < APPLY_BATCH_SIZE * 3; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + id});
     collection.insert(id, payload);
   }
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
 
     // Confirm initial environment
     do_check_empty(engine._store.items);
 
     engine._syncStartup();
     engine._processIncoming();
 
@@ -922,19 +908,16 @@ add_test(function test_processIncoming_a
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_notify_count() {
   _("Ensure that failed records are reported only once.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   const APPLY_BATCH_SIZE = 5;
   const NUMBER_OF_RECORDS = 15;
 
   // Engine that fails the first record.
   let engine = makeRotaryEngine();
   engine.applyIncomingBatchSize = APPLY_BATCH_SIZE;
@@ -947,24 +930,26 @@ add_test(function test_processIncoming_n
   // Create a batch of server side records.
   let collection = new ServerCollection();
   for (var i = 0; i < NUMBER_OF_RECORDS; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + id});
     collection.insert(id, payload);
   }
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
     // Confirm initial environment.
     do_check_eq(engine.lastSync, 0);
     do_check_eq(engine.toFetch.length, 0);
     do_check_eq(engine.previousFailed.length, 0);
     do_check_empty(engine._store.items);
 
     let called = 0;
@@ -1012,19 +997,16 @@ add_test(function test_processIncoming_n
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_previousFailed() {
   _("Ensure that failed records are retried.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   Svc.Prefs.set("client.type", "mobile");
 
   const APPLY_BATCH_SIZE = 4;
   const NUMBER_OF_RECORDS = 14;
 
   // Engine that fails the first 2 records.
   let engine = makeRotaryEngine();
@@ -1038,24 +1020,26 @@ add_test(function test_processIncoming_p
   // Create a batch of server side records.
   let collection = new ServerCollection();
   for (var i = 0; i < NUMBER_OF_RECORDS; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + i});
     collection.insert(id, payload);
   }
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
     // Confirm initial environment.
     do_check_eq(engine.lastSync, 0);
     do_check_eq(engine.toFetch.length, 0);
     do_check_eq(engine.previousFailed.length, 0);
     do_check_empty(engine._store.items);
 
     // Initial failed items in previousFailed to be reset.
@@ -1099,19 +1083,16 @@ add_test(function test_processIncoming_p
   } finally {
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_failed_records() {
   _("Ensure that failed records from _reconcile and applyIncomingBatch are refetched.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   // Let's create three and a bit batches worth of server side records.
   let collection = new ServerCollection();
   const NUMBER_OF_RECORDS = MOBILE_BATCH_SIZE * 3 + 5;
   for (let i = 0; i < NUMBER_OF_RECORDS; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + id});
@@ -1144,36 +1125,38 @@ add_test(function test_processIncoming_f
   engine._store._applyIncoming = engine._store.applyIncoming;
   engine._store.applyIncoming = function (record) {
     if (BOGUS_RECORDS.indexOf(record.id) % 2 == 1) {
       throw "I don't like this record! Baaaaaah!";
     }
     return this._applyIncoming.apply(this, arguments);
   };
 
-  let meta_global = Service.recordManager.set(engine.metaURL,
-                                              new WBORecord(engine.metaURL));
-  meta_global.payload.engines = {rotary: {version: engine.version,
-                                         syncID: engine.syncID}};
-
   // Keep track of requests made of a collection.
   let count = 0;
   let uris  = [];
   function recording_handler(collection) {
     let h = collection.handler();
     return function(req, res) {
       ++count;
       uris.push(req.path + "?" + req.queryString);
       return h(req, res);
     };
   }
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": recording_handler(collection)
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+
+  let meta_global = Service.recordManager.set(engine.metaURL,
+                                              new WBORecord(engine.metaURL));
+  meta_global.payload.engines = {rotary: {version: engine.version,
+                                         syncID: engine.syncID}};
+
   try {
 
     // Confirm initial environment
     do_check_eq(engine.lastSync, 0);
     do_check_eq(engine.toFetch.length, 0);
     do_check_eq(engine.previousFailed.length, 0);
     do_check_empty(engine._store.items);
 
@@ -1235,19 +1218,16 @@ add_test(function test_processIncoming_f
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_processIncoming_decrypt_failed() {
   _("Ensure that records failing to decrypt are either replaced or refetched.");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   // Some good and some bogus records. One doesn't contain valid JSON,
   // the other will throw during decrypt.
   let collection = new ServerCollection();
   collection._wbos.flying = new ServerWBO(
       'flying', encryptPayload({id: 'flying',
                                 denomination: "LNER Class A3 4472"}));
@@ -1269,24 +1249,26 @@ add_test(function test_processIncoming_d
   };
 
   // Some broken records also exist locally.
   let engine = makeRotaryEngine();
   engine.enabled = true;
   engine._store.items = {nojson: "Valid JSON",
                          nodecrypt: "Valid ciphertext"};
 
+  let server = sync_httpd_setup({
+      "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = sync_httpd_setup({
-      "/1.1/foo/storage/rotary": collection.handler()
-  });
-
   try {
 
     // Confirm initial state
     do_check_eq(engine.toFetch.length, 0);
     do_check_eq(engine.previousFailed.length, 0);
 
     let observerSubject;
     let observerData;
@@ -1314,29 +1296,28 @@ add_test(function test_processIncoming_d
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_uploadOutgoing_toEmptyServer() {
   _("SyncEngine._uploadOutgoing uploads new records to server");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   let collection = new ServerCollection();
   collection._wbos.flying = new ServerWBO('flying');
   collection._wbos.scotsman = new ServerWBO('scotsman');
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler(),
       "/1.1/foo/storage/rotary/flying": collection.wbo("flying").handler(),
       "/1.1/foo/storage/rotary/scotsman": collection.wbo("scotsman").handler()
   });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
   generateNewKeys(Service.collectionKeys);
 
   let engine = makeRotaryEngine();
   engine.lastSync = 123; // needs to be non-zero so that tracker is queried
   engine._store.items = {flying: "LNER Class A3 4472",
                          scotsman: "Flying Scotsman"};
   // Mark one of these records as changed
   engine._tracker.addChangedID('scotsman', 0);
@@ -1374,29 +1355,28 @@ add_test(function test_uploadOutgoing_to
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_uploadOutgoing_failed() {
   _("SyncEngine._uploadOutgoing doesn't clear the tracker of objects that failed to upload.");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   let collection = new ServerCollection();
   // We only define the "flying" WBO on the server, not the "scotsman"
   // and "peppercorn" ones.
   collection._wbos.flying = new ServerWBO('flying');
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let engine = makeRotaryEngine();
   engine.lastSync = 123; // needs to be non-zero so that tracker is queried
   engine._store.items = {flying: "LNER Class A3 4472",
                          scotsman: "Flying Scotsman",
                          peppercorn: "Peppercorn Class"};
   // Mark these records as changed
   const FLYING_CHANGED = 12345;
   const SCOTSMAN_CHANGED = 23456;
@@ -1438,19 +1418,16 @@ add_test(function test_uploadOutgoing_fa
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
   _("SyncEngine._uploadOutgoing uploads in batches of MAX_UPLOAD_RECORDS");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   let collection = new ServerCollection();
 
   // Let's count how many times the client posts to the server
   var noOfUploads = 0;
   collection.post = (function(orig) {
     return function() {
       noOfUploads++;
@@ -1471,16 +1448,18 @@ add_test(function test_uploadOutgoing_MA
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   try {
 
     // Confirm initial environment.
     do_check_eq(noOfUploads, 0);
 
     engine._syncStartup();
     engine._uploadOutgoing();
 
@@ -1496,49 +1475,49 @@ add_test(function test_uploadOutgoing_MA
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_syncFinish_noDelete() {
   _("SyncEngine._syncFinish resets tracker's score");
 
-  let syncTesting = new SyncTestingInfrastructure();
+  let server = httpd_setup({});
+
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeRotaryEngine();
   engine._delete = {}; // Nothing to delete
   engine._tracker.score = 100;
 
   // _syncFinish() will reset the engine's score.
   engine._syncFinish();
   do_check_eq(engine.score, 0);
-  run_next_test();
+  server.stop(run_next_test);
 });
 
 
 add_test(function test_syncFinish_deleteByIds() {
   _("SyncEngine._syncFinish deletes server records slated for deletion (list of record IDs).");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   let collection = new ServerCollection();
   collection._wbos.flying = new ServerWBO(
       'flying', encryptPayload({id: 'flying',
                                 denomination: "LNER Class A3 4472"}));
   collection._wbos.scotsman = new ServerWBO(
       'scotsman', encryptPayload({id: 'scotsman',
                                   denomination: "Flying Scotsman"}));
   collection._wbos.rekolok = new ServerWBO(
       'rekolok', encryptPayload({id: 'rekolok',
                                 denomination: "Rekonstruktionslokomotive"}));
 
   let server = httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
+  let syncTesting = new SyncTestingInfrastructure(server);
 
   let engine = makeRotaryEngine();
   try {
     engine._delete = {ids: ['flying', 'rekolok']};
     engine._syncFinish();
 
     // The 'flying' and 'rekolok' records were deleted while the
     // 'scotsman' one wasn't.
@@ -1553,19 +1532,16 @@ add_test(function test_syncFinish_delete
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_syncFinish_deleteLotsInBatches() {
   _("SyncEngine._syncFinish deletes server records in batches of 100 (list of record IDs).");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
   let collection = new ServerCollection();
 
   // Let's count how many times the client does a DELETE request to the server
   var noOfUploads = 0;
   collection.delete = (function(orig) {
     return function() {
       noOfUploads++;
@@ -1582,16 +1558,18 @@ add_test(function test_syncFinish_delete
     wbo.modified = now / 1000 - 60 * (i + 110);
     collection.insertWBO(wbo);
   }
 
   let server = httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let engine = makeRotaryEngine();
   try {
 
     // Confirm initial environment
     do_check_eq(noOfUploads, 0);
 
     // Declare what we want to have deleted: all records no. 100 and
     // up and all records that are less than 200 mins old (which are
@@ -1625,25 +1603,23 @@ add_test(function test_syncFinish_delete
     cleanAndGo(server);
   }
 });
 
 
 add_test(function test_sync_partialUpload() {
   _("SyncEngine.sync() keeps changedIDs that couldn't be uploaded.");
 
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   let collection = new ServerCollection();
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
+  let syncTesting = new SyncTestingInfrastructure(server);
   generateNewKeys(Service.collectionKeys);
 
   let engine = makeRotaryEngine();
   engine.lastSync = 123; // needs to be non-zero so that tracker is queried
   engine.lastSyncLocal = 456;
 
   // Let the third upload fail completely
   var noOfUploads = 0;
@@ -1700,97 +1676,93 @@ add_test(function test_sync_partialUploa
 
   } finally {
     cleanAndGo(server);
   }
 });
 
 add_test(function test_canDecrypt_noCryptoKeys() {
   _("SyncEngine.canDecrypt returns false if the engine fails to decrypt items on the server, e.g. due to a missing crypto key collection.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   // Wipe collection keys so we can test the desired scenario.
   Service.collectionKeys.clear();
 
   let collection = new ServerCollection();
   collection._wbos.flying = new ServerWBO(
       'flying', encryptPayload({id: 'flying',
                                 denomination: "LNER Class A3 4472"}));
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeRotaryEngine();
   try {
 
     do_check_false(engine.canDecrypt());
 
   } finally {
     cleanAndGo(server);
   }
 });
 
 add_test(function test_canDecrypt_true() {
   _("SyncEngine.canDecrypt returns true if the engine can decrypt the items on the server.");
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   generateNewKeys(Service.collectionKeys);
 
   let collection = new ServerCollection();
   collection._wbos.flying = new ServerWBO(
       'flying', encryptPayload({id: 'flying',
                                 denomination: "LNER Class A3 4472"}));
 
   let server = sync_httpd_setup({
       "/1.1/foo/storage/rotary": collection.handler()
   });
 
+  let syncTesting = new SyncTestingInfrastructure(server);
   let engine = makeRotaryEngine();
   try {
 
     do_check_true(engine.canDecrypt());
 
   } finally {
     cleanAndGo(server);
   }
 
 });
 
 add_test(function test_syncapplied_observer() {
-  let syncTesting = new SyncTestingInfrastructure();
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
   Service.identity.username = "foo";
 
   const NUMBER_OF_RECORDS = 10;
 
   let engine = makeRotaryEngine();
 
   // Create a batch of server side records.
   let collection = new ServerCollection();
   for (var i = 0; i < NUMBER_OF_RECORDS; i++) {
     let id = 'record-no-' + i;
     let payload = encryptPayload({id: id, denomination: "Record No. " + id});
     collection.insert(id, payload);
   }
 
+  let server = httpd_setup({
+    "/1.1/foo/storage/rotary": collection.handler()
+  });
+
+  let syncTesting = new SyncTestingInfrastructure(server);
+
   let meta_global = Service.recordManager.set(engine.metaURL,
                                               new WBORecord(engine.metaURL));
   meta_global.payload.engines = {rotary: {version: engine.version,
                                          syncID: engine.syncID}};
-  let server = httpd_setup({
-    "/1.1/foo/storage/rotary": collection.handler()
-  });
 
   let numApplyCalls = 0;
   let engine_name;
   let count;
   function onApplied(subject, data) {
     numApplyCalls++;
     engine_name = data;
     count = subject;
--- a/services/sync/tests/unit/test_syncscheduler.js
+++ b/services/sync/tests/unit/test_syncscheduler.js
@@ -47,19 +47,19 @@ function sync_httpd_setup() {
     "/1.1/johndoe/info/collections": collectionsHelper.handler,
     "/1.1/johndoe/storage/crypto/keys":
       upd("crypto", (new ServerWBO("keys")).handler()),
     "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
     "/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "null")
   });
 }
 
-function setUp() {
+function setUp(server) {
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.clusterURL = server.baseURI + "/";
 
   generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.identity.syncKeyBundle);
   return serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
 }
 
 function cleanUpAndGo(server) {
@@ -191,17 +191,17 @@ add_test(function test_masterpassword_lo
 
   let oldVerifyLogin = Service.verifyLogin;
   Service.verifyLogin = function () {
     Status.login = MASTER_PASSWORD_LOCKED;
     return false;
   };
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   Service.sync();
 
   do_check_true(loginFailed);
   do_check_eq(Status.login, MASTER_PASSWORD_LOCKED);
   do_check_true(rescheduleInterval);
 
   Service.verifyLogin = oldVerifyLogin;
@@ -234,17 +234,17 @@ add_test(function test_calculateBackoff(
 
 add_test(function test_scheduleNextSync_nowOrPast() {
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
     cleanUpAndGo(server);
   });
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // We're late for a sync...
   scheduler.scheduleNextSync(-1);
 });
 
 add_test(function test_scheduleNextSync_future_noBackoff() {
   _("scheduleNextSync() uses the current syncInterval if no interval is provided.");
   // Test backoffInterval is 0 as expected.
@@ -341,17 +341,17 @@ add_test(function test_scheduleNextSync_
   do_check_true(scheduler.nextSync <= Date.now() + Status.backoffInterval);
   do_check_eq(scheduler.syncTimer.delay, Status.backoffInterval);
 
   cleanUpAndGo();
 });
 
 add_test(function test_handleSyncError() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Force sync to fail.
   Svc.Prefs.set("firstSync", "notReady");
 
   _("Ensure expected initial environment.");
   do_check_eq(scheduler._syncErrors, 0);
   do_check_false(Status.enforceBackoff);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
@@ -399,17 +399,17 @@ add_test(function test_handleSyncError()
   do_check_true(Status.enforceBackoff);
   scheduler.syncTimer.clear();
 
   cleanUpAndGo(server);
 });
 
 add_test(function test_client_sync_finish_updateClientMode() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Confirm defaults.
   do_check_eq(scheduler.syncThreshold, SINGLE_USER_THRESHOLD);
   do_check_eq(scheduler.syncInterval, scheduler.singleDeviceInterval);
   do_check_false(scheduler.idle);
 
   // Trigger a change in interval & threshold by adding a client.
   clientsEngine._store.create({id: "foo", cleartext: "bar"});
@@ -440,17 +440,17 @@ add_test(function test_autoconnect_nextS
   // nextSync will be 0 by default, so it's way in the past.
 
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
     cleanUpAndGo(server);
   });
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   scheduler.delayedAutoConnect(0);
 });
 
 add_test(function test_autoconnect_nextSync_future() {
   let previousSync = Date.now() + scheduler.syncInterval / 2;
   scheduler.nextSync = previousSync;
   // nextSync rounds to the nearest second.
@@ -472,17 +472,17 @@ add_test(function test_autoconnect_nextS
   });
 
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
   scheduler.delayedAutoConnect(0);
 });
 
 add_test(function test_autoconnect_mp_locked() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Pretend user did not unlock master password.
   let origLocked = Utils.mpLocked;
   Utils.mpLocked = function() true;
 
   let origGetter = Service.identity.__lookupGetter__("syncKey");
   let origSetter = Service.identity.__lookupSetter__("syncKey");
   delete Service.identity.syncKey;
@@ -507,17 +507,17 @@ add_test(function test_autoconnect_mp_lo
     });
   });
 
   scheduler.delayedAutoConnect(0);
 });
 
 add_test(function test_no_autoconnect_during_wizard() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Simulate the Sync setup wizard.
   Svc.Prefs.set("firstSync", "notReady");
 
   // Ensure we don't actually try to sync (or log in for that matter).
   function onLoginStart() {
     do_throw("Should not get here!");
   }
@@ -556,17 +556,17 @@ add_test(function test_autoconnectDelay_
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
     cleanUpAndGo(server);
   });
 
   Svc.Prefs.set("autoconnectDelay", 1);
 
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   Svc.Obs.notify("weave:service:ready");
 
   // autoconnectDelay pref is multiplied by 1000.
   do_check_eq(scheduler._autoTimer.delay, 1000);
   do_check_eq(Status.service, STATUS_OK);
 });
 
@@ -665,19 +665,19 @@ add_test(function test_back_debouncing()
     cleanUpAndGo();
   }, IDLE_OBSERVER_BACK_DELAY * 1.5, {}, "timer");
 });
 
 add_test(function test_no_sync_node() {
   // Test when Status.sync == NO_SYNC_NODE_FOUND
   // it is not overwritten on sync:finish
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
-  Service.serverURL = TEST_SERVER_URL;
+  Service.serverURL = server.baseURI + "/";
 
   Service.sync();
   do_check_eq(Status.sync, NO_SYNC_NODE_FOUND);
   do_check_eq(scheduler.syncTimer.delay, NO_SYNC_NODE_INTERVAL);
 
   cleanUpAndGo(server);
 });
 
@@ -687,17 +687,17 @@ add_test(function test_sync_failed_parti
   let server = sync_httpd_setup();
 
   let engine = Service.engineManager.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 500};
 
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-  do_check_true(setUp());
+  do_check_true(setUp(server));
 
   Service.sync();
 
   do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
 
   let maxInterval = scheduler._syncErrors * (2 * MINIMUM_BACKOFF_INTERVAL);
   do_check_eq(Status.backoffInterval, 0);
   do_check_true(Status.enforceBackoff);
@@ -717,17 +717,17 @@ add_test(function test_sync_failed_parti
   engine.enabled = true;
   engine.exception = {status: 400};
 
   // Have multiple devices for an active interval.
   clientsEngine._store.create({id: "foo", cleartext: "bar"});
 
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
 
-  do_check_true(setUp());
+  do_check_true(setUp(server));
 
   Service.sync();
 
   do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
   do_check_eq(scheduler.syncInterval, scheduler.activeInterval);
 
   do_check_eq(Status.backoffInterval, 0);
   do_check_false(Status.enforceBackoff);
@@ -735,17 +735,17 @@ add_test(function test_sync_failed_parti
   do_check_true(scheduler.nextSync <= (Date.now() + scheduler.activeInterval));
   do_check_true(scheduler.syncTimer.delay <= scheduler.activeInterval);
 
   cleanUpAndGo(server);
 });
 
 add_test(function test_sync_X_Weave_Backoff() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Use an odd value on purpose so that it doesn't happen to coincide with one
   // of the sync intervals.
   const BACKOFF = 7337;
 
   // Extend info/collections so that we can put it into server maintenance mode.
   const INFO_COLLECTIONS = "/1.1/johndoe/info/collections";
   let infoColl = server._handler._overridePaths[INFO_COLLECTIONS];
@@ -790,17 +790,17 @@ add_test(function test_sync_X_Weave_Back
   do_check_true(scheduler.nextSync >= Date.now() + minimumExpectedDelay);
   do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay);
 
   cleanUpAndGo(server);
 });
 
 add_test(function test_sync_503_Retry_After() {
   let server = sync_httpd_setup();
-  setUp();
+  setUp(server);
 
   // Use an odd value on purpose so that it doesn't happen to coincide with one
   // of the sync intervals.
   const BACKOFF = 7337;
 
   // Extend info/collections so that we can put it into server maintenance mode.
   const INFO_COLLECTIONS = "/1.1/johndoe/info/collections";
   let infoColl = server._handler._overridePaths[INFO_COLLECTIONS];
@@ -850,18 +850,18 @@ add_test(function test_sync_503_Retry_Af
   do_check_true(scheduler.syncTimer.delay >= minimumExpectedDelay);
 
   cleanUpAndGo(server);
 });
 
 add_test(function test_loginError_recoverable_reschedules() {
   _("Verify that a recoverable login error schedules a new sync.");
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
+  Service.serverURL = "http://localhost:1234/";
+  Service.clusterURL = Service.serverURL;
   Service.persistLogin();
   Status.resetSync(); // reset Status.login
 
   Svc.Obs.add("weave:service:login:error", function onLoginError() {
     Svc.Obs.remove("weave:service:login:error", onLoginError);
     Utils.nextTick(function aLittleBitAfterLoginError() {
       do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
@@ -892,25 +892,26 @@ add_test(function test_loginError_recove
   do_check_eq(Status.login, LOGIN_SUCCEEDED);
 
   scheduler.scheduleNextSync(0);
 });
 
 add_test(function test_loginError_fatal_clearsTriggers() {
   _("Verify that a fatal login error clears sync triggers.");
   setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
-  Service.serverURL = TEST_SERVER_URL;
-  Service.clusterURL = TEST_CLUSTER_URL;
-  Service.persistLogin();
-  Status.resetSync(); // reset Status.login
 
   let server = httpd_setup({
     "/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized")
   });
 
+  Service.serverURL = server.baseURI + "/";
+  Service.clusterURL = Service.serverURL;
+  Service.persistLogin();
+  Status.resetSync(); // reset Status.login
+
   Svc.Obs.add("weave:service:login:error", function onLoginError() {
     Svc.Obs.remove("weave:service:login:error", onLoginError);
     Utils.nextTick(function aLittleBitAfterLoginError() {
       do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
 
       do_check_eq(scheduler.nextSync, 0);
       do_check_eq(scheduler.syncTimer, null);
 
--- a/services/sync/tests/unit/test_syncstoragerequest.js
+++ b/services/sync/tests/unit/test_syncstoragerequest.js
@@ -3,34 +3,32 @@
 
 Cu.import("resource://services-common/log4moz.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/rest.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
-const STORAGE_REQUEST_RESOURCE_URL = TEST_SERVER_URL + "resource";
-
 function run_test() {
   Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace;
   initTestLogging();
 
   run_next_test();
 }
 
 add_test(function test_user_agent_desktop() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   let expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
                    " FxSync/" + WEAVE_VERSION + "." +
                    Services.appinfo.appBuildID + ".desktop";
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.onComplete = function onComplete(error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_eq(handler.request.getHeader("User-Agent"), expectedUA);
     server.stop(run_next_test);
   };
   do_check_eq(request.get(), request);
 });
@@ -39,33 +37,33 @@ add_test(function test_user_agent_mobile
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   Svc.Prefs.set("client.type", "mobile");
   let expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
                    " FxSync/" + WEAVE_VERSION + "." +
                    Services.appinfo.appBuildID + ".mobile";
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_eq(handler.request.getHeader("User-Agent"), expectedUA);
     Svc.Prefs.resetBranch("");
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_auth() {
   let handler = httpd_handler(200, "OK");
   let server = httpd_setup({"/resource": handler});
 
   setBasicCredentials("johndoe", "ilovejane", "XXXXXXXXX");
 
-  let request = Service.getStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = Service.getStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_true(basic_auth_matches(handler.request, "johndoe", "ilovejane"));
 
     Svc.Prefs.reset("");
 
     server.stop(run_next_test);
@@ -79,17 +77,17 @@ add_test(function test_weave_timestamp()
   const TIMESTAMP = 1274380461;
   function handler(request, response) {
     response.setHeader("X-Weave-Timestamp", "" + TIMESTAMP, false);
     response.setStatusLine(request.httpVersion, 200, "OK");
   }
   let server = httpd_setup({"/resource": handler});
 
   do_check_eq(SyncStorageRequest.serverTime, undefined);
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_eq(SyncStorageRequest.serverTime, TIMESTAMP);
     delete SyncStorageRequest.serverTime;
     server.stop(run_next_test);
   });
 });
@@ -105,17 +103,17 @@ add_test(function test_weave_backoff() {
   let server = httpd_setup({"/resource": handler});
 
   let backoffInterval;
   Svc.Obs.add("weave:service:backoff:interval", function onBackoff(subject) {
     Svc.Obs.remove("weave:service:backoff:interval", onBackoff);
     backoffInterval = subject;
   });
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_eq(backoffInterval, 600);
     server.stop(run_next_test);
   });
 });
 
@@ -130,17 +128,17 @@ add_test(function test_weave_quota_notic
   let server = httpd_setup({"/resource": handler});
 
   let quotaValue;
   Svc.Obs.add("weave:service:quota:remaining", function onQuota(subject) {
     Svc.Obs.remove("weave:service:quota:remaining", onQuota);
     quotaValue = subject;
   });
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 200);
     do_check_eq(quotaValue, 1048576);
     server.stop(run_next_test);
   });
 });
 
@@ -155,17 +153,17 @@ add_test(function test_weave_quota_error
   let server = httpd_setup({"/resource": handler});
 
   let quotaValue;
   function onQuota(subject) {
     quotaValue = subject;
   }
   Svc.Obs.add("weave:service:quota:remaining", onQuota);
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
   request.get(function (error) {
     do_check_eq(error, null);
     do_check_eq(this.response.status, 400);
     do_check_eq(quotaValue, undefined);
     Svc.Obs.remove("weave:service:quota:remaining", onQuota);
     server.stop(run_next_test);
   });
 });
@@ -174,17 +172,17 @@ add_test(function test_abort() {
   function handler(request, response) {
     response.setHeader("X-Weave-Timestamp", "" + TIMESTAMP, false);
     response.setHeader("X-Weave-Quota-Remaining", '1048576', false);
     response.setHeader("X-Weave-Backoff", '600', false);
     response.setStatusLine(request.httpVersion, 200, "OK");
   }
   let server = httpd_setup({"/resource": handler});
 
-  let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
+  let request = new SyncStorageRequest(server.baseURI + "/resource");
 
   // Aborting a request that hasn't been sent yet is pointless and will throw.
   do_check_throws(function () {
     request.abort();
   });
 
   function throwy() {
     do_throw("Shouldn't have gotten here!");