add http js server for unit tests; add tests for resources, auth manager, base weave record types (WBOs, keys, crypto wrappers/crypto meta)
authorDan Mills <thunder@mozilla.com>
Mon, 03 Nov 2008 14:41:39 -0800
changeset 45012 081fe4edf89a10fdb809fcedb91dd6764b20205e
parent 45011 ea4d8328573e804206e84e9a2783bb74a24d4baa
child 45013 75719bf2e5c1a0c5b38d189f228d12817a152c6e
push idunknown
push userunknown
push dateunknown
add http js server for unit tests; add tests for resources, auth manager, base weave record types (WBOs, keys, crypto wrappers/crypto meta)
services/sync/tests/unit/Makefile
services/sync/tests/unit/head_first.js
services/sync/tests/unit/head_http_server.js
services/sync/tests/unit/test_auth_manager.js
services/sync/tests/unit/test_fault_tolerance.js
services/sync/tests/unit/test_log4moz.js
services/sync/tests/unit/test_records_crypto.js
services/sync/tests/unit/test_records_keys.js
services/sync/tests/unit/test_records_wbo.js
services/sync/tests/unit/test_resource.js
--- a/services/sync/tests/unit/Makefile
+++ b/services/sync/tests/unit/Makefile
@@ -1,2 +1,1 @@
-all:
-	${MAKE} -f ../harness/Makefile
+include ../harness/Makefile
--- a/services/sync/tests/unit/head_first.js
+++ b/services/sync/tests/unit/head_first.js
@@ -27,16 +27,17 @@ let provider = {
       return this;
     }
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 };
 ds.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
 
 do_bind_resource(do_get_file("modules"), "weave");
+do_bind_resource(do_get_file("tests"), "tests");
 
 function loadInSandbox(aUri) {
   var sandbox = Components.utils.Sandbox(this);
   var request = Components.
                 classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
                 createInstance();
 
   request.open("GET", aUri, false);
@@ -69,17 +70,17 @@ function FakeTimerService() {
       return false;
     }
   };
 
   Utils.makeTimerForCall = self.makeTimerForCall;
 };
 
 function getTestLogger(component) {
-  return Log4Moz.Service.getLogger("Testing");
+  return Log4Moz.repository.getLogger("Testing");
 }
 
 function initTestLogging(level) {
   Cu.import("resource://weave/log4moz.js");
 
   function LogStats() {
     this.errorsLogged = 0;
   }
@@ -88,17 +89,17 @@ function initTestLogging(level) {
       if (message.level == Log4Moz.Level.Error)
         this.errorsLogged += 1;
       return message.loggerName + "\t" + message.levelDesc + "\t" +
         message.message + "\n";
     }
   };
   LogStats.prototype.__proto__ = new Log4Moz.Formatter();
 
-  var log = Log4Moz.Service.rootLogger;
+  var log = Log4Moz.repository.rootLogger;
   var logStats = new LogStats();
   var appender = new Log4Moz.DumpAppender(logStats);
 
   if (typeof(level) == "undefined")
     level = "Debug";
   getTestLogger().level = Log4Moz.Level[level];
 
   log.level = Log4Moz.Level.Trace;
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/head_http_server.js
@@ -0,0 +1,25 @@
+let Httpd = {};
+Cu.import("resource://tests/lib/httpd.js", Httpd);
+
+function httpd_setup (handlers) {
+  let server = new Httpd.nsHttpServer();
+  for (let path in handlers) {
+    server.registerPathHandler(path, handlers[path]);
+  }
+  server.start(8080);
+  return server;
+}
+
+function httpd_basic_auth_handler(body, metadata, response) {
+  // no btoa() in xpcshell.  it's guest:guest
+  if (metadata.hasHeader("Authorization") &&
+      metadata.getHeader("Authorization") == "Basic Z3Vlc3Q6Z3Vlc3Q=") {
+    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+  } else {
+    body = "This path exists and is protected - failed";
+    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+  }
+  response.bodyOutputStream.write(body, body.length);
+}
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_auth_manager.js
@@ -0,0 +1,59 @@
+Cu.import("resource://weave/log4moz.js");
+Cu.import("resource://weave/util.js");
+Cu.import("resource://weave/async.js");
+Cu.import("resource://weave/auth.js");
+Cu.import("resource://weave/identity.js");
+Cu.import("resource://weave/resource.js");
+
+Function.prototype.async = Async.sugar;
+
+let logger;
+let Httpd = {};
+Cu.import("resource://tests/lib/httpd.js", Httpd);
+
+function server_handler(metadata, response) {
+  let body;
+
+  // no btoa() in xpcshell.  it's guest:guest
+  if (metadata.hasHeader("Authorization") &&
+      metadata.getHeader("Authorization") == "Basic Z3Vlc3Q6Z3Vlc3Q=") {
+    body = "This path exists and is protected";
+    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+
+  } else {
+    body = "This path exists and is protected - failed";
+    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+  }
+
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function async_test() {
+  let self = yield;
+
+  logger = Log4Moz.repository.getLogger('Test');
+  Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
+
+  let server = new Httpd.nsHttpServer();
+  server.registerPathHandler("/foo", server_handler);
+  server.start(8080);
+
+  let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
+  Auth.defaultAuthenticator = auth;
+
+  let res = new Resource("http://localhost:8080/foo"); // no authenticator
+  let content = yield res.get(self.cb);
+  do_check_eq(content, "This path exists and is protected");
+  do_check_eq(res.lastRequest.status, 200);
+
+  do_test_finished();
+  server.stop();
+  self.done();
+}
+
+function run_test() {
+  async_test.async(this);
+  do_test_pending();
+}
--- a/services/sync/tests/unit/test_fault_tolerance.js
+++ b/services/sync/tests/unit/test_fault_tolerance.js
@@ -1,13 +1,13 @@
 function run_test() {
   Components.utils.import("resource://weave/log4moz.js");
   Components.utils.import("resource://weave/faultTolerance.js");
 
   // Just make sure the getter works and the service is a singleton.
   FaultTolerance.Service._testProperty = "hi";
   do_check_eq(FaultTolerance.Service._testProperty, "hi");
 
-  var log = Log4Moz.Service.rootLogger;
+  var log = Log4Moz.repository.rootLogger;
   log.level = Log4Moz.Level.All;
   log.info("Testing.");
-  do_check_eq(Log4Moz.Service.rootLogger.appenders.length, 1);
+  do_check_eq(Log4Moz.repository.rootLogger.appenders.length, 1);
 }
--- a/services/sync/tests/unit/test_log4moz.js
+++ b/services/sync/tests/unit/test_log4moz.js
@@ -7,17 +7,17 @@ function MockAppender(formatter) {
 MockAppender.prototype = {
   doAppend: function DApp_doAppend(message) {
     this.messages.push(message);
   }
 };
 MockAppender.prototype.__proto__ = new Log4Moz.Appender();
 
 function run_test() {
-  var log = Log4Moz.Service.rootLogger;
+  var log = Log4Moz.repository.rootLogger;
   var appender = new MockAppender(new Log4Moz.BasicFormatter());
 
   log.level = Log4Moz.Level.Debug;
   appender.level = Log4Moz.Level.Info;
   log.addAppender(appender);
   log.info("info test");
   log.debug("this should be logged but not appended.");
 
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_records_crypto.js
@@ -0,0 +1,105 @@
+try {
+  Cu.import("resource://weave/log4moz.js");
+  Cu.import("resource://weave/util.js");
+  Cu.import("resource://weave/async.js");
+  Cu.import("resource://weave/auth.js");
+  Cu.import("resource://weave/identity.js");
+  Cu.import("resource://weave/base_records/keys.js");
+  Cu.import("resource://weave/base_records/crypto.js");
+} catch (e) {
+  do_throw(e);
+}
+Function.prototype.async = Async.sugar;
+
+let jsonSvc, cryptoSvc, salt, iv, symKey, wrappedKey, pubKey, privKey;
+
+function pubkey_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             payload: {type: "pubkey",
+                       private_key: "http://localhost:8080/privkey",
+                       key_data: pubKey}};
+  return httpd_basic_auth_handler(jsonSvc.encode(obj), metadata, response);
+}
+
+function privkey_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             payload: {type: "privkey",
+                       public_key: "http://localhost:8080/pubkey",
+                       key_data: privKey}};
+  return httpd_basic_auth_handler(jsonSvc.encode(obj), metadata, response);
+}
+
+function crypted_resource_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             encryption: "http://localhost:8080/crypto-meta",
+             payload: cryptoSvc.encrypt("my payload here", symKey, iv)};
+  return httpd_basic_auth_handler(jsonSvc.encode(obj), metadata, response);
+}
+
+function crypto_meta_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             payload: {type: "crypto-meta",
+                       salt: salt,
+                       iv: iv,
+                       keyring: {
+                         "pubkey": wrappedKey
+                       }}};
+  return httpd_basic_auth_handler(jsonSvc.encode(obj), metadata, response);
+}
+
+function async_test() {
+  let self = yield;
+  let server;
+
+  try {
+    let log = Log4Moz.repository.getLogger();
+    Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
+
+    jsonSvc = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+    cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"].
+      getService(Ci.IWeaveCrypto);
+
+    let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
+    Auth.defaultAuthenticator = auth;
+    PubKeys.defaultKeyUrl = "http://localhost:8080/pubkey";
+
+    server = httpd_setup({"/pubkey": pubkey_handler,
+                          "/privkey": privkey_handler,
+                          "/crypted-resource": crypted_resource_handler,
+                          "/crypto-meta": crypto_meta_handler});
+
+    salt = cryptoSvc.generateRandomBytes(16);
+    iv = cryptoSvc.generateRandomIV();
+
+    let pubOut = {}, privOut = {};
+    cryptoSvc.generateKeypair("my passphrase", salt, iv, pubOut, privOut);
+    pubKey = pubOut.value;
+    privKey = privOut.value;
+
+    symKey = cryptoSvc.generateRandomKey();
+    wrappedKey = cryptoSvc.wrapSymmetricKey(symKey, pubKey);
+
+    let wrap = new CryptoWrapper("http://localhost:8080/crypted-resource", auth);
+    yield wrap.get(self.cb);
+
+    let payload = yield wrap.decrypt(self.cb, "my passphrase");
+    do_check_eq(payload, "my payload here");
+    do_check_neq(payload, wrap.data.payload); // wrap.data.payload is the encrypted one
+
+    wrap.cleartext = "another payload";
+    yield wrap.encrypt(self.cb, "my passphrase");
+    payload = yield wrap.decrypt(self.cb, "my passphrase");
+    do_check_eq(payload, "another payload");
+
+    do_test_finished();
+  }
+  catch (e) { do_throw(e); }
+  finally { server.stop(); }
+
+  self.done();
+}
+
+function run_test() {
+  async_test.async(this);
+  do_test_pending();
+}
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_records_keys.js
@@ -0,0 +1,64 @@
+try {
+  Cu.import("resource://weave/log4moz.js");
+  Cu.import("resource://weave/util.js");
+  Cu.import("resource://weave/async.js");
+  Cu.import("resource://weave/auth.js");
+  Cu.import("resource://weave/identity.js");
+  Cu.import("resource://weave/base_records/keys.js");
+} catch (e) {
+  do_throw(e);
+}
+Function.prototype.async = Async.sugar;
+
+let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+
+function pubkey_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             payload: {type: "pubkey",
+                       private_key: "http://localhost:8080/privkey",
+                       key_data: "asdfasdfasf..."}};
+  return httpd_basic_auth_handler(json.encode(obj), metadata, response);
+}
+
+function privkey_handler(metadata, response) {
+  let obj = {modified: "2454725.98283",
+             payload: {type: "privkey",
+                       public_key: "http://localhost:8080/pubkey",
+                       key_data: "asdfasdfasf..."}};
+  let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+  return httpd_basic_auth_handler(json.encode(obj), metadata, response);
+}
+
+function async_test() {
+  let self = yield;
+  let server;
+
+  try {
+    let log = Log4Moz.repository.getLogger();
+    Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
+
+    let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
+    Auth.defaultAuthenticator = auth;
+    server = httpd_setup({"/pubkey": pubkey_handler,
+                          "/privkey": privkey_handler});
+
+    let pubkey = yield PubKeys.get(self.cb, "http://localhost:8080/pubkey");
+    do_check_eq(pubkey.data.payload.type, "pubkey");
+    do_check_eq(pubkey.lastRequest.status, 200);
+
+    let privkey = yield PrivKeys.get(self.cb, pubkey.privateKeyUri);
+    do_check_eq(privkey.data.payload.type, "privkey");
+    do_check_eq(privkey.lastRequest.status, 200);
+
+    do_test_finished();
+  }
+  catch (e) { do_throw(e); }
+  finally { server.stop(); }
+
+  self.done();
+}
+
+function run_test() {
+  async_test.async(this);
+  do_test_pending();
+}
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_records_wbo.js
@@ -0,0 +1,44 @@
+Cu.import("resource://weave/log4moz.js");
+Cu.import("resource://weave/async.js");
+Cu.import("resource://weave/auth.js");
+Cu.import("resource://weave/identity.js");
+Cu.import("resource://weave/base_records/wbo.js");
+
+Function.prototype.async = Async.sugar;
+
+let logger;
+let Httpd = {};
+Cu.import("resource://tests/lib/httpd.js", Httpd);
+
+function server_handler(metadata, response) {
+  let body = '{"guid": "asdf-1234-asdf-1234", "type": ["object"]}';
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function async_test() {
+  let self = yield;
+
+  logger = Log4Moz.repository.getLogger('Test');
+  Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
+
+  let server = new Httpd.nsHttpServer();
+  server.registerPathHandler("/record", server_handler);
+  server.start(8080);
+
+  let res = new WBORecord("http://localhost:8080/record");
+  let rec = yield res.get(self.cb);
+  do_check_eq(rec.guid, "asdf-1234-asdf-1234");
+  do_check_eq(rec.type[0], "object");
+  do_check_eq(res.lastRequest.status, 200);
+
+  do_test_finished();
+  server.stop();
+    
+  self.done();
+}
+
+function run_test() {
+  async_test.async(this);
+  do_test_pending();
+}
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_resource.js
@@ -0,0 +1,104 @@
+Cu.import("resource://weave/log4moz.js");
+Cu.import("resource://weave/util.js");
+Cu.import("resource://weave/async.js");
+Cu.import("resource://weave/resource.js");
+Cu.import("resource://weave/auth.js");
+Cu.import("resource://weave/identity.js");
+
+Function.prototype.async = Async.sugar;
+
+let logger;
+let Httpd = {};
+Cu.import("resource://tests/lib/httpd.js", Httpd);
+
+function server_open(metadata, response) {
+  let body = "This path exists";
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function server_protected(metadata, response) {
+  let body;
+
+  // no btoa() in xpcshell.  it's guest:guest
+  if (metadata.hasHeader("Authorization") &&
+      metadata.getHeader("Authorization") == "Basic Z3Vlc3Q6Z3Vlc3Q=") {
+    body = "This path exists and is protected";
+    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+
+  } else {
+    body = "This path exists and is protected - failed";
+    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
+  }
+
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function server_404(metadata, response) {
+  let body = "File not found";
+  response.setStatusLine(metadata.httpVersion, 404, "Not Found");
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function async_test() {
+  let self = yield;
+
+  logger = Log4Moz.repository.getLogger('Test');
+  Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
+
+  let server = new Httpd.nsHttpServer();
+  server.registerPathHandler("/open", server_open);
+  server.registerPathHandler("/protected", server_protected);
+  server.registerPathHandler("/404", server_404);
+  server.start(8080);
+
+  Utils.prefs.setIntPref("network.numRetries", 1); // speed up test
+
+  // 1. A regular non-password-protected resource
+
+  let res = new Resource("http://localhost:8080/open");
+  let content = yield res.get(self.cb);
+  do_check_eq(content, "This path exists");
+  do_check_eq(res.lastRequest.status, 200);
+
+  // 2. A password protected resource (test that it'll fail w/o pass)
+  let res2 = new Resource("http://localhost:8080/protected");
+  try {
+    content = yield res2.get(self.cb);
+    do_check_true(false); // unreachable, get() above must fail
+  } catch (e) {}
+
+  // 3. A password protected resource
+
+  let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
+  let res3 = new Resource("http://localhost:8080/protected", auth);
+  content = yield res3.get(self.cb);
+  do_check_eq(content, "This path exists and is protected");
+  do_check_eq(res3.lastRequest.status, 200);
+
+  // 4. A non-existent resource (test that it'll fail)
+
+  let res4 = new Resource("http://localhost:8080/404");
+  try {
+    let content = yield res4.get(self.cb);
+    do_check_true(false); // unreachable, get() above must fail
+  } catch (e) {}
+  do_check_eq(res4.lastRequest.responseText, "File not found");
+  do_check_eq(res4.lastRequest.status, 404);
+
+  // FIXME: additional coverage needed:
+  // * PUT requests
+  // * DELETE requests
+  // * JsonFilter
+
+  do_test_finished();
+  server.stop();
+  self.done();
+}
+
+function run_test() {
+  async_test.async(this);
+  do_test_pending();
+}