From 5f005e7509bbdc432e3ab8216c5a656536737620 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Wed, 12 Oct 2011 13:57:39 -0700 Subject: [PATCH] Bug 693864 - Implement /storage DELETE handling in test JS Sync server. r=philikon --- services/sync/tests/unit/head_http_server.js | 55 +++++++++++++-- .../sync/tests/unit/test_httpd_sync_server.js | 67 +++++++++++++++++-- 2 files changed, 111 insertions(+), 11 deletions(-) diff --git a/services/sync/tests/unit/head_http_server.js b/services/sync/tests/unit/head_http_server.js index 59c3db43d3ae..6e41c5d81fde 100644 --- a/services/sync/tests/unit/head_http_server.js +++ b/services/sync/tests/unit/head_http_server.js @@ -710,6 +710,27 @@ SyncServer.prototype = { return wbo; }, + /** + * Delete all of the collections for the named user. + * + * @param username + * The name of the affected user. + * + * @return a timestamp. + */ + deleteCollections: function deleteCollections(username) { + if (!(username in this.users)) { + throw new Error("Unknown user."); + } + let userCollections = this.users[username].collections; + for each (let [name, coll] in Iterator(userCollections)) { + this._log.trace("Bulk deleting " + name + " for " + username + "..."); + coll.delete({}); + } + this.users[username].collections = {}; + return this.timestamp(); + }, + /** * Simple accessor to allow collective binding and abbreviation of a bunch of * methods. Yay! @@ -727,11 +748,13 @@ SyncServer.prototype = { let modified = function (collectionName) { return collection(collectionName).timestamp; } + let deleteCollections = this.deleteCollections.bind(this, username); return { - collection: collection, - createCollection: createCollection, - createContents: createContents, - modified: modified + collection: collection, + createCollection: createCollection, + createContents: createContents, + deleteCollections: deleteCollections, + modified: modified }; }, @@ -752,9 +775,9 @@ SyncServer.prototype = { * server code. * * Path: [all, version, username, first, rest] - * Storage: [all, collection, id?] + * Storage: [all, collection?, id?] */ - pathRE: /^\/([0-9]+(?:\.[0-9]+)?)\/([-._a-zA-Z0-9]+)\/([^\/]+)\/(.*)$/, + pathRE: /^\/([0-9]+(?:\.[0-9]+)?)\/([-._a-zA-Z0-9]+)(?:\/([^\/]+)(?:\/(.+))?)?$/, storageRE: /^([-_a-zA-Z0-9]+)(?:\/([-_a-zA-Z0-9]+)\/?)?$/, defaultHeaders: {}, @@ -826,6 +849,25 @@ SyncServer.prototype = { */ toplevelHandlers: { "storage": function handleStorage(handler, req, resp, version, username, rest) { + let respond = this.respond.bind(this, req, resp); + if (!rest || !rest.length) { + this._log.debug("SyncServer: top-level storage " + + req.method + " request."); + + // TODO: verify if this is spec-compliant. + if (req.method != "DELETE") { + respond(405, "Method Not Allowed", "[]", {"Allow": "DELETE"}); + return; + } + + // Delete all collections and track the timestamp for the response. + let timestamp = this.user(username).deleteCollections(); + + // Return timestamp and OK for deletion. + respond(200, "OK", JSON.stringify(timestamp)); + return; + } + let match = this.storageRE.exec(rest); if (!match) { this._log.warn("SyncServer: Unknown storage operation " + rest); @@ -833,7 +875,6 @@ SyncServer.prototype = { } let [all, collection, wboID] = match; let coll = this.getCollection(username, collection); - let respond = this.respond.bind(this, req, resp); switch (req.method) { case "GET": if (!coll) { diff --git a/services/sync/tests/unit/test_httpd_sync_server.js b/services/sync/tests/unit/test_httpd_sync_server.js index d95f68029a8b..008d2719de3c 100644 --- a/services/sync/tests/unit/test_httpd_sync_server.js +++ b/services/sync/tests/unit/test_httpd_sync_server.js @@ -1,4 +1,6 @@ function run_test() { + Log4Moz.repository.getLogger("Sync.Test.Server").level = Log4Moz.Level.Trace; + initTestLogging(); run_next_test(); } @@ -16,13 +18,44 @@ add_test(function test_creation() { add_test(function test_url_parsing() { let s = new SyncServer(); + + // Check that we can parse a WBO URI. let parts = s.pathRE.exec("/1.1/johnsmith/storage/crypto/keys"); let [all, version, username, first, rest] = parts; + do_check_eq(all, "/1.1/johnsmith/storage/crypto/keys"); do_check_eq(version, "1.1"); do_check_eq(username, "johnsmith"); do_check_eq(first, "storage"); do_check_eq(rest, "crypto/keys"); do_check_eq(null, s.pathRE.exec("/nothing/else")); + + // Check that we can parse a collection URI. + parts = s.pathRE.exec("/1.1/johnsmith/storage/crypto"); + let [all, version, username, first, rest] = parts; + do_check_eq(all, "/1.1/johnsmith/storage/crypto"); + do_check_eq(version, "1.1"); + do_check_eq(username, "johnsmith"); + do_check_eq(first, "storage"); + do_check_eq(rest, "crypto"); + + // We don't allow trailing slash on storage URI. + parts = s.pathRE.exec("/1.1/johnsmith/storage/"); + do_check_eq(parts, undefined); + + // storage alone is a valid request. + parts = s.pathRE.exec("/1.1/johnsmith/storage"); + let [all, version, username, first, rest] = parts; + do_check_eq(all, "/1.1/johnsmith/storage"); + do_check_eq(version, "1.1"); + do_check_eq(username, "johnsmith"); + do_check_eq(first, "storage"); + do_check_eq(rest, undefined); + + parts = s.storageRE.exec("storage"); + let [all, storage, collection, id] = parts; + do_check_eq(all, "storage"); + do_check_eq(collection, undefined); + run_next_test(); }); @@ -109,6 +142,8 @@ add_test(function test_info_collections() { add_test(function test_storage_request() { let keysURL = "/1.1/john/storage/crypto/keys?foo=bar"; let foosURL = "/1.1/john/storage/crypto/foos"; + let storageURL = "/1.1/john/storage"; + let s = new SyncServer(); let creation = s.timestamp(); s.registerUser("john", "password"); @@ -145,12 +180,36 @@ add_test(function test_storage_request() { Utils.nextTick(next); }); } + function deleteStorage(next) { + _("Testing DELETE on /storage."); + let now = s.timestamp(); + _("Timestamp: " + now); + let req = localRequest(storageURL); + req.delete(function (err) { + _("Body is " + this.response.body); + _("Modified is " + this.response.newModified); + let parsedBody = JSON.parse(this.response.body); + do_check_true(parsedBody >= now); + do_check_empty(s.users["john"].collections); + Utils.nextTick(next); + }); + } + function getStorageFails(next) { + _("Testing that GET on /storage fails."); + let req = localRequest(storageURL); + req.get(function (err) { + do_check_eq(this.response.status, 405); + do_check_eq(this.response.headers["allow"], "DELETE"); + Utils.nextTick(next); + }); + } s.start(8080, function () { retrieveWBONotExists( - retrieveWBOExists.bind(this, function () { - s.stop(run_next_test); - }) - ); + retrieveWBOExists.bind(this, + getStorageFails.bind(this, + deleteStorage.bind(this, function () { + s.stop(run_next_test); + })))); }); });