michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://services-common/async.js"); michael@0: Cu.import("resource://services-common/rest.js"); michael@0: Cu.import("resource://services-common/utils.js"); michael@0: Cu.import("resource://testing-common/services-common/storageserver.js"); michael@0: michael@0: const DEFAULT_USER = "123"; michael@0: const DEFAULT_PASSWORD = "password"; michael@0: michael@0: /** michael@0: * Helper function to prepare a RESTRequest against the server. michael@0: */ michael@0: function localRequest(server, path, user=DEFAULT_USER, password=DEFAULT_PASSWORD) { michael@0: _("localRequest: " + path); michael@0: let identity = server.server.identity; michael@0: let url = identity.primaryScheme + "://" + identity.primaryHost + ":" + michael@0: identity.primaryPort + path; michael@0: _("url: " + url); michael@0: let req = new RESTRequest(url); michael@0: michael@0: let header = basic_auth_header(user, password); michael@0: req.setHeader("Authorization", header); michael@0: req.setHeader("Accept", "application/json"); michael@0: michael@0: return req; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to validate an HTTP response from the server. michael@0: */ michael@0: function validateResponse(response) { michael@0: do_check_true("x-timestamp" in response.headers); michael@0: michael@0: if ("content-length" in response.headers) { michael@0: let cl = parseInt(response.headers["content-length"]); michael@0: michael@0: if (cl != 0) { michael@0: do_check_true("content-type" in response.headers); michael@0: do_check_eq("application/json", response.headers["content-type"]); michael@0: } michael@0: } michael@0: michael@0: if (response.status == 204 || response.status == 304) { michael@0: do_check_false("content-type" in response.headers); michael@0: michael@0: if ("content-length" in response.headers) { michael@0: do_check_eq(response.headers["content-length"], "0"); michael@0: } michael@0: } michael@0: michael@0: if (response.status == 405) { michael@0: do_check_true("allow" in response.headers); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function to synchronously wait for a response and validate it. michael@0: */ michael@0: function waitAndValidateResponse(cb, request) { michael@0: let error = cb.wait(); michael@0: michael@0: if (!error) { michael@0: validateResponse(request.response); michael@0: } michael@0: michael@0: return error; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to synchronously perform a GET request. michael@0: * michael@0: * @return Error instance or null if no error. michael@0: */ michael@0: function doGetRequest(request) { michael@0: let cb = Async.makeSpinningCallback(); michael@0: request.get(cb); michael@0: michael@0: return waitAndValidateResponse(cb, request); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to synchronously perform a PUT request. michael@0: * michael@0: * @return Error instance or null if no error. michael@0: */ michael@0: function doPutRequest(request, data) { michael@0: let cb = Async.makeSpinningCallback(); michael@0: request.put(data, cb); michael@0: michael@0: return waitAndValidateResponse(cb, request); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to synchronously perform a DELETE request. michael@0: * michael@0: * @return Error or null if no error was encountered. michael@0: */ michael@0: function doDeleteRequest(request) { michael@0: let cb = Async.makeSpinningCallback(); michael@0: request.delete(cb); michael@0: michael@0: return waitAndValidateResponse(cb, request); michael@0: } michael@0: michael@0: function run_test() { michael@0: Log.repository.getLogger("Services.Common.Test.StorageServer").level = michael@0: Log.Level.Trace; michael@0: initTestLogging(); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_test(function test_creation() { michael@0: _("Ensure a simple server can be created."); michael@0: michael@0: // Explicit callback for this one. michael@0: let server = new StorageServer({ michael@0: __proto__: StorageServerCallback, michael@0: }); michael@0: do_check_true(!!server); michael@0: michael@0: server.start(-1, function () { michael@0: _("Started on " + server.port); michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_synchronous_start() { michael@0: _("Ensure starting using startSynchronous works."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.startSynchronous(); michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_url_parsing() { michael@0: _("Ensure server parses URLs properly."); michael@0: michael@0: let server = new StorageServer(); michael@0: michael@0: // Check that we can parse a BSO URI. michael@0: let parts = server.pathRE.exec("/2.0/12345/storage/crypto/keys"); michael@0: let [all, version, user, first, rest] = parts; michael@0: do_check_eq(all, "/2.0/12345/storage/crypto/keys"); michael@0: do_check_eq(version, "2.0"); michael@0: do_check_eq(user, "12345"); michael@0: do_check_eq(first, "storage"); michael@0: do_check_eq(rest, "crypto/keys"); michael@0: do_check_eq(null, server.pathRE.exec("/nothing/else")); michael@0: michael@0: // Check that we can parse a collection URI. michael@0: parts = server.pathRE.exec("/2.0/123/storage/crypto"); michael@0: let [all, version, user, first, rest] = parts; michael@0: do_check_eq(all, "/2.0/123/storage/crypto"); michael@0: do_check_eq(version, "2.0"); michael@0: do_check_eq(user, "123"); michael@0: do_check_eq(first, "storage"); michael@0: do_check_eq(rest, "crypto"); michael@0: michael@0: // We don't allow trailing slash on storage URI. michael@0: parts = server.pathRE.exec("/2.0/1234/storage/"); michael@0: do_check_eq(parts, undefined); michael@0: michael@0: // storage alone is a valid request. michael@0: parts = server.pathRE.exec("/2.0/123456/storage"); michael@0: let [all, version, user, first, rest] = parts; michael@0: do_check_eq(all, "/2.0/123456/storage"); michael@0: do_check_eq(version, "2.0"); michael@0: do_check_eq(user, "123456"); michael@0: do_check_eq(first, "storage"); michael@0: do_check_eq(rest, undefined); michael@0: michael@0: parts = server.storageRE.exec("storage"); michael@0: let [all, storage, collection, id] = parts; michael@0: do_check_eq(all, "storage"); michael@0: do_check_eq(collection, undefined); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_test(function test_basic_http() { michael@0: let server = new StorageServer(); michael@0: server.registerUser("345", "password"); michael@0: do_check_true(server.userExists("345")); michael@0: server.startSynchronous(); michael@0: michael@0: _("Started on " + server.port); michael@0: do_check_eq(server.requestCount, 0); michael@0: let req = localRequest(server, "/2.0/storage/crypto/keys"); michael@0: _("req is " + req); michael@0: req.get(function (err) { michael@0: do_check_eq(null, err); michael@0: do_check_eq(server.requestCount, 1); michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_info_collections() { michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let path = "/2.0/123/info/collections"; michael@0: michael@0: _("info/collections on empty server should be empty object."); michael@0: let request = localRequest(server, path, "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 200); michael@0: do_check_eq(request.response.body, "{}"); michael@0: michael@0: _("Creating an empty collection should result in collection appearing."); michael@0: let coll = server.createCollection("123", "col1"); michael@0: let request = localRequest(server, path, "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 200); michael@0: let info = JSON.parse(request.response.body); michael@0: do_check_attribute_count(info, 1); michael@0: do_check_true("col1" in info); michael@0: do_check_eq(info.col1, coll.timestamp); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_get_existing() { michael@0: _("Ensure that BSO retrieval works."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: test: {"bso": {"foo": "bar"}} michael@0: }); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").collection("test"); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test/bso", "123", michael@0: "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 200); michael@0: do_check_eq(request.response.headers["content-type"], "application/json"); michael@0: let bso = JSON.parse(request.response.body); michael@0: do_check_attribute_count(bso, 3); michael@0: do_check_eq(bso.id, "bso"); michael@0: do_check_eq(bso.modified, coll.bso("bso").modified); michael@0: let payload = JSON.parse(bso.payload); michael@0: do_check_attribute_count(payload, 1); michael@0: do_check_eq(payload.foo, "bar"); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_percent_decoding() { michael@0: _("Ensure query string arguments with percent encoded are handled."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").createCollection("test"); michael@0: coll.insert("001", {foo: "bar"}); michael@0: coll.insert("002", {bar: "foo"}); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test?ids=001%2C002", michael@0: "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_null(error); michael@0: do_check_eq(request.response.status, 200); michael@0: let items = JSON.parse(request.response.body).items; michael@0: do_check_attribute_count(items, 2); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_404() { michael@0: _("Ensure the server responds with a 404 if a BSO does not exist."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: test: {} michael@0: }); michael@0: server.startSynchronous(); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test/foo"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: michael@0: do_check_eq(request.response.status, 404); michael@0: do_check_false("content-type" in request.response.headers); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_if_modified_since_304() { michael@0: _("Ensure the server responds properly to X-If-Modified-Since for BSOs."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: test: {bso: {foo: "bar"}} michael@0: }); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").collection("test"); michael@0: do_check_neq(coll, null); michael@0: michael@0: // Rewind clock just in case. michael@0: coll.timestamp -= 10000; michael@0: coll.bso("bso").modified -= 10000; michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test/bso", michael@0: "123", "password"); michael@0: request.setHeader("X-If-Modified-Since", "" + server.serverTime()); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(null, error); michael@0: michael@0: do_check_eq(request.response.status, 304); michael@0: do_check_false("content-type" in request.response.headers); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test/bso", michael@0: "123", "password"); michael@0: request.setHeader("X-If-Modified-Since", "" + (server.serverTime() - 20000)); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(null, error); michael@0: do_check_eq(request.response.status, 200); michael@0: do_check_eq(request.response.headers["content-type"], "application/json"); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_if_unmodified_since() { michael@0: _("Ensure X-If-Unmodified-Since works properly on BSOs."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: test: {bso: {foo: "bar"}} michael@0: }); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").collection("test"); michael@0: do_check_neq(coll, null); michael@0: michael@0: let time = coll.bso("bso").modified; michael@0: michael@0: _("Ensure we get a 412 for specified times older than server time."); michael@0: let request = localRequest(server, "/2.0/123/storage/test/bso", michael@0: "123", "password"); michael@0: request.setHeader("X-If-Unmodified-Since", time - 5000); michael@0: request.setHeader("Content-Type", "application/json"); michael@0: let payload = JSON.stringify({"payload": "foobar"}); michael@0: let error = doPutRequest(request, payload); michael@0: do_check_eq(null, error); michael@0: do_check_eq(request.response.status, 412); michael@0: michael@0: _("Ensure we get a 204 if update goes through."); michael@0: let request = localRequest(server, "/2.0/123/storage/test/bso", michael@0: "123", "password"); michael@0: request.setHeader("Content-Type", "application/json"); michael@0: request.setHeader("X-If-Unmodified-Since", time + 1); michael@0: let error = doPutRequest(request, payload); michael@0: do_check_eq(null, error); michael@0: do_check_eq(request.response.status, 204); michael@0: do_check_true(coll.timestamp > time); michael@0: michael@0: // Not sure why a client would send X-If-Unmodified-Since if a BSO doesn't michael@0: // exist. But, why not test it? michael@0: _("Ensure we get a 201 if creation goes through."); michael@0: let request = localRequest(server, "/2.0/123/storage/test/none", michael@0: "123", "password"); michael@0: request.setHeader("Content-Type", "application/json"); michael@0: request.setHeader("X-If-Unmodified-Since", time); michael@0: let error = doPutRequest(request, payload); michael@0: do_check_eq(null, error); michael@0: do_check_eq(request.response.status, 201); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_delete_not_exist() { michael@0: _("Ensure server behaves properly when deleting a BSO that does not exist."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.user("123").createCollection("empty"); michael@0: server.startSynchronous(); michael@0: michael@0: server.callback.onItemDeleted = function onItemDeleted(username, collection, michael@0: id) { michael@0: do_throw("onItemDeleted should not have been called."); michael@0: }; michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/empty/nada", michael@0: "123", "password"); michael@0: let error = doDeleteRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 404); michael@0: do_check_false("content-type" in request.response.headers); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_delete_exists() { michael@0: _("Ensure proper semantics when deleting a BSO that exists."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").createCollection("test"); michael@0: let bso = coll.insert("myid", {foo: "bar"}); michael@0: let timestamp = coll.timestamp; michael@0: michael@0: server.callback.onItemDeleted = function onDeleted(username, collection, id) { michael@0: delete server.callback.onItemDeleted; michael@0: do_check_eq(username, "123"); michael@0: do_check_eq(collection, "test"); michael@0: do_check_eq(id, "myid"); michael@0: }; michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/test/myid", michael@0: "123", "password"); michael@0: let error = doDeleteRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 204); michael@0: do_check_eq(coll.bsos().length, 0); michael@0: do_check_true(coll.timestamp > timestamp); michael@0: michael@0: _("On next request the BSO should not exist."); michael@0: let request = localRequest(server, "/2.0/123/storage/test/myid", michael@0: "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 404); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_delete_unmodified() { michael@0: _("Ensure X-If-Unmodified-Since works when deleting BSOs."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.startSynchronous(); michael@0: server.registerUser("123", "password"); michael@0: let coll = server.user("123").createCollection("test"); michael@0: let bso = coll.insert("myid", {foo: "bar"}); michael@0: michael@0: let modified = bso.modified; michael@0: michael@0: _("Issuing a DELETE with an older time should fail."); michael@0: let path = "/2.0/123/storage/test/myid"; michael@0: let request = localRequest(server, path, "123", "password"); michael@0: request.setHeader("X-If-Unmodified-Since", modified - 1000); michael@0: let error = doDeleteRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 412); michael@0: do_check_false("content-type" in request.response.headers); michael@0: do_check_neq(coll.bso("myid"), null); michael@0: michael@0: _("Issuing a DELETE with a newer time should work."); michael@0: let request = localRequest(server, path, "123", "password"); michael@0: request.setHeader("X-If-Unmodified-Since", modified + 1000); michael@0: let error = doDeleteRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 204); michael@0: do_check_true(coll.bso("myid").deleted); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_collection_get_unmodified_since() { michael@0: _("Ensure conditional unmodified get on collection works when it should."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: let collection = server.user("123").createCollection("testcoll"); michael@0: collection.insert("bso0", {foo: "bar"}); michael@0: michael@0: let serverModified = collection.timestamp; michael@0: michael@0: let request1 = localRequest(server, "/2.0/123/storage/testcoll", michael@0: "123", "password"); michael@0: request1.setHeader("X-If-Unmodified-Since", serverModified); michael@0: let error = doGetRequest(request1); michael@0: do_check_null(error); michael@0: do_check_eq(request1.response.status, 200); michael@0: michael@0: let request2 = localRequest(server, "/2.0/123/storage/testcoll", michael@0: "123", "password"); michael@0: request2.setHeader("X-If-Unmodified-Since", serverModified - 1); michael@0: let error = doGetRequest(request2); michael@0: do_check_null(error); michael@0: do_check_eq(request2.response.status, 412); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_bso_get_unmodified_since() { michael@0: _("Ensure conditional unmodified get on BSO works appropriately."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: let collection = server.user("123").createCollection("testcoll"); michael@0: let bso = collection.insert("bso0", {foo: "bar"}); michael@0: michael@0: let serverModified = bso.modified; michael@0: michael@0: let request1 = localRequest(server, "/2.0/123/storage/testcoll/bso0", michael@0: "123", "password"); michael@0: request1.setHeader("X-If-Unmodified-Since", serverModified); michael@0: let error = doGetRequest(request1); michael@0: do_check_null(error); michael@0: do_check_eq(request1.response.status, 200); michael@0: michael@0: let request2 = localRequest(server, "/2.0/123/storage/testcoll/bso0", michael@0: "123", "password"); michael@0: request2.setHeader("X-If-Unmodified-Since", serverModified - 1); michael@0: let error = doGetRequest(request2); michael@0: do_check_null(error); michael@0: do_check_eq(request2.response.status, 412); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_missing_collection_404() { michael@0: _("Ensure a missing collection returns a 404."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage/none", "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 404); michael@0: do_check_false("content-type" in request.response.headers); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_get_storage_405() { michael@0: _("Ensure that a GET on /storage results in a 405."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage", "123", "password"); michael@0: let error = doGetRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 405); michael@0: do_check_eq(request.response.headers["allow"], "DELETE"); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_delete_storage() { michael@0: _("Ensure that deleting all of storage works."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: foo: {a: {foo: "bar"}, b: {bar: "foo"}}, michael@0: baz: {c: {bob: "law"}, blah: {law: "blog"}} michael@0: }); michael@0: michael@0: server.startSynchronous(); michael@0: michael@0: let request = localRequest(server, "/2.0/123/storage", "123", "password"); michael@0: let error = doDeleteRequest(request); michael@0: do_check_eq(error, null); michael@0: do_check_eq(request.response.status, 204); michael@0: do_check_attribute_count(server.users["123"].collections, 0); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_x_num_records() { michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: michael@0: server.createContents("123", { michael@0: crypto: {foos: {foo: "bar"}, michael@0: bars: {foo: "baz"}} michael@0: }); michael@0: server.startSynchronous(); michael@0: let bso = localRequest(server, "/2.0/123/storage/crypto/foos"); michael@0: bso.get(function (err) { michael@0: // BSO fetches don't have one. michael@0: do_check_false("x-num-records" in this.response.headers); michael@0: let col = localRequest(server, "/2.0/123/storage/crypto"); michael@0: col.get(function (err) { michael@0: // Collection fetches do. michael@0: do_check_eq(this.response.headers["x-num-records"], "2"); michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_put_delete_put() { michael@0: _("Bug 790397: Ensure BSO deleted flag is reset on PUT."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.createContents("123", { michael@0: test: {bso: {foo: "bar"}} michael@0: }); michael@0: server.startSynchronous(); michael@0: michael@0: _("Ensure we can PUT an existing record."); michael@0: let request1 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); michael@0: request1.setHeader("Content-Type", "application/json"); michael@0: let payload1 = JSON.stringify({"payload": "foobar"}); michael@0: let error1 = doPutRequest(request1, payload1); michael@0: do_check_eq(null, error1); michael@0: do_check_eq(request1.response.status, 204); michael@0: michael@0: _("Ensure we can DELETE it."); michael@0: let request2 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); michael@0: let error2 = doDeleteRequest(request2); michael@0: do_check_eq(error2, null); michael@0: do_check_eq(request2.response.status, 204); michael@0: do_check_false("content-type" in request2.response.headers); michael@0: michael@0: _("Ensure we can PUT a previously deleted record."); michael@0: let request3 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); michael@0: request3.setHeader("Content-Type", "application/json"); michael@0: let payload3 = JSON.stringify({"payload": "foobar"}); michael@0: let error3 = doPutRequest(request3, payload3); michael@0: do_check_eq(null, error3); michael@0: do_check_eq(request3.response.status, 201); michael@0: michael@0: _("Ensure we can GET the re-uploaded record."); michael@0: let request4 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password"); michael@0: let error4 = doGetRequest(request4); michael@0: do_check_eq(error4, null); michael@0: do_check_eq(request4.response.status, 200); michael@0: do_check_eq(request4.response.headers["content-type"], "application/json"); michael@0: michael@0: server.stop(run_next_test); michael@0: }); michael@0: michael@0: add_test(function test_collection_get_newer() { michael@0: _("Ensure get with newer argument on collection works."); michael@0: michael@0: let server = new StorageServer(); michael@0: server.registerUser("123", "password"); michael@0: server.startSynchronous(); michael@0: michael@0: let coll = server.user("123").createCollection("test"); michael@0: let bso1 = coll.insert("001", {foo: "bar"}); michael@0: let bso2 = coll.insert("002", {bar: "foo"}); michael@0: michael@0: // Don't want both records to have the same timestamp. michael@0: bso2.modified = bso1.modified + 1000; michael@0: michael@0: function newerRequest(newer) { michael@0: return localRequest(server, "/2.0/123/storage/test?newer=" + newer, michael@0: "123", "password"); michael@0: } michael@0: michael@0: let request1 = newerRequest(0); michael@0: let error1 = doGetRequest(request1); michael@0: do_check_null(error1); michael@0: do_check_eq(request1.response.status, 200); michael@0: let items1 = JSON.parse(request1.response.body).items; michael@0: do_check_attribute_count(items1, 2); michael@0: michael@0: let request2 = newerRequest(bso1.modified + 1); michael@0: let error2 = doGetRequest(request2); michael@0: do_check_null(error2); michael@0: do_check_eq(request2.response.status, 200); michael@0: let items2 = JSON.parse(request2.response.body).items; michael@0: do_check_attribute_count(items2, 1); michael@0: michael@0: let request3 = newerRequest(bso2.modified + 1); michael@0: let error3 = doGetRequest(request3); michael@0: do_check_null(error3); michael@0: do_check_eq(request3.response.status, 200); michael@0: let items3 = JSON.parse(request3.response.body).items; michael@0: do_check_attribute_count(items3, 0); michael@0: michael@0: server.stop(run_next_test); michael@0: });