1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,277 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +Cu.import("resource://services-sync/constants.js"); 1.8 +Cu.import("resource://services-sync/engines.js"); 1.9 +Cu.import("resource://services-sync/policies.js"); 1.10 +Cu.import("resource://services-sync/record.js"); 1.11 +Cu.import("resource://services-sync/service.js"); 1.12 +Cu.import("resource://services-sync/status.js"); 1.13 +Cu.import("resource://services-sync/util.js"); 1.14 +Cu.import("resource://testing-common/services/sync/fakeservices.js"); 1.15 +Cu.import("resource://testing-common/services/sync/utils.js"); 1.16 + 1.17 +initTestLogging("Trace"); 1.18 + 1.19 +let engineManager = Service.engineManager; 1.20 +engineManager.clear(); 1.21 + 1.22 +function promiseStopServer(server) { 1.23 + let deferred = Promise.defer(); 1.24 + server.stop(deferred.resolve); 1.25 + return deferred.promise; 1.26 +} 1.27 + 1.28 +function CatapultEngine() { 1.29 + SyncEngine.call(this, "Catapult", Service); 1.30 +} 1.31 +CatapultEngine.prototype = { 1.32 + __proto__: SyncEngine.prototype, 1.33 + exception: null, // tests fill this in 1.34 + _sync: function _sync() { 1.35 + throw this.exception; 1.36 + } 1.37 +}; 1.38 + 1.39 +function sync_httpd_setup() { 1.40 + let collectionsHelper = track_collections_helper(); 1.41 + let upd = collectionsHelper.with_updated_collection; 1.42 + let collections = collectionsHelper.collections; 1.43 + 1.44 + let catapultEngine = engineManager.get("catapult"); 1.45 + let engines = {catapult: {version: catapultEngine.version, 1.46 + syncID: catapultEngine.syncID}}; 1.47 + 1.48 + // Track these using the collections helper, which keeps modified times 1.49 + // up-to-date. 1.50 + let clientsColl = new ServerCollection({}, true); 1.51 + let keysWBO = new ServerWBO("keys"); 1.52 + let globalWBO = new ServerWBO("global", {storageVersion: STORAGE_VERSION, 1.53 + syncID: Utils.makeGUID(), 1.54 + engines: engines}); 1.55 + 1.56 + let handlers = { 1.57 + "/1.1/johndoe/info/collections": collectionsHelper.handler, 1.58 + "/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()), 1.59 + "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()), 1.60 + "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()) 1.61 + }; 1.62 + return httpd_setup(handlers); 1.63 +} 1.64 + 1.65 +function setUp(server) { 1.66 + yield configureIdentity({username: "johndoe"}); 1.67 + Service.serverURL = server.baseURI + "/"; 1.68 + Service.clusterURL = server.baseURI + "/"; 1.69 + new FakeCryptoService(); 1.70 +} 1.71 + 1.72 +function generateAndUploadKeys(server) { 1.73 + generateNewKeys(Service.collectionKeys); 1.74 + let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 1.75 + serverKeys.encrypt(Service.identity.syncKeyBundle); 1.76 + let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys"); 1.77 + return serverKeys.upload(res).success; 1.78 +} 1.79 + 1.80 + 1.81 +add_identity_test(this, function test_backoff500() { 1.82 + _("Test: HTTP 500 sets backoff status."); 1.83 + let server = sync_httpd_setup(); 1.84 + yield setUp(server); 1.85 + 1.86 + let engine = engineManager.get("catapult"); 1.87 + engine.enabled = true; 1.88 + engine.exception = {status: 500}; 1.89 + 1.90 + try { 1.91 + do_check_false(Status.enforceBackoff); 1.92 + 1.93 + // Forcibly create and upload keys here -- otherwise we don't get to the 500! 1.94 + do_check_true(generateAndUploadKeys(server)); 1.95 + 1.96 + Service.login(); 1.97 + Service.sync(); 1.98 + do_check_true(Status.enforceBackoff); 1.99 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.100 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.101 + } finally { 1.102 + Status.resetBackoff(); 1.103 + Service.startOver(); 1.104 + } 1.105 + yield promiseStopServer(server); 1.106 +}); 1.107 + 1.108 +add_identity_test(this, function test_backoff503() { 1.109 + _("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status."); 1.110 + let server = sync_httpd_setup(); 1.111 + yield setUp(server); 1.112 + 1.113 + const BACKOFF = 42; 1.114 + let engine = engineManager.get("catapult"); 1.115 + engine.enabled = true; 1.116 + engine.exception = {status: 503, 1.117 + headers: {"retry-after": BACKOFF}}; 1.118 + 1.119 + let backoffInterval; 1.120 + Svc.Obs.add("weave:service:backoff:interval", function (subject) { 1.121 + backoffInterval = subject; 1.122 + }); 1.123 + 1.124 + try { 1.125 + do_check_false(Status.enforceBackoff); 1.126 + 1.127 + do_check_true(generateAndUploadKeys(server)); 1.128 + 1.129 + Service.login(); 1.130 + Service.sync(); 1.131 + 1.132 + do_check_true(Status.enforceBackoff); 1.133 + do_check_eq(backoffInterval, BACKOFF); 1.134 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.135 + do_check_eq(Status.sync, SERVER_MAINTENANCE); 1.136 + } finally { 1.137 + Status.resetBackoff(); 1.138 + Status.resetSync(); 1.139 + Service.startOver(); 1.140 + } 1.141 + yield promiseStopServer(server); 1.142 +}); 1.143 + 1.144 +add_identity_test(this, function test_overQuota() { 1.145 + _("Test: HTTP 400 with body error code 14 means over quota."); 1.146 + let server = sync_httpd_setup(); 1.147 + yield setUp(server); 1.148 + 1.149 + let engine = engineManager.get("catapult"); 1.150 + engine.enabled = true; 1.151 + engine.exception = {status: 400, 1.152 + toString: function() "14"}; 1.153 + 1.154 + try { 1.155 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.156 + 1.157 + do_check_true(generateAndUploadKeys(server)); 1.158 + 1.159 + Service.login(); 1.160 + Service.sync(); 1.161 + 1.162 + do_check_eq(Status.sync, OVER_QUOTA); 1.163 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.164 + } finally { 1.165 + Status.resetSync(); 1.166 + Service.startOver(); 1.167 + } 1.168 + yield promiseStopServer(server); 1.169 +}); 1.170 + 1.171 +add_identity_test(this, function test_service_networkError() { 1.172 + _("Test: Connection refused error from Service.sync() leads to the right status code."); 1.173 + let server = sync_httpd_setup(); 1.174 + yield setUp(server); 1.175 + let deferred = Promise.defer(); 1.176 + server.stop(() => { 1.177 + // Provoke connection refused. 1.178 + Service.clusterURL = "http://localhost:12345/"; 1.179 + 1.180 + try { 1.181 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.182 + 1.183 + Service._loggedIn = true; 1.184 + Service.sync(); 1.185 + 1.186 + do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 1.187 + do_check_eq(Status.service, SYNC_FAILED); 1.188 + } finally { 1.189 + Status.resetSync(); 1.190 + Service.startOver(); 1.191 + } 1.192 + deferred.resolve(); 1.193 + }); 1.194 + yield deferred.promise; 1.195 +}); 1.196 + 1.197 +add_identity_test(this, function test_service_offline() { 1.198 + _("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count."); 1.199 + let server = sync_httpd_setup(); 1.200 + yield setUp(server); 1.201 + let deferred = Promise.defer(); 1.202 + server.stop(() => { 1.203 + Services.io.offline = true; 1.204 + 1.205 + try { 1.206 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.207 + 1.208 + Service._loggedIn = true; 1.209 + Service.sync(); 1.210 + 1.211 + do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 1.212 + do_check_eq(Status.service, SYNC_FAILED); 1.213 + } finally { 1.214 + Status.resetSync(); 1.215 + Service.startOver(); 1.216 + } 1.217 + Services.io.offline = false; 1.218 + deferred.resolve(); 1.219 + }); 1.220 + yield deferred.promise; 1.221 +}); 1.222 + 1.223 +add_identity_test(this, function test_engine_networkError() { 1.224 + _("Test: Network related exceptions from engine.sync() lead to the right status code."); 1.225 + let server = sync_httpd_setup(); 1.226 + yield setUp(server); 1.227 + 1.228 + let engine = engineManager.get("catapult"); 1.229 + engine.enabled = true; 1.230 + engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST", 1.231 + Cr.NS_ERROR_UNKNOWN_HOST); 1.232 + 1.233 + try { 1.234 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.235 + 1.236 + do_check_true(generateAndUploadKeys(server)); 1.237 + 1.238 + Service.login(); 1.239 + Service.sync(); 1.240 + 1.241 + do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 1.242 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.243 + } finally { 1.244 + Status.resetSync(); 1.245 + Service.startOver(); 1.246 + } 1.247 + yield promiseStopServer(server); 1.248 +}); 1.249 + 1.250 +add_identity_test(this, function test_resource_timeout() { 1.251 + let server = sync_httpd_setup(); 1.252 + yield setUp(server); 1.253 + 1.254 + let engine = engineManager.get("catapult"); 1.255 + engine.enabled = true; 1.256 + // Resource throws this when it encounters a timeout. 1.257 + engine.exception = Components.Exception("Aborting due to channel inactivity.", 1.258 + Cr.NS_ERROR_NET_TIMEOUT); 1.259 + 1.260 + try { 1.261 + do_check_eq(Status.sync, SYNC_SUCCEEDED); 1.262 + 1.263 + do_check_true(generateAndUploadKeys(server)); 1.264 + 1.265 + Service.login(); 1.266 + Service.sync(); 1.267 + 1.268 + do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 1.269 + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); 1.270 + } finally { 1.271 + Status.resetSync(); 1.272 + Service.startOver(); 1.273 + } 1.274 + yield promiseStopServer(server); 1.275 +}); 1.276 + 1.277 +function run_test() { 1.278 + engineManager.register(CatapultEngine); 1.279 + run_next_test(); 1.280 +}