services/sync/tests/unit/test_resource.js

Wed, 31 Dec 2014 07:22:50 +0100

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

Correct previous dual key logic pending first delivery installment.

     1 /* Any copyright is dedicated to the Public Domain.
     2  * http://creativecommons.org/publicdomain/zero/1.0/ */
     4 Cu.import("resource://gre/modules/Log.jsm");
     5 Cu.import("resource://services-common/observers.js");
     6 Cu.import("resource://services-sync/identity.js");
     7 Cu.import("resource://services-sync/resource.js");
     8 Cu.import("resource://services-sync/util.js");
    10 let logger;
    12 let fetched = false;
    13 function server_open(metadata, response) {
    14   let body;
    15   if (metadata.method == "GET") {
    16     fetched = true;
    17     body = "This path exists";
    18     response.setStatusLine(metadata.httpVersion, 200, "OK");
    19   } else {
    20     body = "Wrong request method";
    21     response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
    22   }
    23   response.bodyOutputStream.write(body, body.length);
    24 }
    26 function server_protected(metadata, response) {
    27   let body;
    29   if (basic_auth_matches(metadata, "guest", "guest")) {
    30     body = "This path exists and is protected";
    31     response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
    32     response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
    33   } else {
    34     body = "This path exists and is protected - failed";
    35     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
    36     response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
    37   }
    39   response.bodyOutputStream.write(body, body.length);
    40 }
    42 function server_404(metadata, response) {
    43   let body = "File not found";
    44   response.setStatusLine(metadata.httpVersion, 404, "Not Found");
    45   response.bodyOutputStream.write(body, body.length);
    46 }
    48 let pacFetched = false;
    49 function server_pac(metadata, response) {
    50   pacFetched = true;
    51   let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
    52   response.setStatusLine(metadata.httpVersion, 200, "OK");
    53   response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
    54   response.bodyOutputStream.write(body, body.length);
    55 }
    58 let sample_data = {
    59   some: "sample_data",
    60   injson: "format",
    61   number: 42
    62 };
    64 function server_upload(metadata, response) {
    65   let body;
    67   let input = readBytesFromInputStream(metadata.bodyInputStream);
    68   if (input == JSON.stringify(sample_data)) {
    69     body = "Valid data upload via " + metadata.method;
    70     response.setStatusLine(metadata.httpVersion, 200, "OK");
    71   } else {
    72     body = "Invalid data upload via " + metadata.method + ': ' + input;
    73     response.setStatusLine(metadata.httpVersion, 500, "Internal Server Error");
    74   }
    76   response.bodyOutputStream.write(body, body.length);
    77 }
    79 function server_delete(metadata, response) {
    80   let body;
    81   if (metadata.method == "DELETE") {
    82     body = "This resource has been deleted";
    83     response.setStatusLine(metadata.httpVersion, 200, "OK");
    84   } else {
    85     body = "Wrong request method";
    86     response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
    87   }
    88   response.bodyOutputStream.write(body, body.length);
    89 }
    91 function server_json(metadata, response) {
    92   let body = JSON.stringify(sample_data);
    93   response.setStatusLine(metadata.httpVersion, 200, "OK");
    94   response.bodyOutputStream.write(body, body.length);
    95 }
    97 const TIMESTAMP = 1274380461;
    99 function server_timestamp(metadata, response) {
   100   let body = "Thank you for your request";
   101   response.setHeader("X-Weave-Timestamp", ''+TIMESTAMP, false);
   102   response.setStatusLine(metadata.httpVersion, 200, "OK");
   103   response.bodyOutputStream.write(body, body.length);
   104 }
   106 function server_backoff(metadata, response) {
   107   let body = "Hey, back off!";
   108   response.setHeader("X-Weave-Backoff", '600', false);
   109   response.setStatusLine(metadata.httpVersion, 200, "OK");
   110   response.bodyOutputStream.write(body, body.length);
   111 }
   113 function server_quota_notice(request, response) {
   114   let body = "You're approaching quota.";
   115   response.setHeader("X-Weave-Quota-Remaining", '1048576', false);
   116   response.setStatusLine(request.httpVersion, 200, "OK");
   117   response.bodyOutputStream.write(body, body.length);
   118 }
   120 function server_quota_error(request, response) {
   121   let body = "14";
   122   response.setHeader("X-Weave-Quota-Remaining", '-1024', false);
   123   response.setStatusLine(request.httpVersion, 400, "OK");
   124   response.bodyOutputStream.write(body, body.length);
   125 }
   127 function server_headers(metadata, response) {
   128   let ignore_headers = ["host", "user-agent", "accept", "accept-language",
   129                         "accept-encoding", "accept-charset", "keep-alive",
   130                         "connection", "pragma", "cache-control",
   131                         "content-length"];
   132   let headers = metadata.headers;
   133   let header_names = [];
   134   while (headers.hasMoreElements()) {
   135     let header = headers.getNext().toString();
   136     if (ignore_headers.indexOf(header) == -1) {
   137       header_names.push(header);
   138     }
   139   }
   140   header_names = header_names.sort();
   142   headers = {};
   143   for each (let header in header_names) {
   144     headers[header] = metadata.getHeader(header);
   145   }
   146   let body = JSON.stringify(headers);
   147   response.setStatusLine(metadata.httpVersion, 200, "OK");
   148   response.bodyOutputStream.write(body, body.length);
   149 }
   151 function run_test() {
   152   initTestLogging("Trace");
   154   do_test_pending();
   156   logger = Log.repository.getLogger('Test');
   157   Log.repository.rootLogger.addAppender(new Log.DumpAppender());
   159   let server = httpd_setup({
   160     "/open": server_open,
   161     "/protected": server_protected,
   162     "/404": server_404,
   163     "/upload": server_upload,
   164     "/delete": server_delete,
   165     "/json": server_json,
   166     "/timestamp": server_timestamp,
   167     "/headers": server_headers,
   168     "/backoff": server_backoff,
   169     "/pac1": server_pac,
   170     "/quota-notice": server_quota_notice,
   171     "/quota-error": server_quota_error
   172   });
   174   Svc.Prefs.set("network.numRetries", 1); // speed up test
   176   // This apparently has to come first in order for our PAC URL to be hit.
   177   // Don't put any other HTTP requests earlier in the file!
   178   _("Testing handling of proxy auth redirection.");
   179   PACSystemSettings.PACURI = server.baseURI + "/pac1";
   180   installFakePAC();
   181   let proxiedRes = new Resource(server.baseURI + "/open");
   182   let content = proxiedRes.get();
   183   do_check_true(pacFetched);
   184   do_check_true(fetched);
   185   do_check_eq(content, "This path exists");
   186   pacFetched = fetched = false;
   187   uninstallFakePAC();
   189   _("Resource object members");
   190   let res = new Resource(server.baseURI + "/open");
   191   do_check_true(res.uri instanceof Ci.nsIURI);
   192   do_check_eq(res.uri.spec, server.baseURI + "/open");
   193   do_check_eq(res.spec, server.baseURI + "/open");
   194   do_check_eq(typeof res.headers, "object");
   195   do_check_eq(typeof res.authenticator, "object");
   196   // Initially res.data is null since we haven't performed a GET or
   197   // PUT/POST request yet.
   198   do_check_eq(res.data, null);
   200   _("GET a non-password-protected resource");
   201   content = res.get();
   202   do_check_eq(content, "This path exists");
   203   do_check_eq(content.status, 200);
   204   do_check_true(content.success);
   205   // res.data has been updated with the result from the request
   206   do_check_eq(res.data, content);
   208   // Observe logging messages.
   209   let logger = res._log;
   210   let dbg    = logger.debug;
   211   let debugMessages = [];
   212   logger.debug = function (msg) {
   213     debugMessages.push(msg);
   214     dbg.call(this, msg);
   215   }
   217   // Since we didn't receive proper JSON data, accessing content.obj
   218   // will result in a SyntaxError from JSON.parse.
   219   // Furthermore, we'll have logged.
   220   let didThrow = false;
   221   try {
   222     content.obj;
   223   } catch (ex) {
   224     didThrow = true;
   225   }
   226   do_check_true(didThrow);
   227   do_check_eq(debugMessages.length, 1);
   228   do_check_eq(debugMessages[0],
   229               "Parse fail: Response body starts: \"\"This path exists\"\".");
   230   logger.debug = dbg;
   232   _("Test that the BasicAuthenticator doesn't screw up header case.");
   233   let res1 = new Resource(server.baseURI + "/foo");
   234   res1.setHeader("Authorization", "Basic foobar");
   235   do_check_eq(res1.headers["authorization"], "Basic foobar");
   237   _("GET a password protected resource (test that it'll fail w/o pass, no throw)");
   238   let res2 = new Resource(server.baseURI + "/protected");
   239   content = res2.get();
   240   do_check_eq(content, "This path exists and is protected - failed");
   241   do_check_eq(content.status, 401);
   242   do_check_false(content.success);
   244   _("GET a password protected resource");
   245   let res3 = new Resource(server.baseURI + "/protected");
   246   let identity = new IdentityManager();
   247   let auth = identity.getBasicResourceAuthenticator("guest", "guest");
   248   res3.authenticator = auth;
   249   do_check_eq(res3.authenticator, auth);
   250   content = res3.get();
   251   do_check_eq(content, "This path exists and is protected");
   252   do_check_eq(content.status, 200);
   253   do_check_true(content.success);
   255   _("GET a non-existent resource (test that it'll fail, but not throw)");
   256   let res4 = new Resource(server.baseURI + "/404");
   257   content = res4.get();
   258   do_check_eq(content, "File not found");
   259   do_check_eq(content.status, 404);
   260   do_check_false(content.success);
   262   // Check some headers of the 404 response
   263   do_check_eq(content.headers.connection, "close");
   264   do_check_eq(content.headers.server, "httpd.js");
   265   do_check_eq(content.headers["content-length"], 14);
   267   _("PUT to a resource (string)");
   268   let res5 = new Resource(server.baseURI + "/upload");
   269   content = res5.put(JSON.stringify(sample_data));
   270   do_check_eq(content, "Valid data upload via PUT");
   271   do_check_eq(content.status, 200);
   272   do_check_eq(res5.data, content);
   274   _("PUT to a resource (object)");
   275   content = res5.put(sample_data);
   276   do_check_eq(content, "Valid data upload via PUT");
   277   do_check_eq(content.status, 200);
   278   do_check_eq(res5.data, content);
   280   _("PUT without data arg (uses resource.data) (string)");
   281   res5.data = JSON.stringify(sample_data);
   282   content = res5.put();
   283   do_check_eq(content, "Valid data upload via PUT");
   284   do_check_eq(content.status, 200);
   285   do_check_eq(res5.data, content);
   287   _("PUT without data arg (uses resource.data) (object)");
   288   res5.data = sample_data;
   289   content = res5.put();
   290   do_check_eq(content, "Valid data upload via PUT");
   291   do_check_eq(content.status, 200);
   292   do_check_eq(res5.data, content);
   294   _("POST to a resource (string)");
   295   content = res5.post(JSON.stringify(sample_data));
   296   do_check_eq(content, "Valid data upload via POST");
   297   do_check_eq(content.status, 200);
   298   do_check_eq(res5.data, content);
   300   _("POST to a resource (object)");
   301   content = res5.post(sample_data);
   302   do_check_eq(content, "Valid data upload via POST");
   303   do_check_eq(content.status, 200);
   304   do_check_eq(res5.data, content);
   306   _("POST without data arg (uses resource.data) (string)");
   307   res5.data = JSON.stringify(sample_data);
   308   content = res5.post();
   309   do_check_eq(content, "Valid data upload via POST");
   310   do_check_eq(content.status, 200);
   311   do_check_eq(res5.data, content);
   313   _("POST without data arg (uses resource.data) (object)");
   314   res5.data = sample_data;
   315   content = res5.post();
   316   do_check_eq(content, "Valid data upload via POST");
   317   do_check_eq(content.status, 200);
   318   do_check_eq(res5.data, content);
   320   _("DELETE a resource");
   321   let res6 = new Resource(server.baseURI + "/delete");
   322   content = res6.delete();
   323   do_check_eq(content, "This resource has been deleted")
   324   do_check_eq(content.status, 200);
   326   _("JSON conversion of response body");
   327   let res7 = new Resource(server.baseURI + "/json");
   328   content = res7.get();
   329   do_check_eq(content, JSON.stringify(sample_data));
   330   do_check_eq(content.status, 200);
   331   do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
   333   _("X-Weave-Timestamp header updates AsyncResource.serverTime");
   334   // Before having received any response containing the
   335   // X-Weave-Timestamp header, AsyncResource.serverTime is null.
   336   do_check_eq(AsyncResource.serverTime, null);
   337   let res8 = new Resource(server.baseURI + "/timestamp");
   338   content = res8.get();
   339   do_check_eq(AsyncResource.serverTime, TIMESTAMP);
   341   _("GET: no special request headers");
   342   let res9 = new Resource(server.baseURI + "/headers");
   343   content = res9.get();
   344   do_check_eq(content, '{}');
   346   _("PUT: Content-Type defaults to text/plain");
   347   content = res9.put('data');
   348   do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
   350   _("POST: Content-Type defaults to text/plain");
   351   content = res9.post('data');
   352   do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
   354   _("setHeader(): setting simple header");
   355   res9.setHeader('X-What-Is-Weave', 'awesome');
   356   do_check_eq(res9.headers['x-what-is-weave'], 'awesome');
   357   content = res9.get();
   358   do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
   360   _("setHeader(): setting multiple headers, overwriting existing header");
   361   res9.setHeader('X-WHAT-is-Weave', 'more awesomer');
   362   res9.setHeader('X-Another-Header', 'hello world');
   363   do_check_eq(res9.headers['x-what-is-weave'], 'more awesomer');
   364   do_check_eq(res9.headers['x-another-header'], 'hello world');
   365   content = res9.get();
   366   do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
   367                                        "x-what-is-weave": "more awesomer"}));
   369   _("Setting headers object");
   370   res9.headers = {};
   371   content = res9.get();
   372   do_check_eq(content, "{}");
   374   _("PUT/POST: override default Content-Type");
   375   res9.setHeader('Content-Type', 'application/foobar');
   376   do_check_eq(res9.headers['content-type'], 'application/foobar');
   377   content = res9.put('data');
   378   do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
   379   content = res9.post('data');
   380   do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
   383   _("X-Weave-Backoff header notifies observer");
   384   let backoffInterval;
   385   function onBackoff(subject, data) {
   386     backoffInterval = subject;
   387   }
   388   Observers.add("weave:service:backoff:interval", onBackoff);
   390   let res10 = new Resource(server.baseURI + "/backoff");
   391   content = res10.get();
   392   do_check_eq(backoffInterval, 600);
   395   _("X-Weave-Quota-Remaining header notifies observer on successful requests.");
   396   let quotaValue;
   397   function onQuota(subject, data) {
   398     quotaValue = subject;
   399   }
   400   Observers.add("weave:service:quota:remaining", onQuota);
   402   res10 = new Resource(server.baseURI + "/quota-error");
   403   content = res10.get();
   404   do_check_eq(content.status, 400);
   405   do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
   407   res10 = new Resource(server.baseURI + "/quota-notice");
   408   content = res10.get();
   409   do_check_eq(content.status, 200);
   410   do_check_eq(quotaValue, 1048576);
   413   _("Error handling in _request() preserves exception information");
   414   let error;
   415   let res11 = new Resource("http://localhost:12345/does/not/exist");
   416   try {
   417     content = res11.get();
   418   } catch(ex) {
   419     error = ex;
   420   }
   421   do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
   422   do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
   423   do_check_eq(typeof error.stack, "string");
   425   _("Checking handling of errors in onProgress.");
   426   let res18 = new Resource(server.baseURI + "/json");
   427   let onProgress = function(rec) {
   428     // Provoke an XPC exception without a Javascript wrapper.
   429     Services.io.newURI("::::::::", null, null);
   430   };
   431   res18._onProgress = onProgress;
   432   let oldWarn = res18._log.warn;
   433   let warnings = [];
   434   res18._log.warn = function(msg) { warnings.push(msg) };
   435   error = undefined;
   436   try {
   437     content = res18.get();
   438   } catch (ex) {
   439     error = ex;
   440   }
   442   // It throws and logs.
   443   do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
   444   do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
   445   do_check_eq(warnings.pop(),
   446               "Got exception calling onProgress handler during fetch of " +
   447               server.baseURI + "/json");
   449   // And this is what happens if JS throws an exception.
   450   res18 = new Resource(server.baseURI + "/json");
   451   onProgress = function(rec) {
   452     throw "BOO!";
   453   };
   454   res18._onProgress = onProgress;
   455   oldWarn = res18._log.warn;
   456   warnings = [];
   457   res18._log.warn = function(msg) { warnings.push(msg) };
   458   error = undefined;
   459   try {
   460     content = res18.get();
   461   } catch (ex) {
   462     error = ex;
   463   }
   465   // It throws and logs.
   466   do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
   467   do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
   468   do_check_eq(warnings.pop(),
   469               "Got exception calling onProgress handler during fetch of " +
   470               server.baseURI + "/json");
   473   _("Ensure channel timeouts are thrown appropriately.");
   474   let res19 = new Resource(server.baseURI + "/json");
   475   res19.ABORT_TIMEOUT = 0;
   476   error = undefined;
   477   try {
   478     content = res19.get();
   479   } catch (ex) {
   480     error = ex;
   481   }
   482   do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
   484   _("Testing URI construction.");
   485   let args = [];
   486   args.push("newer=" + 1234);
   487   args.push("limit=" + 1234);
   488   args.push("sort=" + 1234);
   490   let query = "?" + args.join("&");
   492   let uri1 = Utils.makeURI("http://foo/" + query)
   493                   .QueryInterface(Ci.nsIURL);
   494   let uri2 = Utils.makeURI("http://foo/")
   495                   .QueryInterface(Ci.nsIURL);
   496   uri2.query = query;
   497   do_check_eq(uri1.query, uri2.query);
   498   server.stop(do_test_finished);
   499 }

mercurial