|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 Cu.import("resource://services-sync/util.js"); |
|
5 |
|
6 function run_test() { |
|
7 Log.repository.getLogger("Sync.Test.Server").level = Log.Level.Trace; |
|
8 initTestLogging(); |
|
9 run_next_test(); |
|
10 } |
|
11 |
|
12 add_test(function test_creation() { |
|
13 // Explicit callback for this one. |
|
14 let server = new SyncServer({ |
|
15 __proto__: SyncServerCallback, |
|
16 }); |
|
17 do_check_true(!!server); // Just so we have a check. |
|
18 server.start(null, function () { |
|
19 _("Started on " + server.port); |
|
20 server.stop(run_next_test); |
|
21 }); |
|
22 }); |
|
23 |
|
24 add_test(function test_url_parsing() { |
|
25 let server = new SyncServer(); |
|
26 |
|
27 // Check that we can parse a WBO URI. |
|
28 let parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto/keys"); |
|
29 let [all, version, username, first, rest] = parts; |
|
30 do_check_eq(all, "/1.1/johnsmith/storage/crypto/keys"); |
|
31 do_check_eq(version, "1.1"); |
|
32 do_check_eq(username, "johnsmith"); |
|
33 do_check_eq(first, "storage"); |
|
34 do_check_eq(rest, "crypto/keys"); |
|
35 do_check_eq(null, server.pathRE.exec("/nothing/else")); |
|
36 |
|
37 // Check that we can parse a collection URI. |
|
38 parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto"); |
|
39 let [all, version, username, first, rest] = parts; |
|
40 do_check_eq(all, "/1.1/johnsmith/storage/crypto"); |
|
41 do_check_eq(version, "1.1"); |
|
42 do_check_eq(username, "johnsmith"); |
|
43 do_check_eq(first, "storage"); |
|
44 do_check_eq(rest, "crypto"); |
|
45 |
|
46 // We don't allow trailing slash on storage URI. |
|
47 parts = server.pathRE.exec("/1.1/johnsmith/storage/"); |
|
48 do_check_eq(parts, undefined); |
|
49 |
|
50 // storage alone is a valid request. |
|
51 parts = server.pathRE.exec("/1.1/johnsmith/storage"); |
|
52 let [all, version, username, first, rest] = parts; |
|
53 do_check_eq(all, "/1.1/johnsmith/storage"); |
|
54 do_check_eq(version, "1.1"); |
|
55 do_check_eq(username, "johnsmith"); |
|
56 do_check_eq(first, "storage"); |
|
57 do_check_eq(rest, undefined); |
|
58 |
|
59 parts = server.storageRE.exec("storage"); |
|
60 let [all, storage, collection, id] = parts; |
|
61 do_check_eq(all, "storage"); |
|
62 do_check_eq(collection, undefined); |
|
63 |
|
64 run_next_test(); |
|
65 }); |
|
66 |
|
67 Cu.import("resource://services-common/rest.js"); |
|
68 function localRequest(server, path) { |
|
69 _("localRequest: " + path); |
|
70 let url = server.baseURI.substr(0, server.baseURI.length - 1) + path; |
|
71 _("url: " + url); |
|
72 return new RESTRequest(url); |
|
73 } |
|
74 |
|
75 add_test(function test_basic_http() { |
|
76 let server = new SyncServer(); |
|
77 server.registerUser("john", "password"); |
|
78 do_check_true(server.userExists("john")); |
|
79 server.start(null, function () { |
|
80 _("Started on " + server.port); |
|
81 Utils.nextTick(function () { |
|
82 let req = localRequest(server, "/1.1/john/storage/crypto/keys"); |
|
83 _("req is " + req); |
|
84 req.get(function (err) { |
|
85 do_check_eq(null, err); |
|
86 Utils.nextTick(function () { |
|
87 server.stop(run_next_test); |
|
88 }); |
|
89 }); |
|
90 }); |
|
91 }); |
|
92 }); |
|
93 |
|
94 add_test(function test_info_collections() { |
|
95 let server = new SyncServer({ |
|
96 __proto__: SyncServerCallback |
|
97 }); |
|
98 function responseHasCorrectHeaders(r) { |
|
99 do_check_eq(r.status, 200); |
|
100 do_check_eq(r.headers["content-type"], "application/json"); |
|
101 do_check_true("x-weave-timestamp" in r.headers); |
|
102 } |
|
103 |
|
104 server.registerUser("john", "password"); |
|
105 server.start(null, function () { |
|
106 Utils.nextTick(function () { |
|
107 let req = localRequest(server, "/1.1/john/info/collections"); |
|
108 req.get(function (err) { |
|
109 // Initial info/collections fetch is empty. |
|
110 do_check_eq(null, err); |
|
111 responseHasCorrectHeaders(this.response); |
|
112 |
|
113 do_check_eq(this.response.body, "{}"); |
|
114 Utils.nextTick(function () { |
|
115 // When we PUT something to crypto/keys, "crypto" appears in the response. |
|
116 function cb(err) { |
|
117 do_check_eq(null, err); |
|
118 responseHasCorrectHeaders(this.response); |
|
119 let putResponseBody = this.response.body; |
|
120 _("PUT response body: " + JSON.stringify(putResponseBody)); |
|
121 |
|
122 req = localRequest(server, "/1.1/john/info/collections"); |
|
123 req.get(function (err) { |
|
124 do_check_eq(null, err); |
|
125 responseHasCorrectHeaders(this.response); |
|
126 let expectedColl = server.getCollection("john", "crypto"); |
|
127 do_check_true(!!expectedColl); |
|
128 let modified = expectedColl.timestamp; |
|
129 do_check_true(modified > 0); |
|
130 do_check_eq(putResponseBody, modified); |
|
131 do_check_eq(JSON.parse(this.response.body).crypto, modified); |
|
132 Utils.nextTick(function () { |
|
133 server.stop(run_next_test); |
|
134 }); |
|
135 }); |
|
136 } |
|
137 let payload = JSON.stringify({foo: "bar"}); |
|
138 localRequest(server, "/1.1/john/storage/crypto/keys").put(payload, cb); |
|
139 }); |
|
140 }); |
|
141 }); |
|
142 }); |
|
143 }); |
|
144 |
|
145 add_test(function test_storage_request() { |
|
146 let keysURL = "/1.1/john/storage/crypto/keys?foo=bar"; |
|
147 let foosURL = "/1.1/john/storage/crypto/foos"; |
|
148 let storageURL = "/1.1/john/storage"; |
|
149 |
|
150 let server = new SyncServer(); |
|
151 let creation = server.timestamp(); |
|
152 server.registerUser("john", "password"); |
|
153 |
|
154 server.createContents("john", { |
|
155 crypto: {foos: {foo: "bar"}} |
|
156 }); |
|
157 let coll = server.user("john").collection("crypto"); |
|
158 do_check_true(!!coll); |
|
159 |
|
160 _("We're tracking timestamps."); |
|
161 do_check_true(coll.timestamp >= creation); |
|
162 |
|
163 function retrieveWBONotExists(next) { |
|
164 let req = localRequest(server, keysURL); |
|
165 req.get(function (err) { |
|
166 _("Body is " + this.response.body); |
|
167 _("Modified is " + this.response.newModified); |
|
168 do_check_eq(null, err); |
|
169 do_check_eq(this.response.status, 404); |
|
170 do_check_eq(this.response.body, "Not found"); |
|
171 Utils.nextTick(next); |
|
172 }); |
|
173 } |
|
174 function retrieveWBOExists(next) { |
|
175 let req = localRequest(server, foosURL); |
|
176 req.get(function (err) { |
|
177 _("Body is " + this.response.body); |
|
178 _("Modified is " + this.response.newModified); |
|
179 let parsedBody = JSON.parse(this.response.body); |
|
180 do_check_eq(parsedBody.id, "foos"); |
|
181 do_check_eq(parsedBody.modified, coll.wbo("foos").modified); |
|
182 do_check_eq(JSON.parse(parsedBody.payload).foo, "bar"); |
|
183 Utils.nextTick(next); |
|
184 }); |
|
185 } |
|
186 function deleteWBONotExists(next) { |
|
187 let req = localRequest(server, keysURL); |
|
188 server.callback.onItemDeleted = function (username, collection, wboID) { |
|
189 do_throw("onItemDeleted should not have been called."); |
|
190 }; |
|
191 |
|
192 req.delete(function (err) { |
|
193 _("Body is " + this.response.body); |
|
194 _("Modified is " + this.response.newModified); |
|
195 do_check_eq(this.response.status, 200); |
|
196 delete server.callback.onItemDeleted; |
|
197 Utils.nextTick(next); |
|
198 }); |
|
199 } |
|
200 function deleteWBOExists(next) { |
|
201 let req = localRequest(server, foosURL); |
|
202 server.callback.onItemDeleted = function (username, collection, wboID) { |
|
203 _("onItemDeleted called for " + collection + "/" + wboID); |
|
204 delete server.callback.onItemDeleted; |
|
205 do_check_eq(username, "john"); |
|
206 do_check_eq(collection, "crypto"); |
|
207 do_check_eq(wboID, "foos"); |
|
208 Utils.nextTick(next); |
|
209 }; |
|
210 |
|
211 req.delete(function (err) { |
|
212 _("Body is " + this.response.body); |
|
213 _("Modified is " + this.response.newModified); |
|
214 do_check_eq(this.response.status, 200); |
|
215 }); |
|
216 } |
|
217 function deleteStorage(next) { |
|
218 _("Testing DELETE on /storage."); |
|
219 let now = server.timestamp(); |
|
220 _("Timestamp: " + now); |
|
221 let req = localRequest(server, storageURL); |
|
222 req.delete(function (err) { |
|
223 _("Body is " + this.response.body); |
|
224 _("Modified is " + this.response.newModified); |
|
225 let parsedBody = JSON.parse(this.response.body); |
|
226 do_check_true(parsedBody >= now); |
|
227 do_check_empty(server.users["john"].collections); |
|
228 Utils.nextTick(next); |
|
229 }); |
|
230 } |
|
231 function getStorageFails(next) { |
|
232 _("Testing that GET on /storage fails."); |
|
233 let req = localRequest(server, storageURL); |
|
234 req.get(function (err) { |
|
235 do_check_eq(this.response.status, 405); |
|
236 do_check_eq(this.response.headers["allow"], "DELETE"); |
|
237 Utils.nextTick(next); |
|
238 }); |
|
239 } |
|
240 function getMissingCollectionWBO(next) { |
|
241 _("Testing that fetching a WBO from an on-existent collection 404s."); |
|
242 let req = localRequest(server, storageURL + "/foobar/baz"); |
|
243 req.get(function (err) { |
|
244 do_check_eq(this.response.status, 404); |
|
245 Utils.nextTick(next); |
|
246 }); |
|
247 } |
|
248 |
|
249 server.start(null, |
|
250 Async.chain( |
|
251 retrieveWBONotExists, |
|
252 retrieveWBOExists, |
|
253 deleteWBOExists, |
|
254 deleteWBONotExists, |
|
255 getStorageFails, |
|
256 getMissingCollectionWBO, |
|
257 deleteStorage, |
|
258 server.stop.bind(server), |
|
259 run_next_test |
|
260 )); |
|
261 }); |
|
262 |
|
263 add_test(function test_x_weave_records() { |
|
264 let server = new SyncServer(); |
|
265 server.registerUser("john", "password"); |
|
266 |
|
267 server.createContents("john", { |
|
268 crypto: {foos: {foo: "bar"}, |
|
269 bars: {foo: "baz"}} |
|
270 }); |
|
271 server.start(null, function () { |
|
272 let wbo = localRequest(server, "/1.1/john/storage/crypto/foos"); |
|
273 wbo.get(function (err) { |
|
274 // WBO fetches don't have one. |
|
275 do_check_false("x-weave-records" in this.response.headers); |
|
276 let col = localRequest(server, "/1.1/john/storage/crypto"); |
|
277 col.get(function (err) { |
|
278 // Collection fetches do. |
|
279 do_check_eq(this.response.headers["x-weave-records"], "2"); |
|
280 server.stop(run_next_test); |
|
281 }); |
|
282 }); |
|
283 }); |
|
284 }); |