1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/common/tests/unit/test_storage_server.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,691 @@ 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-common/async.js"); 1.8 +Cu.import("resource://services-common/rest.js"); 1.9 +Cu.import("resource://services-common/utils.js"); 1.10 +Cu.import("resource://testing-common/services-common/storageserver.js"); 1.11 + 1.12 +const DEFAULT_USER = "123"; 1.13 +const DEFAULT_PASSWORD = "password"; 1.14 + 1.15 +/** 1.16 + * Helper function to prepare a RESTRequest against the server. 1.17 + */ 1.18 +function localRequest(server, path, user=DEFAULT_USER, password=DEFAULT_PASSWORD) { 1.19 + _("localRequest: " + path); 1.20 + let identity = server.server.identity; 1.21 + let url = identity.primaryScheme + "://" + identity.primaryHost + ":" + 1.22 + identity.primaryPort + path; 1.23 + _("url: " + url); 1.24 + let req = new RESTRequest(url); 1.25 + 1.26 + let header = basic_auth_header(user, password); 1.27 + req.setHeader("Authorization", header); 1.28 + req.setHeader("Accept", "application/json"); 1.29 + 1.30 + return req; 1.31 +} 1.32 + 1.33 +/** 1.34 + * Helper function to validate an HTTP response from the server. 1.35 + */ 1.36 +function validateResponse(response) { 1.37 + do_check_true("x-timestamp" in response.headers); 1.38 + 1.39 + if ("content-length" in response.headers) { 1.40 + let cl = parseInt(response.headers["content-length"]); 1.41 + 1.42 + if (cl != 0) { 1.43 + do_check_true("content-type" in response.headers); 1.44 + do_check_eq("application/json", response.headers["content-type"]); 1.45 + } 1.46 + } 1.47 + 1.48 + if (response.status == 204 || response.status == 304) { 1.49 + do_check_false("content-type" in response.headers); 1.50 + 1.51 + if ("content-length" in response.headers) { 1.52 + do_check_eq(response.headers["content-length"], "0"); 1.53 + } 1.54 + } 1.55 + 1.56 + if (response.status == 405) { 1.57 + do_check_true("allow" in response.headers); 1.58 + } 1.59 +} 1.60 + 1.61 +/** 1.62 + * Helper function to synchronously wait for a response and validate it. 1.63 + */ 1.64 +function waitAndValidateResponse(cb, request) { 1.65 + let error = cb.wait(); 1.66 + 1.67 + if (!error) { 1.68 + validateResponse(request.response); 1.69 + } 1.70 + 1.71 + return error; 1.72 +} 1.73 + 1.74 +/** 1.75 + * Helper function to synchronously perform a GET request. 1.76 + * 1.77 + * @return Error instance or null if no error. 1.78 + */ 1.79 +function doGetRequest(request) { 1.80 + let cb = Async.makeSpinningCallback(); 1.81 + request.get(cb); 1.82 + 1.83 + return waitAndValidateResponse(cb, request); 1.84 +} 1.85 + 1.86 +/** 1.87 + * Helper function to synchronously perform a PUT request. 1.88 + * 1.89 + * @return Error instance or null if no error. 1.90 + */ 1.91 +function doPutRequest(request, data) { 1.92 + let cb = Async.makeSpinningCallback(); 1.93 + request.put(data, cb); 1.94 + 1.95 + return waitAndValidateResponse(cb, request); 1.96 +} 1.97 + 1.98 +/** 1.99 + * Helper function to synchronously perform a DELETE request. 1.100 + * 1.101 + * @return Error or null if no error was encountered. 1.102 + */ 1.103 +function doDeleteRequest(request) { 1.104 + let cb = Async.makeSpinningCallback(); 1.105 + request.delete(cb); 1.106 + 1.107 + return waitAndValidateResponse(cb, request); 1.108 +} 1.109 + 1.110 +function run_test() { 1.111 + Log.repository.getLogger("Services.Common.Test.StorageServer").level = 1.112 + Log.Level.Trace; 1.113 + initTestLogging(); 1.114 + 1.115 + run_next_test(); 1.116 +} 1.117 + 1.118 +add_test(function test_creation() { 1.119 + _("Ensure a simple server can be created."); 1.120 + 1.121 + // Explicit callback for this one. 1.122 + let server = new StorageServer({ 1.123 + __proto__: StorageServerCallback, 1.124 + }); 1.125 + do_check_true(!!server); 1.126 + 1.127 + server.start(-1, function () { 1.128 + _("Started on " + server.port); 1.129 + server.stop(run_next_test); 1.130 + }); 1.131 +}); 1.132 + 1.133 +add_test(function test_synchronous_start() { 1.134 + _("Ensure starting using startSynchronous works."); 1.135 + 1.136 + let server = new StorageServer(); 1.137 + server.startSynchronous(); 1.138 + server.stop(run_next_test); 1.139 +}); 1.140 + 1.141 +add_test(function test_url_parsing() { 1.142 + _("Ensure server parses URLs properly."); 1.143 + 1.144 + let server = new StorageServer(); 1.145 + 1.146 + // Check that we can parse a BSO URI. 1.147 + let parts = server.pathRE.exec("/2.0/12345/storage/crypto/keys"); 1.148 + let [all, version, user, first, rest] = parts; 1.149 + do_check_eq(all, "/2.0/12345/storage/crypto/keys"); 1.150 + do_check_eq(version, "2.0"); 1.151 + do_check_eq(user, "12345"); 1.152 + do_check_eq(first, "storage"); 1.153 + do_check_eq(rest, "crypto/keys"); 1.154 + do_check_eq(null, server.pathRE.exec("/nothing/else")); 1.155 + 1.156 + // Check that we can parse a collection URI. 1.157 + parts = server.pathRE.exec("/2.0/123/storage/crypto"); 1.158 + let [all, version, user, first, rest] = parts; 1.159 + do_check_eq(all, "/2.0/123/storage/crypto"); 1.160 + do_check_eq(version, "2.0"); 1.161 + do_check_eq(user, "123"); 1.162 + do_check_eq(first, "storage"); 1.163 + do_check_eq(rest, "crypto"); 1.164 + 1.165 + // We don't allow trailing slash on storage URI. 1.166 + parts = server.pathRE.exec("/2.0/1234/storage/"); 1.167 + do_check_eq(parts, undefined); 1.168 + 1.169 + // storage alone is a valid request. 1.170 + parts = server.pathRE.exec("/2.0/123456/storage"); 1.171 + let [all, version, user, first, rest] = parts; 1.172 + do_check_eq(all, "/2.0/123456/storage"); 1.173 + do_check_eq(version, "2.0"); 1.174 + do_check_eq(user, "123456"); 1.175 + do_check_eq(first, "storage"); 1.176 + do_check_eq(rest, undefined); 1.177 + 1.178 + parts = server.storageRE.exec("storage"); 1.179 + let [all, storage, collection, id] = parts; 1.180 + do_check_eq(all, "storage"); 1.181 + do_check_eq(collection, undefined); 1.182 + 1.183 + run_next_test(); 1.184 +}); 1.185 + 1.186 +add_test(function test_basic_http() { 1.187 + let server = new StorageServer(); 1.188 + server.registerUser("345", "password"); 1.189 + do_check_true(server.userExists("345")); 1.190 + server.startSynchronous(); 1.191 + 1.192 + _("Started on " + server.port); 1.193 + do_check_eq(server.requestCount, 0); 1.194 + let req = localRequest(server, "/2.0/storage/crypto/keys"); 1.195 + _("req is " + req); 1.196 + req.get(function (err) { 1.197 + do_check_eq(null, err); 1.198 + do_check_eq(server.requestCount, 1); 1.199 + server.stop(run_next_test); 1.200 + }); 1.201 +}); 1.202 + 1.203 +add_test(function test_info_collections() { 1.204 + let server = new StorageServer(); 1.205 + server.registerUser("123", "password"); 1.206 + server.startSynchronous(); 1.207 + 1.208 + let path = "/2.0/123/info/collections"; 1.209 + 1.210 + _("info/collections on empty server should be empty object."); 1.211 + let request = localRequest(server, path, "123", "password"); 1.212 + let error = doGetRequest(request); 1.213 + do_check_eq(error, null); 1.214 + do_check_eq(request.response.status, 200); 1.215 + do_check_eq(request.response.body, "{}"); 1.216 + 1.217 + _("Creating an empty collection should result in collection appearing."); 1.218 + let coll = server.createCollection("123", "col1"); 1.219 + let request = localRequest(server, path, "123", "password"); 1.220 + let error = doGetRequest(request); 1.221 + do_check_eq(error, null); 1.222 + do_check_eq(request.response.status, 200); 1.223 + let info = JSON.parse(request.response.body); 1.224 + do_check_attribute_count(info, 1); 1.225 + do_check_true("col1" in info); 1.226 + do_check_eq(info.col1, coll.timestamp); 1.227 + 1.228 + server.stop(run_next_test); 1.229 +}); 1.230 + 1.231 +add_test(function test_bso_get_existing() { 1.232 + _("Ensure that BSO retrieval works."); 1.233 + 1.234 + let server = new StorageServer(); 1.235 + server.registerUser("123", "password"); 1.236 + server.createContents("123", { 1.237 + test: {"bso": {"foo": "bar"}} 1.238 + }); 1.239 + server.startSynchronous(); 1.240 + 1.241 + let coll = server.user("123").collection("test"); 1.242 + 1.243 + let request = localRequest(server, "/2.0/123/storage/test/bso", "123", 1.244 + "password"); 1.245 + let error = doGetRequest(request); 1.246 + do_check_eq(error, null); 1.247 + do_check_eq(request.response.status, 200); 1.248 + do_check_eq(request.response.headers["content-type"], "application/json"); 1.249 + let bso = JSON.parse(request.response.body); 1.250 + do_check_attribute_count(bso, 3); 1.251 + do_check_eq(bso.id, "bso"); 1.252 + do_check_eq(bso.modified, coll.bso("bso").modified); 1.253 + let payload = JSON.parse(bso.payload); 1.254 + do_check_attribute_count(payload, 1); 1.255 + do_check_eq(payload.foo, "bar"); 1.256 + 1.257 + server.stop(run_next_test); 1.258 +}); 1.259 + 1.260 +add_test(function test_percent_decoding() { 1.261 + _("Ensure query string arguments with percent encoded are handled."); 1.262 + 1.263 + let server = new StorageServer(); 1.264 + server.registerUser("123", "password"); 1.265 + server.startSynchronous(); 1.266 + 1.267 + let coll = server.user("123").createCollection("test"); 1.268 + coll.insert("001", {foo: "bar"}); 1.269 + coll.insert("002", {bar: "foo"}); 1.270 + 1.271 + let request = localRequest(server, "/2.0/123/storage/test?ids=001%2C002", 1.272 + "123", "password"); 1.273 + let error = doGetRequest(request); 1.274 + do_check_null(error); 1.275 + do_check_eq(request.response.status, 200); 1.276 + let items = JSON.parse(request.response.body).items; 1.277 + do_check_attribute_count(items, 2); 1.278 + 1.279 + server.stop(run_next_test); 1.280 +}); 1.281 + 1.282 +add_test(function test_bso_404() { 1.283 + _("Ensure the server responds with a 404 if a BSO does not exist."); 1.284 + 1.285 + let server = new StorageServer(); 1.286 + server.registerUser("123", "password"); 1.287 + server.createContents("123", { 1.288 + test: {} 1.289 + }); 1.290 + server.startSynchronous(); 1.291 + 1.292 + let request = localRequest(server, "/2.0/123/storage/test/foo"); 1.293 + let error = doGetRequest(request); 1.294 + do_check_eq(error, null); 1.295 + 1.296 + do_check_eq(request.response.status, 404); 1.297 + do_check_false("content-type" in request.response.headers); 1.298 + 1.299 + server.stop(run_next_test); 1.300 +}); 1.301 + 1.302 +add_test(function test_bso_if_modified_since_304() { 1.303 + _("Ensure the server responds properly to X-If-Modified-Since for BSOs."); 1.304 + 1.305 + let server = new StorageServer(); 1.306 + server.registerUser("123", "password"); 1.307 + server.createContents("123", { 1.308 + test: {bso: {foo: "bar"}} 1.309 + }); 1.310 + server.startSynchronous(); 1.311 + 1.312 + let coll = server.user("123").collection("test"); 1.313 + do_check_neq(coll, null); 1.314 + 1.315 + // Rewind clock just in case. 1.316 + coll.timestamp -= 10000; 1.317 + coll.bso("bso").modified -= 10000; 1.318 + 1.319 + let request = localRequest(server, "/2.0/123/storage/test/bso", 1.320 + "123", "password"); 1.321 + request.setHeader("X-If-Modified-Since", "" + server.serverTime()); 1.322 + let error = doGetRequest(request); 1.323 + do_check_eq(null, error); 1.324 + 1.325 + do_check_eq(request.response.status, 304); 1.326 + do_check_false("content-type" in request.response.headers); 1.327 + 1.328 + let request = localRequest(server, "/2.0/123/storage/test/bso", 1.329 + "123", "password"); 1.330 + request.setHeader("X-If-Modified-Since", "" + (server.serverTime() - 20000)); 1.331 + let error = doGetRequest(request); 1.332 + do_check_eq(null, error); 1.333 + do_check_eq(request.response.status, 200); 1.334 + do_check_eq(request.response.headers["content-type"], "application/json"); 1.335 + 1.336 + server.stop(run_next_test); 1.337 +}); 1.338 + 1.339 +add_test(function test_bso_if_unmodified_since() { 1.340 + _("Ensure X-If-Unmodified-Since works properly on BSOs."); 1.341 + 1.342 + let server = new StorageServer(); 1.343 + server.registerUser("123", "password"); 1.344 + server.createContents("123", { 1.345 + test: {bso: {foo: "bar"}} 1.346 + }); 1.347 + server.startSynchronous(); 1.348 + 1.349 + let coll = server.user("123").collection("test"); 1.350 + do_check_neq(coll, null); 1.351 + 1.352 + let time = coll.bso("bso").modified; 1.353 + 1.354 + _("Ensure we get a 412 for specified times older than server time."); 1.355 + let request = localRequest(server, "/2.0/123/storage/test/bso", 1.356 + "123", "password"); 1.357 + request.setHeader("X-If-Unmodified-Since", time - 5000); 1.358 + request.setHeader("Content-Type", "application/json"); 1.359 + let payload = JSON.stringify({"payload": "foobar"}); 1.360 + let error = doPutRequest(request, payload); 1.361 + do_check_eq(null, error); 1.362 + do_check_eq(request.response.status, 412); 1.363 + 1.364 + _("Ensure we get a 204 if update goes through."); 1.365 + let request = localRequest(server, "/2.0/123/storage/test/bso", 1.366 + "123", "password"); 1.367 + request.setHeader("Content-Type", "application/json"); 1.368 + request.setHeader("X-If-Unmodified-Since", time + 1); 1.369 + let error = doPutRequest(request, payload); 1.370 + do_check_eq(null, error); 1.371 + do_check_eq(request.response.status, 204); 1.372 + do_check_true(coll.timestamp > time); 1.373 + 1.374 + // Not sure why a client would send X-If-Unmodified-Since if a BSO doesn't 1.375 + // exist. But, why not test it? 1.376 + _("Ensure we get a 201 if creation goes through."); 1.377 + let request = localRequest(server, "/2.0/123/storage/test/none", 1.378 + "123", "password"); 1.379 + request.setHeader("Content-Type", "application/json"); 1.380 + request.setHeader("X-If-Unmodified-Since", time); 1.381 + let error = doPutRequest(request, payload); 1.382 + do_check_eq(null, error); 1.383 + do_check_eq(request.response.status, 201); 1.384 + 1.385 + server.stop(run_next_test); 1.386 +}); 1.387 + 1.388 +add_test(function test_bso_delete_not_exist() { 1.389 + _("Ensure server behaves properly when deleting a BSO that does not exist."); 1.390 + 1.391 + let server = new StorageServer(); 1.392 + server.registerUser("123", "password"); 1.393 + server.user("123").createCollection("empty"); 1.394 + server.startSynchronous(); 1.395 + 1.396 + server.callback.onItemDeleted = function onItemDeleted(username, collection, 1.397 + id) { 1.398 + do_throw("onItemDeleted should not have been called."); 1.399 + }; 1.400 + 1.401 + let request = localRequest(server, "/2.0/123/storage/empty/nada", 1.402 + "123", "password"); 1.403 + let error = doDeleteRequest(request); 1.404 + do_check_eq(error, null); 1.405 + do_check_eq(request.response.status, 404); 1.406 + do_check_false("content-type" in request.response.headers); 1.407 + 1.408 + server.stop(run_next_test); 1.409 +}); 1.410 + 1.411 +add_test(function test_bso_delete_exists() { 1.412 + _("Ensure proper semantics when deleting a BSO that exists."); 1.413 + 1.414 + let server = new StorageServer(); 1.415 + server.registerUser("123", "password"); 1.416 + server.startSynchronous(); 1.417 + 1.418 + let coll = server.user("123").createCollection("test"); 1.419 + let bso = coll.insert("myid", {foo: "bar"}); 1.420 + let timestamp = coll.timestamp; 1.421 + 1.422 + server.callback.onItemDeleted = function onDeleted(username, collection, id) { 1.423 + delete server.callback.onItemDeleted; 1.424 + do_check_eq(username, "123"); 1.425 + do_check_eq(collection, "test"); 1.426 + do_check_eq(id, "myid"); 1.427 + }; 1.428 + 1.429 + let request = localRequest(server, "/2.0/123/storage/test/myid", 1.430 + "123", "password"); 1.431 + let error = doDeleteRequest(request); 1.432 + do_check_eq(error, null); 1.433 + do_check_eq(request.response.status, 204); 1.434 + do_check_eq(coll.bsos().length, 0); 1.435 + do_check_true(coll.timestamp > timestamp); 1.436 + 1.437 + _("On next request the BSO should not exist."); 1.438 + let request = localRequest(server, "/2.0/123/storage/test/myid", 1.439 + "123", "password"); 1.440 + let error = doGetRequest(request); 1.441 + do_check_eq(error, null); 1.442 + do_check_eq(request.response.status, 404); 1.443 + 1.444 + server.stop(run_next_test); 1.445 +}); 1.446 + 1.447 +add_test(function test_bso_delete_unmodified() { 1.448 + _("Ensure X-If-Unmodified-Since works when deleting BSOs."); 1.449 + 1.450 + let server = new StorageServer(); 1.451 + server.startSynchronous(); 1.452 + server.registerUser("123", "password"); 1.453 + let coll = server.user("123").createCollection("test"); 1.454 + let bso = coll.insert("myid", {foo: "bar"}); 1.455 + 1.456 + let modified = bso.modified; 1.457 + 1.458 + _("Issuing a DELETE with an older time should fail."); 1.459 + let path = "/2.0/123/storage/test/myid"; 1.460 + let request = localRequest(server, path, "123", "password"); 1.461 + request.setHeader("X-If-Unmodified-Since", modified - 1000); 1.462 + let error = doDeleteRequest(request); 1.463 + do_check_eq(error, null); 1.464 + do_check_eq(request.response.status, 412); 1.465 + do_check_false("content-type" in request.response.headers); 1.466 + do_check_neq(coll.bso("myid"), null); 1.467 + 1.468 + _("Issuing a DELETE with a newer time should work."); 1.469 + let request = localRequest(server, path, "123", "password"); 1.470 + request.setHeader("X-If-Unmodified-Since", modified + 1000); 1.471 + let error = doDeleteRequest(request); 1.472 + do_check_eq(error, null); 1.473 + do_check_eq(request.response.status, 204); 1.474 + do_check_true(coll.bso("myid").deleted); 1.475 + 1.476 + server.stop(run_next_test); 1.477 +}); 1.478 + 1.479 +add_test(function test_collection_get_unmodified_since() { 1.480 + _("Ensure conditional unmodified get on collection works when it should."); 1.481 + 1.482 + let server = new StorageServer(); 1.483 + server.registerUser("123", "password"); 1.484 + server.startSynchronous(); 1.485 + let collection = server.user("123").createCollection("testcoll"); 1.486 + collection.insert("bso0", {foo: "bar"}); 1.487 + 1.488 + let serverModified = collection.timestamp; 1.489 + 1.490 + let request1 = localRequest(server, "/2.0/123/storage/testcoll", 1.491 + "123", "password"); 1.492 + request1.setHeader("X-If-Unmodified-Since", serverModified); 1.493 + let error = doGetRequest(request1); 1.494 + do_check_null(error); 1.495 + do_check_eq(request1.response.status, 200); 1.496 + 1.497 + let request2 = localRequest(server, "/2.0/123/storage/testcoll", 1.498 + "123", "password"); 1.499 + request2.setHeader("X-If-Unmodified-Since", serverModified - 1); 1.500 + let error = doGetRequest(request2); 1.501 + do_check_null(error); 1.502 + do_check_eq(request2.response.status, 412); 1.503 + 1.504 + server.stop(run_next_test); 1.505 +}); 1.506 + 1.507 +add_test(function test_bso_get_unmodified_since() { 1.508 + _("Ensure conditional unmodified get on BSO works appropriately."); 1.509 + 1.510 + let server = new StorageServer(); 1.511 + server.registerUser("123", "password"); 1.512 + server.startSynchronous(); 1.513 + let collection = server.user("123").createCollection("testcoll"); 1.514 + let bso = collection.insert("bso0", {foo: "bar"}); 1.515 + 1.516 + let serverModified = bso.modified; 1.517 + 1.518 + let request1 = localRequest(server, "/2.0/123/storage/testcoll/bso0", 1.519 + "123", "password"); 1.520 + request1.setHeader("X-If-Unmodified-Since", serverModified); 1.521 + let error = doGetRequest(request1); 1.522 + do_check_null(error); 1.523 + do_check_eq(request1.response.status, 200); 1.524 + 1.525 + let request2 = localRequest(server, "/2.0/123/storage/testcoll/bso0", 1.526 + "123", "password"); 1.527 + request2.setHeader("X-If-Unmodified-Since", serverModified - 1); 1.528 + let error = doGetRequest(request2); 1.529 + do_check_null(error); 1.530 + do_check_eq(request2.response.status, 412); 1.531 + 1.532 + server.stop(run_next_test); 1.533 +}); 1.534 + 1.535 +add_test(function test_missing_collection_404() { 1.536 + _("Ensure a missing collection returns a 404."); 1.537 + 1.538 + let server = new StorageServer(); 1.539 + server.registerUser("123", "password"); 1.540 + server.startSynchronous(); 1.541 + 1.542 + let request = localRequest(server, "/2.0/123/storage/none", "123", "password"); 1.543 + let error = doGetRequest(request); 1.544 + do_check_eq(error, null); 1.545 + do_check_eq(request.response.status, 404); 1.546 + do_check_false("content-type" in request.response.headers); 1.547 + 1.548 + server.stop(run_next_test); 1.549 +}); 1.550 + 1.551 +add_test(function test_get_storage_405() { 1.552 + _("Ensure that a GET on /storage results in a 405."); 1.553 + 1.554 + let server = new StorageServer(); 1.555 + server.registerUser("123", "password"); 1.556 + server.startSynchronous(); 1.557 + 1.558 + let request = localRequest(server, "/2.0/123/storage", "123", "password"); 1.559 + let error = doGetRequest(request); 1.560 + do_check_eq(error, null); 1.561 + do_check_eq(request.response.status, 405); 1.562 + do_check_eq(request.response.headers["allow"], "DELETE"); 1.563 + 1.564 + server.stop(run_next_test); 1.565 +}); 1.566 + 1.567 +add_test(function test_delete_storage() { 1.568 + _("Ensure that deleting all of storage works."); 1.569 + 1.570 + let server = new StorageServer(); 1.571 + server.registerUser("123", "password"); 1.572 + server.createContents("123", { 1.573 + foo: {a: {foo: "bar"}, b: {bar: "foo"}}, 1.574 + baz: {c: {bob: "law"}, blah: {law: "blog"}} 1.575 + }); 1.576 + 1.577 + server.startSynchronous(); 1.578 + 1.579 + let request = localRequest(server, "/2.0/123/storage", "123", "password"); 1.580 + let error = doDeleteRequest(request); 1.581 + do_check_eq(error, null); 1.582 + do_check_eq(request.response.status, 204); 1.583 + do_check_attribute_count(server.users["123"].collections, 0); 1.584 + 1.585 + server.stop(run_next_test); 1.586 +}); 1.587 + 1.588 +add_test(function test_x_num_records() { 1.589 + let server = new StorageServer(); 1.590 + server.registerUser("123", "password"); 1.591 + 1.592 + server.createContents("123", { 1.593 + crypto: {foos: {foo: "bar"}, 1.594 + bars: {foo: "baz"}} 1.595 + }); 1.596 + server.startSynchronous(); 1.597 + let bso = localRequest(server, "/2.0/123/storage/crypto/foos"); 1.598 + bso.get(function (err) { 1.599 + // BSO fetches don't have one. 1.600 + do_check_false("x-num-records" in this.response.headers); 1.601 + let col = localRequest(server, "/2.0/123/storage/crypto"); 1.602 + col.get(function (err) { 1.603 + // Collection fetches do. 1.604 + do_check_eq(this.response.headers["x-num-records"], "2"); 1.605 + server.stop(run_next_test); 1.606 + }); 1.607 + }); 1.608 +}); 1.609 + 1.610 +add_test(function test_put_delete_put() { 1.611 + _("Bug 790397: Ensure BSO deleted flag is reset on PUT."); 1.612 + 1.613 + let server = new StorageServer(); 1.614 + server.registerUser("123", "password"); 1.615 + server.createContents("123", { 1.616 + test: {bso: {foo: "bar"}} 1.617 + }); 1.618 + server.startSynchronous(); 1.619 + 1.620 + _("Ensure we can PUT an existing record."); 1.621 + let request1 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); 1.622 + request1.setHeader("Content-Type", "application/json"); 1.623 + let payload1 = JSON.stringify({"payload": "foobar"}); 1.624 + let error1 = doPutRequest(request1, payload1); 1.625 + do_check_eq(null, error1); 1.626 + do_check_eq(request1.response.status, 204); 1.627 + 1.628 + _("Ensure we can DELETE it."); 1.629 + let request2 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); 1.630 + let error2 = doDeleteRequest(request2); 1.631 + do_check_eq(error2, null); 1.632 + do_check_eq(request2.response.status, 204); 1.633 + do_check_false("content-type" in request2.response.headers); 1.634 + 1.635 + _("Ensure we can PUT a previously deleted record."); 1.636 + let request3 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); 1.637 + request3.setHeader("Content-Type", "application/json"); 1.638 + let payload3 = JSON.stringify({"payload": "foobar"}); 1.639 + let error3 = doPutRequest(request3, payload3); 1.640 + do_check_eq(null, error3); 1.641 + do_check_eq(request3.response.status, 201); 1.642 + 1.643 + _("Ensure we can GET the re-uploaded record."); 1.644 + let request4 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); 1.645 + let error4 = doGetRequest(request4); 1.646 + do_check_eq(error4, null); 1.647 + do_check_eq(request4.response.status, 200); 1.648 + do_check_eq(request4.response.headers["content-type"], "application/json"); 1.649 + 1.650 + server.stop(run_next_test); 1.651 +}); 1.652 + 1.653 +add_test(function test_collection_get_newer() { 1.654 + _("Ensure get with newer argument on collection works."); 1.655 + 1.656 + let server = new StorageServer(); 1.657 + server.registerUser("123", "password"); 1.658 + server.startSynchronous(); 1.659 + 1.660 + let coll = server.user("123").createCollection("test"); 1.661 + let bso1 = coll.insert("001", {foo: "bar"}); 1.662 + let bso2 = coll.insert("002", {bar: "foo"}); 1.663 + 1.664 + // Don't want both records to have the same timestamp. 1.665 + bso2.modified = bso1.modified + 1000; 1.666 + 1.667 + function newerRequest(newer) { 1.668 + return localRequest(server, "/2.0/123/storage/test?newer=" + newer, 1.669 + "123", "password"); 1.670 + } 1.671 + 1.672 + let request1 = newerRequest(0); 1.673 + let error1 = doGetRequest(request1); 1.674 + do_check_null(error1); 1.675 + do_check_eq(request1.response.status, 200); 1.676 + let items1 = JSON.parse(request1.response.body).items; 1.677 + do_check_attribute_count(items1, 2); 1.678 + 1.679 + let request2 = newerRequest(bso1.modified + 1); 1.680 + let error2 = doGetRequest(request2); 1.681 + do_check_null(error2); 1.682 + do_check_eq(request2.response.status, 200); 1.683 + let items2 = JSON.parse(request2.response.body).items; 1.684 + do_check_attribute_count(items2, 1); 1.685 + 1.686 + let request3 = newerRequest(bso2.modified + 1); 1.687 + let error3 = doGetRequest(request3); 1.688 + do_check_null(error3); 1.689 + do_check_eq(request3.response.status, 200); 1.690 + let items3 = JSON.parse(request3.response.body).items; 1.691 + do_check_attribute_count(items3, 0); 1.692 + 1.693 + server.stop(run_next_test); 1.694 +});