1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tests/unit/test_httpd_sync_server.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,284 @@ 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/util.js"); 1.8 + 1.9 +function run_test() { 1.10 + Log.repository.getLogger("Sync.Test.Server").level = Log.Level.Trace; 1.11 + initTestLogging(); 1.12 + run_next_test(); 1.13 +} 1.14 + 1.15 +add_test(function test_creation() { 1.16 + // Explicit callback for this one. 1.17 + let server = new SyncServer({ 1.18 + __proto__: SyncServerCallback, 1.19 + }); 1.20 + do_check_true(!!server); // Just so we have a check. 1.21 + server.start(null, function () { 1.22 + _("Started on " + server.port); 1.23 + server.stop(run_next_test); 1.24 + }); 1.25 +}); 1.26 + 1.27 +add_test(function test_url_parsing() { 1.28 + let server = new SyncServer(); 1.29 + 1.30 + // Check that we can parse a WBO URI. 1.31 + let parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto/keys"); 1.32 + let [all, version, username, first, rest] = parts; 1.33 + do_check_eq(all, "/1.1/johnsmith/storage/crypto/keys"); 1.34 + do_check_eq(version, "1.1"); 1.35 + do_check_eq(username, "johnsmith"); 1.36 + do_check_eq(first, "storage"); 1.37 + do_check_eq(rest, "crypto/keys"); 1.38 + do_check_eq(null, server.pathRE.exec("/nothing/else")); 1.39 + 1.40 + // Check that we can parse a collection URI. 1.41 + parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto"); 1.42 + let [all, version, username, first, rest] = parts; 1.43 + do_check_eq(all, "/1.1/johnsmith/storage/crypto"); 1.44 + do_check_eq(version, "1.1"); 1.45 + do_check_eq(username, "johnsmith"); 1.46 + do_check_eq(first, "storage"); 1.47 + do_check_eq(rest, "crypto"); 1.48 + 1.49 + // We don't allow trailing slash on storage URI. 1.50 + parts = server.pathRE.exec("/1.1/johnsmith/storage/"); 1.51 + do_check_eq(parts, undefined); 1.52 + 1.53 + // storage alone is a valid request. 1.54 + parts = server.pathRE.exec("/1.1/johnsmith/storage"); 1.55 + let [all, version, username, first, rest] = parts; 1.56 + do_check_eq(all, "/1.1/johnsmith/storage"); 1.57 + do_check_eq(version, "1.1"); 1.58 + do_check_eq(username, "johnsmith"); 1.59 + do_check_eq(first, "storage"); 1.60 + do_check_eq(rest, undefined); 1.61 + 1.62 + parts = server.storageRE.exec("storage"); 1.63 + let [all, storage, collection, id] = parts; 1.64 + do_check_eq(all, "storage"); 1.65 + do_check_eq(collection, undefined); 1.66 + 1.67 + run_next_test(); 1.68 +}); 1.69 + 1.70 +Cu.import("resource://services-common/rest.js"); 1.71 +function localRequest(server, path) { 1.72 + _("localRequest: " + path); 1.73 + let url = server.baseURI.substr(0, server.baseURI.length - 1) + path; 1.74 + _("url: " + url); 1.75 + return new RESTRequest(url); 1.76 +} 1.77 + 1.78 +add_test(function test_basic_http() { 1.79 + let server = new SyncServer(); 1.80 + server.registerUser("john", "password"); 1.81 + do_check_true(server.userExists("john")); 1.82 + server.start(null, function () { 1.83 + _("Started on " + server.port); 1.84 + Utils.nextTick(function () { 1.85 + let req = localRequest(server, "/1.1/john/storage/crypto/keys"); 1.86 + _("req is " + req); 1.87 + req.get(function (err) { 1.88 + do_check_eq(null, err); 1.89 + Utils.nextTick(function () { 1.90 + server.stop(run_next_test); 1.91 + }); 1.92 + }); 1.93 + }); 1.94 + }); 1.95 +}); 1.96 + 1.97 +add_test(function test_info_collections() { 1.98 + let server = new SyncServer({ 1.99 + __proto__: SyncServerCallback 1.100 + }); 1.101 + function responseHasCorrectHeaders(r) { 1.102 + do_check_eq(r.status, 200); 1.103 + do_check_eq(r.headers["content-type"], "application/json"); 1.104 + do_check_true("x-weave-timestamp" in r.headers); 1.105 + } 1.106 + 1.107 + server.registerUser("john", "password"); 1.108 + server.start(null, function () { 1.109 + Utils.nextTick(function () { 1.110 + let req = localRequest(server, "/1.1/john/info/collections"); 1.111 + req.get(function (err) { 1.112 + // Initial info/collections fetch is empty. 1.113 + do_check_eq(null, err); 1.114 + responseHasCorrectHeaders(this.response); 1.115 + 1.116 + do_check_eq(this.response.body, "{}"); 1.117 + Utils.nextTick(function () { 1.118 + // When we PUT something to crypto/keys, "crypto" appears in the response. 1.119 + function cb(err) { 1.120 + do_check_eq(null, err); 1.121 + responseHasCorrectHeaders(this.response); 1.122 + let putResponseBody = this.response.body; 1.123 + _("PUT response body: " + JSON.stringify(putResponseBody)); 1.124 + 1.125 + req = localRequest(server, "/1.1/john/info/collections"); 1.126 + req.get(function (err) { 1.127 + do_check_eq(null, err); 1.128 + responseHasCorrectHeaders(this.response); 1.129 + let expectedColl = server.getCollection("john", "crypto"); 1.130 + do_check_true(!!expectedColl); 1.131 + let modified = expectedColl.timestamp; 1.132 + do_check_true(modified > 0); 1.133 + do_check_eq(putResponseBody, modified); 1.134 + do_check_eq(JSON.parse(this.response.body).crypto, modified); 1.135 + Utils.nextTick(function () { 1.136 + server.stop(run_next_test); 1.137 + }); 1.138 + }); 1.139 + } 1.140 + let payload = JSON.stringify({foo: "bar"}); 1.141 + localRequest(server, "/1.1/john/storage/crypto/keys").put(payload, cb); 1.142 + }); 1.143 + }); 1.144 + }); 1.145 + }); 1.146 +}); 1.147 + 1.148 +add_test(function test_storage_request() { 1.149 + let keysURL = "/1.1/john/storage/crypto/keys?foo=bar"; 1.150 + let foosURL = "/1.1/john/storage/crypto/foos"; 1.151 + let storageURL = "/1.1/john/storage"; 1.152 + 1.153 + let server = new SyncServer(); 1.154 + let creation = server.timestamp(); 1.155 + server.registerUser("john", "password"); 1.156 + 1.157 + server.createContents("john", { 1.158 + crypto: {foos: {foo: "bar"}} 1.159 + }); 1.160 + let coll = server.user("john").collection("crypto"); 1.161 + do_check_true(!!coll); 1.162 + 1.163 + _("We're tracking timestamps."); 1.164 + do_check_true(coll.timestamp >= creation); 1.165 + 1.166 + function retrieveWBONotExists(next) { 1.167 + let req = localRequest(server, keysURL); 1.168 + req.get(function (err) { 1.169 + _("Body is " + this.response.body); 1.170 + _("Modified is " + this.response.newModified); 1.171 + do_check_eq(null, err); 1.172 + do_check_eq(this.response.status, 404); 1.173 + do_check_eq(this.response.body, "Not found"); 1.174 + Utils.nextTick(next); 1.175 + }); 1.176 + } 1.177 + function retrieveWBOExists(next) { 1.178 + let req = localRequest(server, foosURL); 1.179 + req.get(function (err) { 1.180 + _("Body is " + this.response.body); 1.181 + _("Modified is " + this.response.newModified); 1.182 + let parsedBody = JSON.parse(this.response.body); 1.183 + do_check_eq(parsedBody.id, "foos"); 1.184 + do_check_eq(parsedBody.modified, coll.wbo("foos").modified); 1.185 + do_check_eq(JSON.parse(parsedBody.payload).foo, "bar"); 1.186 + Utils.nextTick(next); 1.187 + }); 1.188 + } 1.189 + function deleteWBONotExists(next) { 1.190 + let req = localRequest(server, keysURL); 1.191 + server.callback.onItemDeleted = function (username, collection, wboID) { 1.192 + do_throw("onItemDeleted should not have been called."); 1.193 + }; 1.194 + 1.195 + req.delete(function (err) { 1.196 + _("Body is " + this.response.body); 1.197 + _("Modified is " + this.response.newModified); 1.198 + do_check_eq(this.response.status, 200); 1.199 + delete server.callback.onItemDeleted; 1.200 + Utils.nextTick(next); 1.201 + }); 1.202 + } 1.203 + function deleteWBOExists(next) { 1.204 + let req = localRequest(server, foosURL); 1.205 + server.callback.onItemDeleted = function (username, collection, wboID) { 1.206 + _("onItemDeleted called for " + collection + "/" + wboID); 1.207 + delete server.callback.onItemDeleted; 1.208 + do_check_eq(username, "john"); 1.209 + do_check_eq(collection, "crypto"); 1.210 + do_check_eq(wboID, "foos"); 1.211 + Utils.nextTick(next); 1.212 + }; 1.213 + 1.214 + req.delete(function (err) { 1.215 + _("Body is " + this.response.body); 1.216 + _("Modified is " + this.response.newModified); 1.217 + do_check_eq(this.response.status, 200); 1.218 + }); 1.219 + } 1.220 + function deleteStorage(next) { 1.221 + _("Testing DELETE on /storage."); 1.222 + let now = server.timestamp(); 1.223 + _("Timestamp: " + now); 1.224 + let req = localRequest(server, storageURL); 1.225 + req.delete(function (err) { 1.226 + _("Body is " + this.response.body); 1.227 + _("Modified is " + this.response.newModified); 1.228 + let parsedBody = JSON.parse(this.response.body); 1.229 + do_check_true(parsedBody >= now); 1.230 + do_check_empty(server.users["john"].collections); 1.231 + Utils.nextTick(next); 1.232 + }); 1.233 + } 1.234 + function getStorageFails(next) { 1.235 + _("Testing that GET on /storage fails."); 1.236 + let req = localRequest(server, storageURL); 1.237 + req.get(function (err) { 1.238 + do_check_eq(this.response.status, 405); 1.239 + do_check_eq(this.response.headers["allow"], "DELETE"); 1.240 + Utils.nextTick(next); 1.241 + }); 1.242 + } 1.243 + function getMissingCollectionWBO(next) { 1.244 + _("Testing that fetching a WBO from an on-existent collection 404s."); 1.245 + let req = localRequest(server, storageURL + "/foobar/baz"); 1.246 + req.get(function (err) { 1.247 + do_check_eq(this.response.status, 404); 1.248 + Utils.nextTick(next); 1.249 + }); 1.250 + } 1.251 + 1.252 + server.start(null, 1.253 + Async.chain( 1.254 + retrieveWBONotExists, 1.255 + retrieveWBOExists, 1.256 + deleteWBOExists, 1.257 + deleteWBONotExists, 1.258 + getStorageFails, 1.259 + getMissingCollectionWBO, 1.260 + deleteStorage, 1.261 + server.stop.bind(server), 1.262 + run_next_test 1.263 + )); 1.264 +}); 1.265 + 1.266 +add_test(function test_x_weave_records() { 1.267 + let server = new SyncServer(); 1.268 + server.registerUser("john", "password"); 1.269 + 1.270 + server.createContents("john", { 1.271 + crypto: {foos: {foo: "bar"}, 1.272 + bars: {foo: "baz"}} 1.273 + }); 1.274 + server.start(null, function () { 1.275 + let wbo = localRequest(server, "/1.1/john/storage/crypto/foos"); 1.276 + wbo.get(function (err) { 1.277 + // WBO fetches don't have one. 1.278 + do_check_false("x-weave-records" in this.response.headers); 1.279 + let col = localRequest(server, "/1.1/john/storage/crypto"); 1.280 + col.get(function (err) { 1.281 + // Collection fetches do. 1.282 + do_check_eq(this.response.headers["x-weave-records"], "2"); 1.283 + server.stop(run_next_test); 1.284 + }); 1.285 + }); 1.286 + }); 1.287 +});