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