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-sync/util.js"); michael@0: michael@0: function run_test() { michael@0: Log.repository.getLogger("Sync.Test.Server").level = Log.Level.Trace; michael@0: initTestLogging(); michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_test(function test_creation() { michael@0: // Explicit callback for this one. michael@0: let server = new SyncServer({ michael@0: __proto__: SyncServerCallback, michael@0: }); michael@0: do_check_true(!!server); // Just so we have a check. michael@0: server.start(null, 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_url_parsing() { michael@0: let server = new SyncServer(); michael@0: michael@0: // Check that we can parse a WBO URI. michael@0: let parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto/keys"); michael@0: let [all, version, username, first, rest] = parts; michael@0: do_check_eq(all, "/1.1/johnsmith/storage/crypto/keys"); michael@0: do_check_eq(version, "1.1"); michael@0: do_check_eq(username, "johnsmith"); 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("/1.1/johnsmith/storage/crypto"); michael@0: let [all, version, username, first, rest] = parts; michael@0: do_check_eq(all, "/1.1/johnsmith/storage/crypto"); michael@0: do_check_eq(version, "1.1"); michael@0: do_check_eq(username, "johnsmith"); 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("/1.1/johnsmith/storage/"); michael@0: do_check_eq(parts, undefined); michael@0: michael@0: // storage alone is a valid request. michael@0: parts = server.pathRE.exec("/1.1/johnsmith/storage"); michael@0: let [all, version, username, first, rest] = parts; michael@0: do_check_eq(all, "/1.1/johnsmith/storage"); michael@0: do_check_eq(version, "1.1"); michael@0: do_check_eq(username, "johnsmith"); 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: Cu.import("resource://services-common/rest.js"); michael@0: function localRequest(server, path) { michael@0: _("localRequest: " + path); michael@0: let url = server.baseURI.substr(0, server.baseURI.length - 1) + path; michael@0: _("url: " + url); michael@0: return new RESTRequest(url); michael@0: } michael@0: michael@0: add_test(function test_basic_http() { michael@0: let server = new SyncServer(); michael@0: server.registerUser("john", "password"); michael@0: do_check_true(server.userExists("john")); michael@0: server.start(null, function () { michael@0: _("Started on " + server.port); michael@0: Utils.nextTick(function () { michael@0: let req = localRequest(server, "/1.1/john/storage/crypto/keys"); michael@0: _("req is " + req); michael@0: req.get(function (err) { michael@0: do_check_eq(null, err); michael@0: Utils.nextTick(function () { michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_info_collections() { michael@0: let server = new SyncServer({ michael@0: __proto__: SyncServerCallback michael@0: }); michael@0: function responseHasCorrectHeaders(r) { michael@0: do_check_eq(r.status, 200); michael@0: do_check_eq(r.headers["content-type"], "application/json"); michael@0: do_check_true("x-weave-timestamp" in r.headers); michael@0: } michael@0: michael@0: server.registerUser("john", "password"); michael@0: server.start(null, function () { michael@0: Utils.nextTick(function () { michael@0: let req = localRequest(server, "/1.1/john/info/collections"); michael@0: req.get(function (err) { michael@0: // Initial info/collections fetch is empty. michael@0: do_check_eq(null, err); michael@0: responseHasCorrectHeaders(this.response); michael@0: michael@0: do_check_eq(this.response.body, "{}"); michael@0: Utils.nextTick(function () { michael@0: // When we PUT something to crypto/keys, "crypto" appears in the response. michael@0: function cb(err) { michael@0: do_check_eq(null, err); michael@0: responseHasCorrectHeaders(this.response); michael@0: let putResponseBody = this.response.body; michael@0: _("PUT response body: " + JSON.stringify(putResponseBody)); michael@0: michael@0: req = localRequest(server, "/1.1/john/info/collections"); michael@0: req.get(function (err) { michael@0: do_check_eq(null, err); michael@0: responseHasCorrectHeaders(this.response); michael@0: let expectedColl = server.getCollection("john", "crypto"); michael@0: do_check_true(!!expectedColl); michael@0: let modified = expectedColl.timestamp; michael@0: do_check_true(modified > 0); michael@0: do_check_eq(putResponseBody, modified); michael@0: do_check_eq(JSON.parse(this.response.body).crypto, modified); michael@0: Utils.nextTick(function () { michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: } michael@0: let payload = JSON.stringify({foo: "bar"}); michael@0: localRequest(server, "/1.1/john/storage/crypto/keys").put(payload, cb); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_storage_request() { michael@0: let keysURL = "/1.1/john/storage/crypto/keys?foo=bar"; michael@0: let foosURL = "/1.1/john/storage/crypto/foos"; michael@0: let storageURL = "/1.1/john/storage"; michael@0: michael@0: let server = new SyncServer(); michael@0: let creation = server.timestamp(); michael@0: server.registerUser("john", "password"); michael@0: michael@0: server.createContents("john", { michael@0: crypto: {foos: {foo: "bar"}} michael@0: }); michael@0: let coll = server.user("john").collection("crypto"); michael@0: do_check_true(!!coll); michael@0: michael@0: _("We're tracking timestamps."); michael@0: do_check_true(coll.timestamp >= creation); michael@0: michael@0: function retrieveWBONotExists(next) { michael@0: let req = localRequest(server, keysURL); michael@0: req.get(function (err) { michael@0: _("Body is " + this.response.body); michael@0: _("Modified is " + this.response.newModified); michael@0: do_check_eq(null, err); michael@0: do_check_eq(this.response.status, 404); michael@0: do_check_eq(this.response.body, "Not found"); michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: function retrieveWBOExists(next) { michael@0: let req = localRequest(server, foosURL); michael@0: req.get(function (err) { michael@0: _("Body is " + this.response.body); michael@0: _("Modified is " + this.response.newModified); michael@0: let parsedBody = JSON.parse(this.response.body); michael@0: do_check_eq(parsedBody.id, "foos"); michael@0: do_check_eq(parsedBody.modified, coll.wbo("foos").modified); michael@0: do_check_eq(JSON.parse(parsedBody.payload).foo, "bar"); michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: function deleteWBONotExists(next) { michael@0: let req = localRequest(server, keysURL); michael@0: server.callback.onItemDeleted = function (username, collection, wboID) { michael@0: do_throw("onItemDeleted should not have been called."); michael@0: }; michael@0: michael@0: req.delete(function (err) { michael@0: _("Body is " + this.response.body); michael@0: _("Modified is " + this.response.newModified); michael@0: do_check_eq(this.response.status, 200); michael@0: delete server.callback.onItemDeleted; michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: function deleteWBOExists(next) { michael@0: let req = localRequest(server, foosURL); michael@0: server.callback.onItemDeleted = function (username, collection, wboID) { michael@0: _("onItemDeleted called for " + collection + "/" + wboID); michael@0: delete server.callback.onItemDeleted; michael@0: do_check_eq(username, "john"); michael@0: do_check_eq(collection, "crypto"); michael@0: do_check_eq(wboID, "foos"); michael@0: Utils.nextTick(next); michael@0: }; michael@0: michael@0: req.delete(function (err) { michael@0: _("Body is " + this.response.body); michael@0: _("Modified is " + this.response.newModified); michael@0: do_check_eq(this.response.status, 200); michael@0: }); michael@0: } michael@0: function deleteStorage(next) { michael@0: _("Testing DELETE on /storage."); michael@0: let now = server.timestamp(); michael@0: _("Timestamp: " + now); michael@0: let req = localRequest(server, storageURL); michael@0: req.delete(function (err) { michael@0: _("Body is " + this.response.body); michael@0: _("Modified is " + this.response.newModified); michael@0: let parsedBody = JSON.parse(this.response.body); michael@0: do_check_true(parsedBody >= now); michael@0: do_check_empty(server.users["john"].collections); michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: function getStorageFails(next) { michael@0: _("Testing that GET on /storage fails."); michael@0: let req = localRequest(server, storageURL); michael@0: req.get(function (err) { michael@0: do_check_eq(this.response.status, 405); michael@0: do_check_eq(this.response.headers["allow"], "DELETE"); michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: function getMissingCollectionWBO(next) { michael@0: _("Testing that fetching a WBO from an on-existent collection 404s."); michael@0: let req = localRequest(server, storageURL + "/foobar/baz"); michael@0: req.get(function (err) { michael@0: do_check_eq(this.response.status, 404); michael@0: Utils.nextTick(next); michael@0: }); michael@0: } michael@0: michael@0: server.start(null, michael@0: Async.chain( michael@0: retrieveWBONotExists, michael@0: retrieveWBOExists, michael@0: deleteWBOExists, michael@0: deleteWBONotExists, michael@0: getStorageFails, michael@0: getMissingCollectionWBO, michael@0: deleteStorage, michael@0: server.stop.bind(server), michael@0: run_next_test michael@0: )); michael@0: }); michael@0: michael@0: add_test(function test_x_weave_records() { michael@0: let server = new SyncServer(); michael@0: server.registerUser("john", "password"); michael@0: michael@0: server.createContents("john", { michael@0: crypto: {foos: {foo: "bar"}, michael@0: bars: {foo: "baz"}} michael@0: }); michael@0: server.start(null, function () { michael@0: let wbo = localRequest(server, "/1.1/john/storage/crypto/foos"); michael@0: wbo.get(function (err) { michael@0: // WBO fetches don't have one. michael@0: do_check_false("x-weave-records" in this.response.headers); michael@0: let col = localRequest(server, "/1.1/john/storage/crypto"); michael@0: col.get(function (err) { michael@0: // Collection fetches do. michael@0: do_check_eq(this.response.headers["x-weave-records"], "2"); michael@0: server.stop(run_next_test); michael@0: }); michael@0: }); michael@0: }); michael@0: });