test_errorhandler_sync_checkServerError.js (7994B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { Service } = ChromeUtils.importESModule( 5 "resource://services-sync/service.sys.mjs" 6 ); 7 const { Status } = ChromeUtils.importESModule( 8 "resource://services-sync/status.sys.mjs" 9 ); 10 const { FakeCryptoService } = ChromeUtils.importESModule( 11 "resource://testing-common/services/sync/fakeservices.sys.mjs" 12 ); 13 14 var engineManager = Service.engineManager; 15 16 function CatapultEngine() { 17 SyncEngine.call(this, "Catapult", Service); 18 } 19 CatapultEngine.prototype = { 20 exception: null, // tests fill this in 21 async _sync() { 22 throw this.exception; 23 }, 24 }; 25 Object.setPrototypeOf(CatapultEngine.prototype, SyncEngine.prototype); 26 27 async function sync_httpd_setup() { 28 let collectionsHelper = track_collections_helper(); 29 let upd = collectionsHelper.with_updated_collection; 30 31 let catapultEngine = engineManager.get("catapult"); 32 let syncID = await catapultEngine.resetLocalSyncID(); 33 let engines = { catapult: { version: catapultEngine.version, syncID } }; 34 35 // Track these using the collections helper, which keeps modified times 36 // up-to-date. 37 let clientsColl = new ServerCollection({}, true); 38 let keysWBO = new ServerWBO("keys"); 39 let globalWBO = new ServerWBO("global", { 40 storageVersion: STORAGE_VERSION, 41 syncID: Utils.makeGUID(), 42 engines, 43 }); 44 45 let handlers = { 46 "/1.1/johndoe/info/collections": collectionsHelper.handler, 47 "/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()), 48 "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()), 49 "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), 50 }; 51 return httpd_setup(handlers); 52 } 53 54 async function setUp(server) { 55 await configureIdentity({ username: "johndoe" }, server); 56 new FakeCryptoService(); 57 syncTestLogging(); 58 } 59 60 async function generateAndUploadKeys(server) { 61 await generateNewKeys(Service.collectionKeys); 62 let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); 63 await serverKeys.encrypt(Service.identity.syncKeyBundle); 64 let res = Service.resource( 65 server.baseURI + "/1.1/johndoe/storage/crypto/keys" 66 ); 67 return (await serverKeys.upload(res)).success; 68 } 69 70 add_task(async function setup() { 71 await engineManager.clear(); 72 validate_all_future_pings(); 73 await engineManager.register(CatapultEngine); 74 }); 75 76 add_task(async function test_backoff500() { 77 enableValidationPrefs(); 78 79 _("Test: HTTP 500 sets backoff status."); 80 let server = await sync_httpd_setup(); 81 await setUp(server); 82 83 let engine = engineManager.get("catapult"); 84 engine.enabled = true; 85 engine.exception = { status: 500 }; 86 87 try { 88 Assert.ok(!Status.enforceBackoff); 89 90 // Forcibly create and upload keys here -- otherwise we don't get to the 500! 91 Assert.ok(await generateAndUploadKeys(server)); 92 93 await Service.login(); 94 await Service.sync(); 95 Assert.ok(Status.enforceBackoff); 96 Assert.equal(Status.sync, SYNC_SUCCEEDED); 97 Assert.equal(Status.service, SYNC_FAILED_PARTIAL); 98 } finally { 99 Status.resetBackoff(); 100 await Service.startOver(); 101 } 102 await promiseStopServer(server); 103 }); 104 105 add_task(async function test_backoff503() { 106 enableValidationPrefs(); 107 108 _( 109 "Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status." 110 ); 111 let server = await sync_httpd_setup(); 112 await setUp(server); 113 114 const BACKOFF = 42; 115 let engine = engineManager.get("catapult"); 116 engine.enabled = true; 117 engine.exception = { status: 503, headers: { "retry-after": BACKOFF } }; 118 119 let backoffInterval; 120 Svc.Obs.add("weave:service:backoff:interval", function (subject) { 121 backoffInterval = subject; 122 }); 123 124 try { 125 Assert.ok(!Status.enforceBackoff); 126 127 Assert.ok(await generateAndUploadKeys(server)); 128 129 await Service.login(); 130 await Service.sync(); 131 132 Assert.ok(Status.enforceBackoff); 133 Assert.equal(backoffInterval, BACKOFF); 134 Assert.equal(Status.service, SYNC_FAILED_PARTIAL); 135 Assert.equal(Status.sync, SERVER_MAINTENANCE); 136 } finally { 137 Status.resetBackoff(); 138 Status.resetSync(); 139 await Service.startOver(); 140 } 141 await promiseStopServer(server); 142 }); 143 144 add_task(async function test_overQuota() { 145 enableValidationPrefs(); 146 147 _("Test: HTTP 400 with body error code 14 means over quota."); 148 let server = await sync_httpd_setup(); 149 await setUp(server); 150 151 let engine = engineManager.get("catapult"); 152 engine.enabled = true; 153 engine.exception = { 154 status: 400, 155 toString() { 156 return "14"; 157 }, 158 }; 159 160 try { 161 Assert.equal(Status.sync, SYNC_SUCCEEDED); 162 163 Assert.ok(await generateAndUploadKeys(server)); 164 165 await Service.login(); 166 await Service.sync(); 167 168 Assert.equal(Status.sync, OVER_QUOTA); 169 Assert.equal(Status.service, SYNC_FAILED_PARTIAL); 170 } finally { 171 Status.resetSync(); 172 await Service.startOver(); 173 } 174 await promiseStopServer(server); 175 }); 176 177 add_task(async function test_service_networkError() { 178 enableValidationPrefs(); 179 180 _( 181 "Test: Connection refused error from Service.sync() leads to the right status code." 182 ); 183 let server = await sync_httpd_setup(); 184 await setUp(server); 185 await promiseStopServer(server); 186 // Provoke connection refused. 187 Service.clusterURL = "http://localhost:12345/"; 188 189 try { 190 Assert.equal(Status.sync, SYNC_SUCCEEDED); 191 192 Service._loggedIn = true; 193 await Service.sync(); 194 195 Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 196 Assert.equal(Status.service, SYNC_FAILED); 197 } finally { 198 Status.resetSync(); 199 await Service.startOver(); 200 } 201 }); 202 203 add_task(async function test_service_offline() { 204 enableValidationPrefs(); 205 206 _( 207 "Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count." 208 ); 209 let server = await sync_httpd_setup(); 210 await setUp(server); 211 212 await promiseStopServer(server); 213 Services.io.offline = true; 214 Services.prefs.setBoolPref("network.dns.offline-localhost", false); 215 216 try { 217 Assert.equal(Status.sync, SYNC_SUCCEEDED); 218 219 Service._loggedIn = true; 220 await Service.sync(); 221 222 Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 223 Assert.equal(Status.service, SYNC_FAILED); 224 } finally { 225 Status.resetSync(); 226 await Service.startOver(); 227 } 228 Services.io.offline = false; 229 Services.prefs.clearUserPref("network.dns.offline-localhost"); 230 }); 231 232 add_task(async function test_engine_networkError() { 233 enableValidationPrefs(); 234 235 _( 236 "Test: Network related exceptions from engine.sync() lead to the right status code." 237 ); 238 let server = await sync_httpd_setup(); 239 await setUp(server); 240 241 let engine = engineManager.get("catapult"); 242 engine.enabled = true; 243 engine.exception = Components.Exception( 244 "NS_ERROR_UNKNOWN_HOST", 245 Cr.NS_ERROR_UNKNOWN_HOST 246 ); 247 248 try { 249 Assert.equal(Status.sync, SYNC_SUCCEEDED); 250 251 Assert.ok(await generateAndUploadKeys(server)); 252 253 await Service.login(); 254 await Service.sync(); 255 256 Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 257 Assert.equal(Status.service, SYNC_FAILED_PARTIAL); 258 } finally { 259 Status.resetSync(); 260 await Service.startOver(); 261 } 262 await promiseStopServer(server); 263 }); 264 265 add_task(async function test_resource_timeout() { 266 enableValidationPrefs(); 267 268 let server = await sync_httpd_setup(); 269 await setUp(server); 270 271 let engine = engineManager.get("catapult"); 272 engine.enabled = true; 273 // Resource throws this when it encounters a timeout. 274 engine.exception = Components.Exception( 275 "Aborting due to channel inactivity.", 276 Cr.NS_ERROR_NET_TIMEOUT 277 ); 278 279 try { 280 Assert.equal(Status.sync, SYNC_SUCCEEDED); 281 282 Assert.ok(await generateAndUploadKeys(server)); 283 284 await Service.login(); 285 await Service.sync(); 286 287 Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR); 288 Assert.equal(Status.service, SYNC_FAILED_PARTIAL); 289 } finally { 290 Status.resetSync(); 291 await Service.startOver(); 292 } 293 await promiseStopServer(server); 294 });