michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-sync/engines.js"); michael@0: Cu.import("resource://services-sync/policies.js"); michael@0: Cu.import("resource://services-sync/record.js"); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: Cu.import("resource://services-sync/status.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/fakeservices.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: initTestLogging("Trace"); michael@0: michael@0: let engineManager = Service.engineManager; michael@0: engineManager.clear(); michael@0: michael@0: function promiseStopServer(server) { michael@0: let deferred = Promise.defer(); michael@0: server.stop(deferred.resolve); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function CatapultEngine() { michael@0: SyncEngine.call(this, "Catapult", Service); michael@0: } michael@0: CatapultEngine.prototype = { michael@0: __proto__: SyncEngine.prototype, michael@0: exception: null, // tests fill this in michael@0: _sync: function _sync() { michael@0: throw this.exception; michael@0: } michael@0: }; michael@0: michael@0: function sync_httpd_setup() { michael@0: let collectionsHelper = track_collections_helper(); michael@0: let upd = collectionsHelper.with_updated_collection; michael@0: let collections = collectionsHelper.collections; michael@0: michael@0: let catapultEngine = engineManager.get("catapult"); michael@0: let engines = {catapult: {version: catapultEngine.version, michael@0: syncID: catapultEngine.syncID}}; michael@0: michael@0: // Track these using the collections helper, which keeps modified times michael@0: // up-to-date. michael@0: let clientsColl = new ServerCollection({}, true); michael@0: let keysWBO = new ServerWBO("keys"); michael@0: let globalWBO = new ServerWBO("global", {storageVersion: STORAGE_VERSION, michael@0: syncID: Utils.makeGUID(), michael@0: engines: engines}); michael@0: michael@0: let handlers = { michael@0: "/1.1/johndoe/info/collections": collectionsHelper.handler, michael@0: "/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()), michael@0: "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()), michael@0: "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()) michael@0: }; michael@0: return httpd_setup(handlers); michael@0: } michael@0: michael@0: function setUp(server) { michael@0: yield configureIdentity({username: "johndoe"}); michael@0: Service.serverURL = server.baseURI + "/"; michael@0: Service.clusterURL = server.baseURI + "/"; michael@0: new FakeCryptoService(); michael@0: } michael@0: michael@0: function generateAndUploadKeys(server) { michael@0: generateNewKeys(Service.collectionKeys); michael@0: let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); michael@0: serverKeys.encrypt(Service.identity.syncKeyBundle); michael@0: let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys"); michael@0: return serverKeys.upload(res).success; michael@0: } michael@0: michael@0: michael@0: add_identity_test(this, function test_backoff500() { michael@0: _("Test: HTTP 500 sets backoff status."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let engine = engineManager.get("catapult"); michael@0: engine.enabled = true; michael@0: engine.exception = {status: 500}; michael@0: michael@0: try { michael@0: do_check_false(Status.enforceBackoff); michael@0: michael@0: // Forcibly create and upload keys here -- otherwise we don't get to the 500! michael@0: do_check_true(generateAndUploadKeys(server)); michael@0: michael@0: Service.login(); michael@0: Service.sync(); michael@0: do_check_true(Status.enforceBackoff); michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: do_check_eq(Status.service, SYNC_FAILED_PARTIAL); michael@0: } finally { michael@0: Status.resetBackoff(); michael@0: Service.startOver(); michael@0: } michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_backoff503() { michael@0: _("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: const BACKOFF = 42; michael@0: let engine = engineManager.get("catapult"); michael@0: engine.enabled = true; michael@0: engine.exception = {status: 503, michael@0: headers: {"retry-after": BACKOFF}}; michael@0: michael@0: let backoffInterval; michael@0: Svc.Obs.add("weave:service:backoff:interval", function (subject) { michael@0: backoffInterval = subject; michael@0: }); michael@0: michael@0: try { michael@0: do_check_false(Status.enforceBackoff); michael@0: michael@0: do_check_true(generateAndUploadKeys(server)); michael@0: michael@0: Service.login(); michael@0: Service.sync(); michael@0: michael@0: do_check_true(Status.enforceBackoff); michael@0: do_check_eq(backoffInterval, BACKOFF); michael@0: do_check_eq(Status.service, SYNC_FAILED_PARTIAL); michael@0: do_check_eq(Status.sync, SERVER_MAINTENANCE); michael@0: } finally { michael@0: Status.resetBackoff(); michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_overQuota() { michael@0: _("Test: HTTP 400 with body error code 14 means over quota."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let engine = engineManager.get("catapult"); michael@0: engine.enabled = true; michael@0: engine.exception = {status: 400, michael@0: toString: function() "14"}; michael@0: michael@0: try { michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: michael@0: do_check_true(generateAndUploadKeys(server)); michael@0: michael@0: Service.login(); michael@0: Service.sync(); michael@0: michael@0: do_check_eq(Status.sync, OVER_QUOTA); michael@0: do_check_eq(Status.service, SYNC_FAILED_PARTIAL); michael@0: } finally { michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_service_networkError() { michael@0: _("Test: Connection refused error from Service.sync() leads to the right status code."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: let deferred = Promise.defer(); michael@0: server.stop(() => { michael@0: // Provoke connection refused. michael@0: Service.clusterURL = "http://localhost:12345/"; michael@0: michael@0: try { michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: michael@0: Service._loggedIn = true; michael@0: Service.sync(); michael@0: michael@0: do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); michael@0: do_check_eq(Status.service, SYNC_FAILED); michael@0: } finally { michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: deferred.resolve(); michael@0: }); michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: add_identity_test(this, function test_service_offline() { michael@0: _("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: let deferred = Promise.defer(); michael@0: server.stop(() => { michael@0: Services.io.offline = true; michael@0: michael@0: try { michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: michael@0: Service._loggedIn = true; michael@0: Service.sync(); michael@0: michael@0: do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); michael@0: do_check_eq(Status.service, SYNC_FAILED); michael@0: } finally { michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: Services.io.offline = false; michael@0: deferred.resolve(); michael@0: }); michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: add_identity_test(this, function test_engine_networkError() { michael@0: _("Test: Network related exceptions from engine.sync() lead to the right status code."); michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let engine = engineManager.get("catapult"); michael@0: engine.enabled = true; michael@0: engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST", michael@0: Cr.NS_ERROR_UNKNOWN_HOST); michael@0: michael@0: try { michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: michael@0: do_check_true(generateAndUploadKeys(server)); michael@0: michael@0: Service.login(); michael@0: Service.sync(); michael@0: michael@0: do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); michael@0: do_check_eq(Status.service, SYNC_FAILED_PARTIAL); michael@0: } finally { michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: add_identity_test(this, function test_resource_timeout() { michael@0: let server = sync_httpd_setup(); michael@0: yield setUp(server); michael@0: michael@0: let engine = engineManager.get("catapult"); michael@0: engine.enabled = true; michael@0: // Resource throws this when it encounters a timeout. michael@0: engine.exception = Components.Exception("Aborting due to channel inactivity.", michael@0: Cr.NS_ERROR_NET_TIMEOUT); michael@0: michael@0: try { michael@0: do_check_eq(Status.sync, SYNC_SUCCEEDED); michael@0: michael@0: do_check_true(generateAndUploadKeys(server)); michael@0: michael@0: Service.login(); michael@0: Service.sync(); michael@0: michael@0: do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); michael@0: do_check_eq(Status.service, SYNC_FAILED_PARTIAL); michael@0: } finally { michael@0: Status.resetSync(); michael@0: Service.startOver(); michael@0: } michael@0: yield promiseStopServer(server); michael@0: }); michael@0: michael@0: function run_test() { michael@0: engineManager.register(CatapultEngine); michael@0: run_next_test(); michael@0: }