services/common/tests/unit/test_storage_server.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

     1 /* Any copyright is dedicated to the Public Domain.
     2    http://creativecommons.org/publicdomain/zero/1.0/ */
     4 Cu.import("resource://services-common/async.js");
     5 Cu.import("resource://services-common/rest.js");
     6 Cu.import("resource://services-common/utils.js");
     7 Cu.import("resource://testing-common/services-common/storageserver.js");
     9 const DEFAULT_USER = "123";
    10 const DEFAULT_PASSWORD = "password";
    12 /**
    13  * Helper function to prepare a RESTRequest against the server.
    14  */
    15 function localRequest(server, path, user=DEFAULT_USER, password=DEFAULT_PASSWORD) {
    16   _("localRequest: " + path);
    17   let identity = server.server.identity;
    18   let url = identity.primaryScheme + "://" + identity.primaryHost + ":" +
    19             identity.primaryPort + path;
    20   _("url: " + url);
    21   let req = new RESTRequest(url);
    23   let header = basic_auth_header(user, password);
    24   req.setHeader("Authorization", header);
    25   req.setHeader("Accept", "application/json");
    27   return req;
    28 }
    30 /**
    31  * Helper function to validate an HTTP response from the server.
    32  */
    33 function validateResponse(response) {
    34   do_check_true("x-timestamp" in response.headers);
    36   if ("content-length" in response.headers) {
    37     let cl = parseInt(response.headers["content-length"]);
    39     if (cl != 0) {
    40       do_check_true("content-type" in response.headers);
    41       do_check_eq("application/json", response.headers["content-type"]);
    42     }
    43   }
    45   if (response.status == 204 || response.status == 304) {
    46     do_check_false("content-type" in response.headers);
    48     if ("content-length" in response.headers) {
    49       do_check_eq(response.headers["content-length"], "0");
    50     }
    51   }
    53   if (response.status == 405) {
    54     do_check_true("allow" in response.headers);
    55   }
    56 }
    58 /**
    59  * Helper function to synchronously wait for a response and validate it.
    60  */
    61 function waitAndValidateResponse(cb, request) {
    62   let error = cb.wait();
    64   if (!error) {
    65     validateResponse(request.response);
    66   }
    68   return error;
    69 }
    71 /**
    72  * Helper function to synchronously perform a GET request.
    73  *
    74  * @return Error instance or null if no error.
    75  */
    76 function doGetRequest(request) {
    77   let cb = Async.makeSpinningCallback();
    78   request.get(cb);
    80   return waitAndValidateResponse(cb, request);
    81 }
    83 /**
    84  * Helper function to synchronously perform a PUT request.
    85  *
    86  * @return Error instance or null if no error.
    87  */
    88 function doPutRequest(request, data) {
    89   let cb = Async.makeSpinningCallback();
    90   request.put(data, cb);
    92   return waitAndValidateResponse(cb, request);
    93 }
    95 /**
    96  * Helper function to synchronously perform a DELETE request.
    97  *
    98  * @return Error or null if no error was encountered.
    99  */
   100 function doDeleteRequest(request) {
   101   let cb = Async.makeSpinningCallback();
   102   request.delete(cb);
   104   return waitAndValidateResponse(cb, request);
   105 }
   107 function run_test() {
   108   Log.repository.getLogger("Services.Common.Test.StorageServer").level =
   109     Log.Level.Trace;
   110   initTestLogging();
   112   run_next_test();
   113 }
   115 add_test(function test_creation() {
   116   _("Ensure a simple server can be created.");
   118   // Explicit callback for this one.
   119   let server = new StorageServer({
   120     __proto__: StorageServerCallback,
   121   });
   122   do_check_true(!!server);
   124   server.start(-1, function () {
   125     _("Started on " + server.port);
   126     server.stop(run_next_test);
   127   });
   128 });
   130 add_test(function test_synchronous_start() {
   131   _("Ensure starting using startSynchronous works.");
   133   let server = new StorageServer();
   134   server.startSynchronous();
   135   server.stop(run_next_test);
   136 });
   138 add_test(function test_url_parsing() {
   139   _("Ensure server parses URLs properly.");
   141   let server = new StorageServer();
   143   // Check that we can parse a BSO URI.
   144   let parts = server.pathRE.exec("/2.0/12345/storage/crypto/keys");
   145   let [all, version, user, first, rest] = parts;
   146   do_check_eq(all, "/2.0/12345/storage/crypto/keys");
   147   do_check_eq(version, "2.0");
   148   do_check_eq(user, "12345");
   149   do_check_eq(first, "storage");
   150   do_check_eq(rest, "crypto/keys");
   151   do_check_eq(null, server.pathRE.exec("/nothing/else"));
   153   // Check that we can parse a collection URI.
   154   parts = server.pathRE.exec("/2.0/123/storage/crypto");
   155   let [all, version, user, first, rest] = parts;
   156   do_check_eq(all, "/2.0/123/storage/crypto");
   157   do_check_eq(version, "2.0");
   158   do_check_eq(user, "123");
   159   do_check_eq(first, "storage");
   160   do_check_eq(rest, "crypto");
   162   // We don't allow trailing slash on storage URI.
   163   parts = server.pathRE.exec("/2.0/1234/storage/");
   164   do_check_eq(parts, undefined);
   166   // storage alone is a valid request.
   167   parts = server.pathRE.exec("/2.0/123456/storage");
   168   let [all, version, user, first, rest] = parts;
   169   do_check_eq(all, "/2.0/123456/storage");
   170   do_check_eq(version, "2.0");
   171   do_check_eq(user, "123456");
   172   do_check_eq(first, "storage");
   173   do_check_eq(rest, undefined);
   175   parts = server.storageRE.exec("storage");
   176   let [all, storage, collection, id] = parts;
   177   do_check_eq(all, "storage");
   178   do_check_eq(collection, undefined);
   180   run_next_test();
   181 });
   183 add_test(function test_basic_http() {
   184   let server = new StorageServer();
   185   server.registerUser("345", "password");
   186   do_check_true(server.userExists("345"));
   187   server.startSynchronous();
   189   _("Started on " + server.port);
   190   do_check_eq(server.requestCount, 0);
   191   let req = localRequest(server, "/2.0/storage/crypto/keys");
   192   _("req is " + req);
   193   req.get(function (err) {
   194     do_check_eq(null, err);
   195     do_check_eq(server.requestCount, 1);
   196     server.stop(run_next_test);
   197   });
   198 });
   200 add_test(function test_info_collections() {
   201   let server = new StorageServer();
   202   server.registerUser("123", "password");
   203   server.startSynchronous();
   205   let path = "/2.0/123/info/collections";
   207   _("info/collections on empty server should be empty object.");
   208   let request = localRequest(server, path, "123", "password");
   209   let error = doGetRequest(request);
   210   do_check_eq(error, null);
   211   do_check_eq(request.response.status, 200);
   212   do_check_eq(request.response.body, "{}");
   214   _("Creating an empty collection should result in collection appearing.");
   215   let coll = server.createCollection("123", "col1");
   216   let request = localRequest(server, path, "123", "password");
   217   let error = doGetRequest(request);
   218   do_check_eq(error, null);
   219   do_check_eq(request.response.status, 200);
   220   let info = JSON.parse(request.response.body);
   221   do_check_attribute_count(info, 1);
   222   do_check_true("col1" in info);
   223   do_check_eq(info.col1, coll.timestamp);
   225   server.stop(run_next_test);
   226 });
   228 add_test(function test_bso_get_existing() {
   229   _("Ensure that BSO retrieval works.");
   231   let server = new StorageServer();
   232   server.registerUser("123", "password");
   233   server.createContents("123", {
   234     test: {"bso": {"foo": "bar"}}
   235   });
   236   server.startSynchronous();
   238   let coll = server.user("123").collection("test");
   240   let request = localRequest(server, "/2.0/123/storage/test/bso", "123",
   241                              "password");
   242   let error = doGetRequest(request);
   243   do_check_eq(error, null);
   244   do_check_eq(request.response.status, 200);
   245   do_check_eq(request.response.headers["content-type"], "application/json");
   246   let bso = JSON.parse(request.response.body);
   247   do_check_attribute_count(bso, 3);
   248   do_check_eq(bso.id, "bso");
   249   do_check_eq(bso.modified, coll.bso("bso").modified);
   250   let payload = JSON.parse(bso.payload);
   251   do_check_attribute_count(payload, 1);
   252   do_check_eq(payload.foo, "bar");
   254   server.stop(run_next_test);
   255 });
   257 add_test(function test_percent_decoding() {
   258   _("Ensure query string arguments with percent encoded are handled.");
   260   let server = new StorageServer();
   261   server.registerUser("123", "password");
   262   server.startSynchronous();
   264   let coll = server.user("123").createCollection("test");
   265   coll.insert("001", {foo: "bar"});
   266   coll.insert("002", {bar: "foo"});
   268   let request = localRequest(server, "/2.0/123/storage/test?ids=001%2C002",
   269                              "123", "password");
   270   let error = doGetRequest(request);
   271   do_check_null(error);
   272   do_check_eq(request.response.status, 200);
   273   let items = JSON.parse(request.response.body).items;
   274   do_check_attribute_count(items, 2);
   276   server.stop(run_next_test);
   277 });
   279 add_test(function test_bso_404() {
   280   _("Ensure the server responds with a 404 if a BSO does not exist.");
   282   let server = new StorageServer();
   283   server.registerUser("123", "password");
   284   server.createContents("123", {
   285     test: {}
   286   });
   287   server.startSynchronous();
   289   let request = localRequest(server, "/2.0/123/storage/test/foo");
   290   let error = doGetRequest(request);
   291   do_check_eq(error, null);
   293   do_check_eq(request.response.status, 404);
   294   do_check_false("content-type" in request.response.headers);
   296   server.stop(run_next_test);
   297 });
   299 add_test(function test_bso_if_modified_since_304() {
   300   _("Ensure the server responds properly to X-If-Modified-Since for BSOs.");
   302   let server = new StorageServer();
   303   server.registerUser("123", "password");
   304   server.createContents("123", {
   305     test: {bso: {foo: "bar"}}
   306   });
   307   server.startSynchronous();
   309   let coll = server.user("123").collection("test");
   310   do_check_neq(coll, null);
   312   // Rewind clock just in case.
   313   coll.timestamp -= 10000;
   314   coll.bso("bso").modified -= 10000;
   316   let request = localRequest(server, "/2.0/123/storage/test/bso",
   317                              "123", "password");
   318   request.setHeader("X-If-Modified-Since", "" + server.serverTime());
   319   let error = doGetRequest(request);
   320   do_check_eq(null, error);
   322   do_check_eq(request.response.status, 304);
   323   do_check_false("content-type" in request.response.headers);
   325   let request = localRequest(server, "/2.0/123/storage/test/bso",
   326                              "123", "password");
   327   request.setHeader("X-If-Modified-Since", "" + (server.serverTime() - 20000));
   328   let error = doGetRequest(request);
   329   do_check_eq(null, error);
   330   do_check_eq(request.response.status, 200);
   331   do_check_eq(request.response.headers["content-type"], "application/json");
   333   server.stop(run_next_test);
   334 });
   336 add_test(function test_bso_if_unmodified_since() {
   337   _("Ensure X-If-Unmodified-Since works properly on BSOs.");
   339   let server = new StorageServer();
   340   server.registerUser("123", "password");
   341   server.createContents("123", {
   342     test: {bso: {foo: "bar"}}
   343   });
   344   server.startSynchronous();
   346   let coll = server.user("123").collection("test");
   347   do_check_neq(coll, null);
   349   let time = coll.bso("bso").modified;
   351   _("Ensure we get a 412 for specified times older than server time.");
   352   let request = localRequest(server, "/2.0/123/storage/test/bso",
   353                              "123", "password");
   354   request.setHeader("X-If-Unmodified-Since", time - 5000);
   355   request.setHeader("Content-Type", "application/json");
   356   let payload = JSON.stringify({"payload": "foobar"});
   357   let error = doPutRequest(request, payload);
   358   do_check_eq(null, error);
   359   do_check_eq(request.response.status, 412);
   361   _("Ensure we get a 204 if update goes through.");
   362   let request = localRequest(server, "/2.0/123/storage/test/bso",
   363                              "123", "password");
   364   request.setHeader("Content-Type", "application/json");
   365   request.setHeader("X-If-Unmodified-Since", time + 1);
   366   let error = doPutRequest(request, payload);
   367   do_check_eq(null, error);
   368   do_check_eq(request.response.status, 204);
   369   do_check_true(coll.timestamp > time);
   371   // Not sure why a client would send X-If-Unmodified-Since if a BSO doesn't
   372   // exist. But, why not test it?
   373   _("Ensure we get a 201 if creation goes through.");
   374   let request = localRequest(server, "/2.0/123/storage/test/none",
   375                              "123", "password");
   376   request.setHeader("Content-Type", "application/json");
   377   request.setHeader("X-If-Unmodified-Since", time);
   378   let error = doPutRequest(request, payload);
   379   do_check_eq(null, error);
   380   do_check_eq(request.response.status, 201);
   382   server.stop(run_next_test);
   383 });
   385 add_test(function test_bso_delete_not_exist() {
   386   _("Ensure server behaves properly when deleting a BSO that does not exist.");
   388   let server = new StorageServer();
   389   server.registerUser("123", "password");
   390   server.user("123").createCollection("empty");
   391   server.startSynchronous();
   393   server.callback.onItemDeleted = function onItemDeleted(username, collection,
   394                                                          id) {
   395     do_throw("onItemDeleted should not have been called.");
   396   };
   398   let request = localRequest(server, "/2.0/123/storage/empty/nada",
   399                              "123", "password");
   400   let error = doDeleteRequest(request);
   401   do_check_eq(error, null);
   402   do_check_eq(request.response.status, 404);
   403   do_check_false("content-type" in request.response.headers);
   405   server.stop(run_next_test);
   406 });
   408 add_test(function test_bso_delete_exists() {
   409   _("Ensure proper semantics when deleting a BSO that exists.");
   411   let server = new StorageServer();
   412   server.registerUser("123", "password");
   413   server.startSynchronous();
   415   let coll = server.user("123").createCollection("test");
   416   let bso = coll.insert("myid", {foo: "bar"});
   417   let timestamp = coll.timestamp;
   419   server.callback.onItemDeleted = function onDeleted(username, collection, id) {
   420     delete server.callback.onItemDeleted;
   421     do_check_eq(username, "123");
   422     do_check_eq(collection, "test");
   423     do_check_eq(id, "myid");
   424   };
   426   let request = localRequest(server, "/2.0/123/storage/test/myid",
   427                              "123", "password");
   428   let error = doDeleteRequest(request);
   429   do_check_eq(error, null);
   430   do_check_eq(request.response.status, 204);
   431   do_check_eq(coll.bsos().length, 0);
   432   do_check_true(coll.timestamp > timestamp);
   434   _("On next request the BSO should not exist.");
   435   let request = localRequest(server, "/2.0/123/storage/test/myid",
   436                              "123", "password");
   437   let error = doGetRequest(request);
   438   do_check_eq(error, null);
   439   do_check_eq(request.response.status, 404);
   441   server.stop(run_next_test);
   442 });
   444 add_test(function test_bso_delete_unmodified() {
   445   _("Ensure X-If-Unmodified-Since works when deleting BSOs.");
   447   let server = new StorageServer();
   448   server.startSynchronous();
   449   server.registerUser("123", "password");
   450   let coll = server.user("123").createCollection("test");
   451   let bso = coll.insert("myid", {foo: "bar"});
   453   let modified = bso.modified;
   455   _("Issuing a DELETE with an older time should fail.");
   456   let path = "/2.0/123/storage/test/myid";
   457   let request = localRequest(server, path, "123", "password");
   458   request.setHeader("X-If-Unmodified-Since", modified - 1000);
   459   let error = doDeleteRequest(request);
   460   do_check_eq(error, null);
   461   do_check_eq(request.response.status, 412);
   462   do_check_false("content-type" in request.response.headers);
   463   do_check_neq(coll.bso("myid"), null);
   465   _("Issuing a DELETE with a newer time should work.");
   466   let request = localRequest(server, path, "123", "password");
   467   request.setHeader("X-If-Unmodified-Since", modified + 1000);
   468   let error = doDeleteRequest(request);
   469   do_check_eq(error, null);
   470   do_check_eq(request.response.status, 204);
   471   do_check_true(coll.bso("myid").deleted);
   473   server.stop(run_next_test);
   474 });
   476 add_test(function test_collection_get_unmodified_since() {
   477   _("Ensure conditional unmodified get on collection works when it should.");
   479   let server = new StorageServer();
   480   server.registerUser("123", "password");
   481   server.startSynchronous();
   482   let collection = server.user("123").createCollection("testcoll");
   483   collection.insert("bso0", {foo: "bar"});
   485   let serverModified = collection.timestamp;
   487   let request1 = localRequest(server, "/2.0/123/storage/testcoll",
   488                               "123", "password");
   489   request1.setHeader("X-If-Unmodified-Since", serverModified);
   490   let error = doGetRequest(request1);
   491   do_check_null(error);
   492   do_check_eq(request1.response.status, 200);
   494   let request2 = localRequest(server, "/2.0/123/storage/testcoll",
   495                               "123", "password");
   496   request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
   497   let error = doGetRequest(request2);
   498   do_check_null(error);
   499   do_check_eq(request2.response.status, 412);
   501   server.stop(run_next_test);
   502 });
   504 add_test(function test_bso_get_unmodified_since() {
   505   _("Ensure conditional unmodified get on BSO works appropriately.");
   507   let server = new StorageServer();
   508   server.registerUser("123", "password");
   509   server.startSynchronous();
   510   let collection = server.user("123").createCollection("testcoll");
   511   let bso = collection.insert("bso0", {foo: "bar"});
   513   let serverModified = bso.modified;
   515   let request1 = localRequest(server, "/2.0/123/storage/testcoll/bso0",
   516                               "123", "password");
   517   request1.setHeader("X-If-Unmodified-Since", serverModified);
   518   let error = doGetRequest(request1);
   519   do_check_null(error);
   520   do_check_eq(request1.response.status, 200);
   522   let request2 = localRequest(server, "/2.0/123/storage/testcoll/bso0",
   523                               "123", "password");
   524   request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
   525   let error = doGetRequest(request2);
   526   do_check_null(error);
   527   do_check_eq(request2.response.status, 412);
   529   server.stop(run_next_test);
   530 });
   532 add_test(function test_missing_collection_404() {
   533   _("Ensure a missing collection returns a 404.");
   535   let server = new StorageServer();
   536   server.registerUser("123", "password");
   537   server.startSynchronous();
   539   let request = localRequest(server, "/2.0/123/storage/none", "123", "password");
   540   let error = doGetRequest(request);
   541   do_check_eq(error, null);
   542   do_check_eq(request.response.status, 404);
   543   do_check_false("content-type" in request.response.headers);
   545   server.stop(run_next_test);
   546 });
   548 add_test(function test_get_storage_405() {
   549   _("Ensure that a GET on /storage results in a 405.");
   551   let server = new StorageServer();
   552   server.registerUser("123", "password");
   553   server.startSynchronous();
   555   let request = localRequest(server, "/2.0/123/storage", "123", "password");
   556   let error = doGetRequest(request);
   557   do_check_eq(error, null);
   558   do_check_eq(request.response.status, 405);
   559   do_check_eq(request.response.headers["allow"], "DELETE");
   561   server.stop(run_next_test);
   562 });
   564 add_test(function test_delete_storage() {
   565   _("Ensure that deleting all of storage works.");
   567   let server = new StorageServer();
   568   server.registerUser("123", "password");
   569   server.createContents("123", {
   570     foo: {a: {foo: "bar"}, b: {bar: "foo"}},
   571     baz: {c: {bob: "law"}, blah: {law: "blog"}}
   572   });
   574   server.startSynchronous();
   576   let request = localRequest(server, "/2.0/123/storage", "123", "password");
   577   let error = doDeleteRequest(request);
   578   do_check_eq(error, null);
   579   do_check_eq(request.response.status, 204);
   580   do_check_attribute_count(server.users["123"].collections, 0);
   582   server.stop(run_next_test);
   583 });
   585 add_test(function test_x_num_records() {
   586   let server = new StorageServer();
   587   server.registerUser("123", "password");
   589   server.createContents("123", {
   590     crypto: {foos: {foo: "bar"},
   591              bars: {foo: "baz"}}
   592   });
   593   server.startSynchronous();
   594   let bso = localRequest(server, "/2.0/123/storage/crypto/foos");
   595   bso.get(function (err) {
   596     // BSO fetches don't have one.
   597     do_check_false("x-num-records" in this.response.headers);
   598     let col = localRequest(server, "/2.0/123/storage/crypto");
   599     col.get(function (err) {
   600       // Collection fetches do.
   601       do_check_eq(this.response.headers["x-num-records"], "2");
   602       server.stop(run_next_test);
   603     });
   604   });
   605 });
   607 add_test(function test_put_delete_put() {
   608   _("Bug 790397: Ensure BSO deleted flag is reset on PUT.");
   610   let server = new StorageServer();
   611   server.registerUser("123", "password");
   612   server.createContents("123", {
   613     test: {bso: {foo: "bar"}}
   614   });
   615   server.startSynchronous();
   617   _("Ensure we can PUT an existing record.");
   618   let request1 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   619   request1.setHeader("Content-Type", "application/json");
   620   let payload1 = JSON.stringify({"payload": "foobar"});
   621   let error1 = doPutRequest(request1, payload1);
   622   do_check_eq(null, error1);
   623   do_check_eq(request1.response.status, 204);
   625   _("Ensure we can DELETE it.");
   626   let request2 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   627   let error2 = doDeleteRequest(request2);
   628   do_check_eq(error2, null);
   629   do_check_eq(request2.response.status, 204);
   630   do_check_false("content-type" in request2.response.headers);
   632   _("Ensure we can PUT a previously deleted record.");
   633   let request3 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   634   request3.setHeader("Content-Type", "application/json");
   635   let payload3 = JSON.stringify({"payload": "foobar"});
   636   let error3 = doPutRequest(request3, payload3);
   637   do_check_eq(null, error3);
   638   do_check_eq(request3.response.status, 201);
   640   _("Ensure we can GET the re-uploaded record.");
   641   let request4 = localRequest(server, "/2.0/123/storage/test/bso", "123", "password");
   642   let error4 = doGetRequest(request4);
   643   do_check_eq(error4, null);
   644   do_check_eq(request4.response.status, 200);
   645   do_check_eq(request4.response.headers["content-type"], "application/json");
   647   server.stop(run_next_test);
   648 });
   650 add_test(function test_collection_get_newer() {
   651   _("Ensure get with newer argument on collection works.");
   653   let server = new StorageServer();
   654   server.registerUser("123", "password");
   655   server.startSynchronous();
   657   let coll = server.user("123").createCollection("test");
   658   let bso1 = coll.insert("001", {foo: "bar"});
   659   let bso2 = coll.insert("002", {bar: "foo"});
   661   // Don't want both records to have the same timestamp.
   662   bso2.modified = bso1.modified + 1000;
   664   function newerRequest(newer) {
   665     return localRequest(server, "/2.0/123/storage/test?newer=" + newer,
   666                         "123", "password");
   667   }
   669   let request1 = newerRequest(0);
   670   let error1 = doGetRequest(request1);
   671   do_check_null(error1);
   672   do_check_eq(request1.response.status, 200);
   673   let items1 = JSON.parse(request1.response.body).items;
   674   do_check_attribute_count(items1, 2);
   676   let request2 = newerRequest(bso1.modified + 1);
   677   let error2 = doGetRequest(request2);
   678   do_check_null(error2);
   679   do_check_eq(request2.response.status, 200);
   680   let items2 = JSON.parse(request2.response.body).items;
   681   do_check_attribute_count(items2, 1);
   683   let request3 = newerRequest(bso2.modified + 1);
   684   let error3 = doGetRequest(request3);
   685   do_check_null(error3);
   686   do_check_eq(request3.response.status, 200);
   687   let items3 = JSON.parse(request3.response.body).items;
   688   do_check_attribute_count(items3, 0);
   690   server.stop(run_next_test);
   691 });

mercurial