couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject [2/2] git commit: split replicator_db tests
Date Fri, 08 Feb 2013 18:17:49 GMT
Updated Branches:
  refs/heads/master 3b103eb10 -> ae048910e


split replicator_db tests


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/ae048910
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/ae048910
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/ae048910

Branch: refs/heads/master
Commit: ae048910ebea89942116fe7734d36baaa7d6cfcf
Parents: 3b103eb
Author: Jan Lehnardt <jan@apache.org>
Authored: Sun Jan 13 15:22:01 2013 +0100
Committer: Jan Lehnardt <jan@apache.org>
Committed: Fri Feb 8 19:17:43 2013 +0100

----------------------------------------------------------------------
 CHANGES                                            |   10 +
 share/Makefile.am                                  |   20 +-
 share/www/script/couch_tests.js                    |   19 +-
 share/www/script/replicator_db_inc.js              |   96 +
 share/www/script/test/replicator_db.js             | 1560 ---------------
 share/www/script/test/replicator_db_bad_rep_id.js  |   77 +
 share/www/script/test/replicator_db_by_doc_id.js   |   99 +
 .../script/test/replicator_db_compact_rep_db.js    |  119 ++
 share/www/script/test/replicator_db_continuous.js  |  137 ++
 .../test/replicator_db_credential_delegation.js    |  149 ++
 .../script/test/replicator_db_field_validation.js  |  178 ++
 share/www/script/test/replicator_db_filtered.js    |  105 +
 share/www/script/test/replicator_db_identical.js   |   87 +
 .../test/replicator_db_identical_continuous.js     |  132 ++
 .../script/test/replicator_db_invalid_filter.js    |  115 ++
 share/www/script/test/replicator_db_simple.js      |  114 ++
 share/www/script/test/replicator_db_successive.js  |  127 ++
 share/www/script/test/replicator_db_survives.js    |  128 ++
 share/www/script/test/replicator_db_swap_rep_db.js |  170 ++
 .../script/test/replicator_db_update_security.js   |   92 +
 share/www/script/test/replicator_db_user_ctx.js    |  272 +++
 share/www/script/test/replicator_db_write_auth.js  |  102 +
 test/javascript/run.tpl                            |    1 +
 23 files changed, 2347 insertions(+), 1562 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index e4b2aab..3012e3d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,16 @@
 Apache CouchDB CHANGES
 ======================
 
+Version 1.4.0
+-------------
+
+This version has not been released yet.
+
+Test Suite:
+
+ * Split up replicator_db tests into multiple independent tests.
+
+
 Version 1.3.0
 -------------
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/Makefile.am
----------------------------------------------------------------------
diff --git a/share/Makefile.am b/share/Makefile.am
index 31373ee..876c701 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -176,7 +176,25 @@ nobase_dist_localdata_DATA = \
     www/script/test/reduce_false.js \
     www/script/test/reduce_false_temp.js \
     www/script/test/replication.js \
-    www/script/test/replicator_db.js \
+    www/script/replicator_db_inc.js \
+    www/script/test/replicator_db_bad_rep_id.js \
+    www/script/test/replicator_db_by_doc_id.js \
+    www/script/test/replicator_db_compact_rep_db.js \
+    www/script/test/replicator_db_continuous.js \
+    www/script/test/replicator_db_credential_delegation.js \
+    www/script/test/replicator_db_field_validation.js \
+    www/script/test/replicator_db_filtered.js \
+    www/script/test/replicator_db_identical.js \
+    www/script/test/replicator_db_identical_continuous.js \
+    www/script/test/replicator_db_invalid_filter.js \
+    www/script/test/replicator_db_security.js \
+    www/script/test/replicator_db_simple.js \
+    www/script/test/replicator_db_successive.js \
+    www/script/test/replicator_db_survives.js \
+    www/script/test/replicator_db_swap_rep_db.js \
+    www/script/test/replicator_db_update_security.js \
+    www/script/test/replicator_db_user_ctx.js \
+    www/script/test/replicator_db_write_auth.js \
     www/script/test/replicator_db_security.js \
     www/script/test/rev_stemming.js \
     www/script/test/rewrite.js \

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/couch_tests.js
----------------------------------------------------------------------
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index c1cdf75..99d63e7 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -68,7 +68,24 @@ loadTest("reduce_builtin.js");
 loadTest("reduce_false.js");
 loadTest("reduce_false_temp.js");
 loadTest("replication.js");
-loadTest("replicator_db.js");
+loadScript("script/replicator_db_inc.js");
+loadTest("replicator_db_bad_rep_id.js");
+loadTest("replicator_db_by_doc_id.js");
+loadTest("replicator_db_compact_rep_db.js");
+loadTest("replicator_db_continuous.js");
+loadTest("replicator_db_credential_delegation.js");
+loadTest("replicator_db_field_validation.js");
+loadTest("replicator_db_filtered.js");
+loadTest("replicator_db_identical.js");
+loadTest("replicator_db_identical_continuous.js");
+loadTest("replicator_db_invalid_filter.js");
+loadTest("replicator_db_simple.js");
+loadTest("replicator_db_successive.js");
+loadTest("replicator_db_survives.js");
+loadTest("replicator_db_swap_rep_db.js");
+loadTest("replicator_db_update_security.js");
+loadTest("replicator_db_user_ctx.js");
+loadTest("replicator_db_write_auth.js");
 loadTest("replicator_db_security.js");
 loadTest("rev_stemming.js");
 loadTest("rewrite.js");

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/replicator_db_inc.js
----------------------------------------------------------------------
diff --git a/share/www/script/replicator_db_inc.js b/share/www/script/replicator_db_inc.js
new file mode 100644
index 0000000..23f8587
--- /dev/null
+++ b/share/www/script/replicator_db_inc.js
@@ -0,0 +1,96 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+var replicator_db = {};
+replicator_db.wait_rep_doc = 500; // number of millisecs to wait after saving a Rep Doc
+replicator_db.dbA = new CouchDB("test_suite_rep_db_a", {"X-Couch-Full-Commit":"false"});
+replicator_db.dbB = new CouchDB("test_suite_rep_db_b", {"X-Couch-Full-Commit":"false"});
+replicator_db.repDb = new CouchDB("test_suite_rep_db", {"X-Couch-Full-Commit":"false"});
+replicator_db.usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
+
+replicator_db.docs1 = [
+  {
+    _id: "foo1",
+    value: 11
+  },
+  {
+    _id: "foo2",
+    value: 22
+  },
+  {
+    _id: "foo3",
+    value: 33
+  }
+];
+
+replicator_db.waitForRep = function waitForSeq(repDb, repDoc, state) {
+  var newRep,
+      t0 = new Date(),
+      t1,
+      ms = 3000;
+
+  do {
+    newRep = repDb.open(repDoc._id);
+    t1 = new Date();
+  } while (((t1 - t0) <= ms) && newRep._replication_state !== state);
+}
+
+replicator_db.waitForSeq = function waitForSeq(sourceDb, targetDb) {
+  var targetSeq,
+      sourceSeq = sourceDb.info().update_seq,
+      t0 = new Date(),
+      t1,
+      ms = 3000;
+
+  do {
+    targetSeq = targetDb.info().update_seq;
+    t1 = new Date();
+  } while (((t1 - t0) <= ms) && targetSeq < sourceSeq);
+}
+
+replicator_db.waitForDocPos = function waitForDocPos(db, docId, pos) {
+  var doc, curPos, t0, t1,
+      maxWait = 3000;
+
+  doc = db.open(docId);
+  curPos = Number(doc._rev.split("-", 1));
+  t0 = t1 = new Date();
+
+  while ((curPos < pos) && ((t1 - t0) <= maxWait)) {
+     doc = db.open(docId);
+     curPos = Number(doc._rev.split("-", 1));
+     t1 = new Date();
+  }
+
+  return doc;
+}
+
+replicator_db.wait = function wait(ms) {
+  var t0 = new Date(), t1;
+  do {
+    CouchDB.request("GET", "/");
+    t1 = new Date();
+  } while ((t1 - t0) <= ms);
+}
+
+
+replicator_db.populate_db = function populate_db(db, docs) {
+  if (db.name !== replicator_db.usersDb.name) {
+    db.deleteDb();
+    db.createDb();
+  }
+  for (var i = 0; i < docs.length; i++) {
+    var d = docs[i];
+    delete d._rev;
+    T(db.save(d).ok);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db.js b/share/www/script/test/replicator_db.js
deleted file mode 100644
index edc85f4..0000000
--- a/share/www/script/test/replicator_db.js
+++ /dev/null
@@ -1,1560 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-couchTests.replicator_db = function(debug) {
-
-  if (debug) debugger;
-
-  var wait_rep_doc = 500; // number of millisecs to wait after saving a Rep Doc
-  var dbA = new CouchDB("test_suite_rep_db_a", {"X-Couch-Full-Commit":"false"});
-  var dbB = new CouchDB("test_suite_rep_db_b", {"X-Couch-Full-Commit":"false"});
-  var repDb = new CouchDB("test_suite_rep_db", {"X-Couch-Full-Commit":"false"});
-  var usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
-
-  var docs1 = [
-    {
-      _id: "foo1",
-      value: 11
-    },
-    {
-      _id: "foo2",
-      value: 22
-    },
-    {
-      _id: "foo3",
-      value: 33
-    }
-  ];
-
-  function waitForRep(repDb, repDoc, state) {
-    var newRep,
-        t0 = new Date(),
-        t1,
-        ms = 3000;
-
-    do {
-      newRep = repDb.open(repDoc._id);
-      t1 = new Date();
-    } while (((t1 - t0) <= ms) && newRep._replication_state !== state);
-  }
-
-  function waitForSeq(sourceDb, targetDb) {
-    var targetSeq,
-        sourceSeq = sourceDb.info().update_seq,
-        t0 = new Date(),
-        t1,
-        ms = 3000;
-
-    do {
-      targetSeq = targetDb.info().update_seq;
-      t1 = new Date();
-    } while (((t1 - t0) <= ms) && targetSeq < sourceSeq);
-  }
-
-  function waitForDocPos(db, docId, pos) {
-    var doc, curPos, t0, t1,
-        maxWait = 3000;
-
-    doc = db.open(docId);
-    curPos = Number(doc._rev.split("-", 1));
-    t0 = t1 = new Date();
-
-    while ((curPos < pos) && ((t1 - t0) <= maxWait)) {
-       doc = db.open(docId);
-       curPos = Number(doc._rev.split("-", 1));
-       t1 = new Date();
-    }
-
-    return doc;
-  }
-
-  function wait(ms) {
-    var t0 = new Date(), t1;
-    do {
-      CouchDB.request("GET", "/");
-      t1 = new Date();
-    } while ((t1 - t0) <= ms);
-  }
-
-
-  function populate_db(db, docs) {
-    if (db.name !== usersDb.name) {
-      db.deleteDb();
-      db.createDb();
-    }
-    for (var i = 0; i < docs.length; i++) {
-      var d = docs[i];
-      delete d._rev;
-      T(db.save(d).ok);
-    }
-  }
-
-  function simple_replication() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_simple_rep",
-      source: dbA.name,
-      target: dbB.name
-    };
-    T(repDb.save(repDoc).ok);
-
-    waitForRep(repDb, repDoc, "completed");
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    T(repDoc1.source === repDoc.source);
-    T(repDoc1.target === repDoc.target);
-    T(repDoc1._replication_state === "completed", "simple");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-    T(typeof repDoc1._replication_stats === "object", "doc has stats");
-    var stats = repDoc1._replication_stats;
-    TEquals(docs1.length, stats.revisions_checked,
-       "right # of revisions_checked");
-    TEquals(docs1.length, stats.missing_revisions_found,
-      "right # of missing_revisions_found");
-    TEquals(docs1.length, stats.docs_read, "right # of docs_read");
-    TEquals(docs1.length, stats.docs_written, "right # of docs_written");
-    TEquals(0, stats.doc_write_failures, "right # of doc_write_failures");
-    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
-      "right checkpointed_source_seq");
-  }
-
-
-  function filtered_replication() {
-    var docs2 = docs1.concat([
-      {
-        _id: "_design/mydesign",
-        language : "javascript",
-        filters : {
-          myfilter : (function(doc, req) {
-            return (doc.value % 2) !== Number(req.query.myparam);
-          }).toString()
-        }
-      }
-    ]);
-
-    populate_db(dbA, docs2);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_filt_rep_doc",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      filter: "mydesign/myfilter",
-      query_params: {
-        myparam: 1
-      }
-    };
-    T(repDb.save(repDoc).ok);
-
-    waitForRep(repDb, repDoc, "completed");
-    for (var i = 0; i < docs2.length; i++) {
-      var doc = docs2[i];
-      var copy = dbB.open(doc._id);
-
-      if (typeof doc.value === "number") {
-        if ((doc.value % 2) !== 1) {
-          T(copy !== null);
-          T(copy.value === doc.value);
-        } else {
-          T(copy === null);
-        }
-      }
-    }
-
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    T(repDoc1.source === repDoc.source);
-    T(repDoc1.target === repDoc.target);
-    T(repDoc1._replication_state === "completed", "filtered");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-    T(typeof repDoc1._replication_stats === "object", "doc has stats");
-    var stats = repDoc1._replication_stats;
-    TEquals(2, stats.revisions_checked, "right # of revisions_checked");
-    TEquals(2, stats.missing_revisions_found, "right # of missing_revisions_found");
-    TEquals(2, stats.docs_read, "right # of docs_read");
-    TEquals(1, stats.docs_written, "right # of docs_written");
-    TEquals(1, stats.doc_write_failures, "right # of doc_write_failures");
-    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
-      "right checkpointed_source_seq");
-  }
-
-
-  function continuous_replication() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_cont_rep_doc",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      continuous: true,
-      user_ctx: {
-        roles: ["_admin"]
-      }
-    };
-
-    T(repDb.save(repDoc).ok);
-
-    waitForSeq(dbA, dbB);
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    var tasks = JSON.parse(CouchDB.request("GET", "/_active_tasks").responseText);
-    TEquals(1, tasks.length, "1 active task");
-    TEquals(repDoc._id, tasks[0].doc_id, "replication doc id in active tasks");
-
-    // add another doc to source, it will be replicated to target
-    var docX = {
-      _id: "foo1000",
-      value: 1001
-    };
-
-    T(dbA.save(docX).ok);
-
-    waitForSeq(dbA, dbB);
-    var copy = dbB.open("foo1000");
-    T(copy !== null);
-    T(copy.value === 1001);
-
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    T(repDoc1.source === repDoc.source);
-    T(repDoc1.target === repDoc.target);
-    T(repDoc1._replication_state === "triggered");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-
-    // Design documents are only replicated to local targets if the respective
-    // replication document has a user_ctx filed with the "_admin" role in it.
-    var ddoc = {
-      _id: "_design/foobar",
-      language: "javascript"
-    };
-
-    T(dbA.save(ddoc).ok);
-
-    waitForSeq(dbA, dbB);
-    var ddoc_copy = dbB.open("_design/foobar");
-    T(ddoc_copy !== null);
-    T(ddoc.language === "javascript");
-
-    // update the design doc on source, test that the new revision is replicated
-    ddoc.language = "erlang";
-    T(dbA.save(ddoc).ok);
-    T(ddoc._rev.indexOf("2-") === 0);
-
-    waitForSeq(dbA, dbB);
-    ddoc_copy = dbB.open("_design/foobar");
-    T(ddoc_copy !== null);
-    T(ddoc_copy._rev === ddoc._rev);
-    T(ddoc.language === "erlang");
-
-    // stop replication by deleting the replication document
-    T(repDb.deleteDoc(repDoc1).ok);
-
-    // add another doc to source, it will NOT be replicated to target
-    var docY = {
-      _id: "foo666",
-      value: 999
-    };
-
-    T(dbA.save(docY).ok);
-
-    wait(200); // is there a way to avoid wait here?
-    var copy = dbB.open("foo666");
-    T(copy === null);
-  }
-
-
-  function by_doc_ids_replication() {
-    // to test that we can replicate docs with slashes in their IDs
-    var docs2 = docs1.concat([
-      {
-        _id: "_design/mydesign",
-        language : "javascript"
-      }
-    ]);
-
-    populate_db(dbA, docs2);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_cont_rep_doc",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      doc_ids: ["foo666", "foo3", "_design/mydesign", "foo999", "foo1"]
-    };
-    T(repDb.save(repDoc).ok);
-
-    waitForRep(repDb, repDoc, "completed");
-    var copy = dbB.open("foo1");
-    T(copy !== null);
-    T(copy.value === 11);
-
-    copy = dbB.open("foo2");
-    T(copy === null);
-
-    copy = dbB.open("foo3");
-    T(copy !== null);
-    T(copy.value === 33);
-
-    copy = dbB.open("foo666");
-    T(copy === null);
-
-    copy = dbB.open("foo999");
-    T(copy === null);
-
-    copy = dbB.open("_design/mydesign");
-    T(copy === null);
-
-    repDoc = repDb.open(repDoc._id);
-    T(typeof repDoc._replication_stats === "object", "doc has stats");
-    var stats = repDoc._replication_stats;
-    TEquals(3, stats.revisions_checked, "right # of revisions_checked");
-    TEquals(3, stats.missing_revisions_found, "right # of missing_revisions_found");
-    TEquals(3, stats.docs_read, "right # of docs_read");
-    TEquals(2, stats.docs_written, "right # of docs_written");
-    TEquals(1, stats.doc_write_failures, "right # of doc_write_failures");
-    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
-      "right checkpointed_source_seq");
-  }
-
-
-  function successive_identical_replications() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc1 = {
-      _id: "foo_ident_rep_1",
-      source: dbA.name,
-      target: dbB.name
-    };
-    T(repDb.save(repDoc1).ok);
-
-    waitForRep(repDb, repDoc1, "completed");
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    var repDoc1_copy = repDb.open(repDoc1._id);
-    T(repDoc1_copy !== null);
-    T(repDoc1_copy.source === repDoc1.source);
-    T(repDoc1_copy.target === repDoc1.target);
-    T(repDoc1_copy._replication_state === "completed");
-    T(typeof repDoc1_copy._replication_state_time === "string");
-    T(typeof repDoc1_copy._replication_id  === "string");
-    T(typeof repDoc1_copy._replication_stats === "object", "doc has stats");
-    var stats = repDoc1_copy._replication_stats;
-    TEquals(docs1.length, stats.revisions_checked,
-      "right # of revisions_checked");
-    TEquals(docs1.length, stats.missing_revisions_found,
-      "right # of missing_revisions_found");
-    TEquals(docs1.length, stats.docs_read, "right # of docs_read");
-    TEquals(docs1.length, stats.docs_written, "right # of docs_written");
-    TEquals(0, stats.doc_write_failures, "right # of doc_write_failures");
-    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
-      "right checkpointed_source_seq");
-
-    var newDoc = {
-      _id: "doc666",
-      value: 666
-    };
-    T(dbA.save(newDoc).ok);
-
-    wait(200);
-    var newDoc_copy = dbB.open(newDoc._id);
-    // not replicated because first replication is complete (not continuous)
-    T(newDoc_copy === null);
-
-    var repDoc2 = {
-      _id: "foo_ident_rep_2",
-      source: dbA.name,
-      target: dbB.name
-    };
-    T(repDb.save(repDoc2).ok);
-
-    waitForRep(repDb, repDoc2, "completed");
-    var newDoc_copy = dbB.open(newDoc._id);
-    T(newDoc_copy !== null);
-    T(newDoc_copy.value === newDoc.value);
-
-    var repDoc2_copy = repDb.open(repDoc2._id);
-    T(repDoc2_copy !== null);
-    T(repDoc2_copy.source === repDoc1.source);
-    T(repDoc2_copy.target === repDoc1.target);
-    T(repDoc2_copy._replication_state === "completed");
-    T(typeof repDoc2_copy._replication_state_time === "string");
-    T(typeof repDoc2_copy._replication_id === "string");
-    T(repDoc2_copy._replication_id === repDoc1_copy._replication_id);
-    T(typeof repDoc2_copy._replication_stats === "object", "doc has stats");
-    stats = repDoc2_copy._replication_stats;
-    TEquals(1, stats.revisions_checked, "right # of revisions_checked");
-    TEquals(1, stats.missing_revisions_found,
-      "right # of missing_revisions_found");
-    TEquals(1, stats.docs_read, "right # of docs_read");
-    TEquals(1, stats.docs_written, "right # of docs_written");
-    TEquals(0, stats.doc_write_failures, "right # of doc_write_failures");
-    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
-      "right checkpointed_source_seq");
-  }
-
-
-  // test the case where multiple replication docs (different IDs)
-  // describe in fact the same replication (source, target, etc)
-  function identical_rep_docs() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc1 = {
-      _id: "foo_dup_rep_doc_1",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name
-    };
-    var repDoc2 = {
-      _id: "foo_dup_rep_doc_2",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name
-    };
-
-    T(repDb.save(repDoc1).ok);
-    T(repDb.save(repDoc2).ok);
-
-    waitForRep(repDb, repDoc1, "completed");
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    repDoc1 = repDb.open("foo_dup_rep_doc_1");
-    T(repDoc1 !== null);
-    T(repDoc1._replication_state === "completed", "identical");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-
-    repDoc2 = repDb.open("foo_dup_rep_doc_2");
-    T(repDoc2 !== null);
-    T(typeof repDoc2._replication_state === "undefined");
-    T(typeof repDoc2._replication_state_time === "undefined");
-    T(repDoc2._replication_id === repDoc1._replication_id);
-  }
-
-
-  // test the case where multiple replication docs (different IDs)
-  // describe in fact the same continuous replication (source, target, etc)
-  function identical_continuous_rep_docs() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc1 = {
-      _id: "foo_dup_cont_rep_doc_1",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      continuous: true
-    };
-    var repDoc2 = {
-      _id: "foo_dup_cont_rep_doc_2",
-      source: "http://" + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      continuous: true
-    };
-
-    T(repDb.save(repDoc1).ok);
-    T(repDb.save(repDoc2).ok);
-
-    waitForSeq(dbA, dbB);
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    repDoc1 = repDb.open("foo_dup_cont_rep_doc_1");
-    T(repDoc1 !== null);
-    T(repDoc1._replication_state === "triggered");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-
-    repDoc2 = repDb.open("foo_dup_cont_rep_doc_2");
-    T(repDoc2 !== null);
-    T(typeof repDoc2._replication_state === "undefined");
-    T(typeof repDoc2._replication_state_time === "undefined");
-    T(repDoc2._replication_id === repDoc1._replication_id);
-
-    var newDoc = {
-      _id: "foo666",
-      value: 999
-    };
-    T(dbA.save(newDoc).ok);
-
-    waitForSeq(dbA, dbB);
-    var copy = dbB.open("foo666");
-    T(copy !== null);
-    T(copy.value === 999);
-
-    // deleting second replication doc, doesn't affect the 1st one and
-    // neither it stops the replication
-    T(repDb.deleteDoc(repDoc2).ok);
-    repDoc1 = repDb.open("foo_dup_cont_rep_doc_1");
-    T(repDoc1 !== null);
-    T(repDoc1._replication_state === "triggered");
-    T(typeof repDoc1._replication_state_time === "string");
-
-    var newDoc2 = {
-        _id: "foo5000",
-        value: 5000
-    };
-    T(dbA.save(newDoc2).ok);
-
-    waitForSeq(dbA, dbB);
-    var copy = dbB.open("foo5000");
-    T(copy !== null);
-    T(copy.value === 5000);
-
-    // deleting the 1st replication document stops the replication
-    T(repDb.deleteDoc(repDoc1).ok);
-    var newDoc3 = {
-        _id: "foo1983",
-        value: 1983
-    };
-    T(dbA.save(newDoc3).ok);
-
-    wait(wait_rep_doc); //how to remove wait?
-    var copy = dbB.open("foo1983");
-    T(copy === null);
-  }
-
-
-  function test_replication_credentials_delegation() {
-    populate_db(usersDb, []);
-
-    var joeUserDoc = CouchDB.prepareUserDoc({
-      name: "joe",
-      roles: ["god", "erlanger"]
-    }, "erly");
-    T(usersDb.save(joeUserDoc).ok);
-
-    var ddoc = {
-      _id: "_design/beer",
-      language: "javascript"
-    };
-    populate_db(dbA, docs1.concat([ddoc]));
-    populate_db(dbB, []);
-
-    T(dbB.setSecObj({
-      admins: {
-        names: [],
-        roles: ["god"]
-      }
-    }).ok);
-
-    var server_admins_config = [
-      {
-        section: "couch_httpd_auth",
-        key: "iterations",
-        value: "1"
-      },
-      {
-        section: "admins",
-        key: "fdmanana",
-        value: "qwerty"
-      }
-    ];
-
-    run_on_modified_server(server_admins_config, function() {
-
-      T(CouchDB.login("fdmanana", "qwerty").ok);
-      T(CouchDB.session().userCtx.name === "fdmanana");
-      T(CouchDB.session().userCtx.roles.indexOf("_admin") !== -1);
-
-      var repDoc = {
-        _id: "foo_rep_del_doc_1",
-        source: dbA.name,
-        target: dbB.name,
-        user_ctx: {
-          name: "joe",
-          roles: ["erlanger"]
-        }
-      };
-
-      T(repDb.save(repDoc).ok);
-
-      waitForRep(repDb, repDoc, "completed");
-      for (var i = 0; i < docs1.length; i++) {
-        var doc = docs1[i];
-        var copy = dbB.open(doc._id);
-        T(copy !== null);
-        T(copy.value === doc.value);
-      }
-
-      // design doc was not replicated, because joe is not an admin of db B
-      var doc = dbB.open(ddoc._id);
-      T(doc === null);
-
-      // now test the same replication but putting the role "god" in the
-      // delegation user context property
-      var repDoc2 = {
-        _id: "foo_rep_del_doc_2",
-        source: dbA.name,
-        target: dbB.name,
-        user_ctx: {
-          name: "joe",
-          roles: ["erlanger", "god"]
-        }
-      };
-      T(repDb.save(repDoc2).ok);
-
-      waitForRep(repDb, repDoc2, "completed");
-      for (var i = 0; i < docs1.length; i++) {
-        var doc = docs1[i];
-        var copy = dbB.open(doc._id);
-        T(copy !== null);
-        T(copy.value === doc.value);
-      }
-
-      // because anyone with a 'god' role is an admin of db B, a replication
-      // that is delegated to a 'god' role can write design docs to db B
-      doc = dbB.open(ddoc._id);
-      T(doc !== null);
-      T(doc.language === ddoc.language);
-    });
-  }
-
-
-  function continuous_replication_survives_restart() {
-    var origRepDbName = CouchDB.request(
-      "GET", "/_config/replicator/db").responseText;
-
-    repDb.deleteDb();
-
-    var xhr = CouchDB.request("PUT", "/_config/replicator/db", {
-      body : JSON.stringify(repDb.name),
-      headers: {"X-Couch-Persist": "false"}
-    });
-    T(xhr.status === 200);
-
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_cont_rep_survives_doc",
-      source: dbA.name,
-      target: dbB.name,
-      continuous: true
-    };
-
-    T(repDb.save(repDoc).ok);
-
-    waitForSeq(dbA, dbB);
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    repDb.ensureFullCommit();
-    dbA.ensureFullCommit();
-
-    restartServer();
-
-    xhr = CouchDB.request("PUT", "/_config/replicator/db", {
-      body : JSON.stringify(repDb.name),
-      headers: {"X-Couch-Persist": "false"}
-    });
-
-    T(xhr.status === 200);
-
-    // add another doc to source, it will be replicated to target
-    var docX = {
-      _id: "foo1000",
-      value: 1001
-    };
-
-    T(dbA.save(docX).ok);
-
-    waitForSeq(dbA, dbB);
-    var copy = dbB.open("foo1000");
-    T(copy !== null);
-    T(copy.value === 1001);
-
-    repDoc = waitForDocPos(repDb, "foo_cont_rep_survives_doc", 3);
-    T(repDoc !== null);
-    T(repDoc.continuous === true);
-
-    // stop replication
-    T(repDb.deleteDoc(repDoc).ok);
-
-    xhr = CouchDB.request("PUT", "/_config/replicator/db", {
-      body : origRepDbName,
-      headers: {"X-Couch-Persist": "false"}
-    });
-    T(xhr.status === 200);
-  }
-
-
-  function rep_db_write_authorization() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var server_admins_config = [
-      {
-        section: "admins",
-        key: "fdmanana",
-        value: "qwerty"
-      }
-    ];
-
-    run_on_modified_server(server_admins_config, function() {
-      var repDoc = {
-        _id: "foo_rep_doc",
-        source: dbA.name,
-        target: dbB.name,
-        continuous: true
-      };
-
-      T(CouchDB.login("fdmanana", "qwerty").ok);
-      T(CouchDB.session().userCtx.name === "fdmanana");
-      T(CouchDB.session().userCtx.roles.indexOf("_admin") !== -1);
-
-      T(repDb.save(repDoc).ok);
-
-      waitForRep(repDb, repDoc, "completed");
-
-      for (var i = 0; i < docs1.length; i++) {
-        var doc = docs1[i];
-        var copy = dbB.open(doc._id);
-
-        T(copy !== null);
-        T(copy.value === doc.value);
-      }
-
-      repDoc = repDb.open("foo_rep_doc");
-      T(repDoc !== null);
-      repDoc.target = "test_suite_foo_db";
-      repDoc.create_target = true;
-
-      // Only the replicator can update replication documents.
-      // Admins can only add and delete replication documents.
-      try {
-        repDb.save(repDoc);
-        T(false && "Should have thrown an exception");
-      } catch (x) {
-        T(x["error"] === "forbidden");
-      }
-    });
-  }
-
-
-  function test_user_ctx_validation() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-    populate_db(usersDb, []);
-
-    var joeUserDoc = CouchDB.prepareUserDoc({
-      name: "joe",
-      roles: ["erlanger", "bar"]
-    }, "erly");
-    var fdmananaUserDoc = CouchDB.prepareUserDoc({
-      name: "fdmanana",
-      roles: ["a", "b", "c"]
-    }, "qwerty");
-
-    TEquals(true, usersDb.save(joeUserDoc).ok);
-    TEquals(true, usersDb.save(fdmananaUserDoc).ok);
-
-    T(dbB.setSecObj({
-      admins: {
-        names: [],
-        roles: ["god"]
-      },
-      readers: {
-        names: [],
-        roles: ["foo"]
-      }
-    }).ok);
-
-    TEquals(true, CouchDB.login("joe", "erly").ok);
-    TEquals("joe", CouchDB.session().userCtx.name);
-    TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin"));
-
-    var repDoc = {
-      _id: "foo_rep",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbB.name
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "Should have failed, user_ctx missing.");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc.user_ctx = {
-      name: "john",
-      roles: ["erlanger"]
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "Should have failed, wrong user_ctx.name.");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc.user_ctx = {
-      name: "joe",
-      roles: ["bar", "god", "erlanger"]
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "Should have failed, a bad role in user_ctx.roles.");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    // user_ctx.roles might contain only a subset of the user's roles
-    repDoc.user_ctx = {
-      name: "joe",
-      roles: ["erlanger"]
-    };
-
-    TEquals(true, repDb.save(repDoc).ok);
-    CouchDB.logout();
-
-    waitForRep(repDb, repDoc, "error");
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    TEquals(repDoc.source, repDoc1.source);
-    TEquals(repDoc.target, repDoc1.target);
-    TEquals("error", repDoc1._replication_state);
-    TEquals("string", typeof repDoc1._replication_id);
-    TEquals("string", typeof repDoc1._replication_state_time);
-
-    TEquals(true, CouchDB.login("fdmanana", "qwerty").ok);
-    TEquals("fdmanana", CouchDB.session().userCtx.name);
-    TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin"));
-
-    try {
-      T(repDb.deleteDoc(repDoc1).ok);
-      T(false, "Shouldn't be able to delete replication document.");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    CouchDB.logout();
-    TEquals(true, CouchDB.login("joe", "erly").ok);
-    TEquals("joe", CouchDB.session().userCtx.name);
-    TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin"));
-
-    T(repDb.deleteDoc(repDoc1).ok);
-    CouchDB.logout();
-
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-
-      TEquals(null, copy);
-    }
-
-    T(dbB.setSecObj({
-      admins: {
-        names: [],
-        roles: ["god", "erlanger"]
-      },
-      readers: {
-        names: [],
-        roles: ["foo"]
-      }
-    }).ok);
-
-    TEquals(true, CouchDB.login("joe", "erly").ok);
-    TEquals("joe", CouchDB.session().userCtx.name);
-    TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin"));
-
-    repDoc = {
-      _id: "foo_rep_2",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      user_ctx: {
-        name: "joe",
-        roles: ["erlanger"]
-      }
-    };
-
-    TEquals(true, repDb.save(repDoc).ok);
-    CouchDB.logout();
-
-    waitForRep(repDb, repDoc, "complete");
-    repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    TEquals(repDoc.source, repDoc1.source);
-    TEquals(repDoc.target, repDoc1.target);
-    TEquals("completed", repDoc1._replication_state);
-    TEquals("string", typeof repDoc1._replication_id);
-    TEquals("string", typeof repDoc1._replication_state_time);
-
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-
-      T(copy !== null);
-      TEquals(doc.value, copy.value);
-    }
-
-    // Admins don't need to supply a user_ctx property in replication docs.
-    // If they do not, the implicit user_ctx "user_ctx": {name: null, roles: []}
-    // is used, meaning that design documents will not be replicated into
-    // local targets
-    T(dbB.setSecObj({
-      admins: {
-        names: [],
-        roles: []
-      },
-      readers: {
-        names: [],
-        roles: []
-      }
-    }).ok);
-
-    var ddoc = { _id: "_design/foo" };
-    TEquals(true, dbA.save(ddoc).ok);
-
-    repDoc = {
-      _id: "foo_rep_3",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbB.name
-    };
-
-    TEquals(true, repDb.save(repDoc).ok);
-    waitForRep(repDb, repDoc, "complete");
-    repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    TEquals(repDoc.source, repDoc1.source);
-    TEquals(repDoc.target, repDoc1.target);
-    TEquals("completed", repDoc1._replication_state);
-    TEquals("string", typeof repDoc1._replication_id);
-    TEquals("string", typeof repDoc1._replication_state_time);
-
-    var ddoc_copy = dbB.open(ddoc._id);
-    T(ddoc_copy === null);
-
-    repDoc = {
-      _id: "foo_rep_4",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbB.name,
-      user_ctx: {
-        roles: ["_admin"]
-      }
-    };
-
-    TEquals(true, repDb.save(repDoc).ok);
-    waitForRep(repDb, repDoc, "complete");
-    repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    TEquals(repDoc.source, repDoc1.source);
-    TEquals(repDoc.target, repDoc1.target);
-    TEquals("completed", repDoc1._replication_state);
-    TEquals("string", typeof repDoc1._replication_id);
-    TEquals("string", typeof repDoc1._replication_state_time);
-
-    ddoc_copy = dbB.open(ddoc._id);
-    T(ddoc_copy !== null);
-  }
-
-
-  function rep_doc_with_bad_rep_id() {
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc = {
-      _id: "foo_rep",
-      source: dbA.name,
-      target: dbB.name,
-      replication_id: "1234abc"
-    };
-    T(repDb.save(repDoc).ok);
-
-    waitForRep(repDb, repDoc, "completed");
-    for (var i = 0; i < docs1.length; i++) {
-      var doc = docs1[i];
-      var copy = dbB.open(doc._id);
-      T(copy !== null);
-      T(copy.value === doc.value);
-    }
-
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    T(repDoc1.source === repDoc.source);
-    T(repDoc1.target === repDoc.target);
-    T(repDoc1._replication_state === "completed",
-      "replication document with bad replication id failed");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-    T(repDoc1._replication_id !== "1234abc");
-  }
-
-
-  function swap_rep_db() {
-    var repDb2 = new CouchDB("test_suite_rep_db_2");
-    var dbA = new CouchDB("test_suite_rep_db_a");
-    var dbA_copy = new CouchDB("test_suite_rep_db_a_copy");
-    var dbB = new CouchDB("test_suite_rep_db_b");
-    var dbB_copy = new CouchDB("test_suite_rep_db_b_copy");
-    var dbC = new CouchDB("test_suite_rep_db_c");
-    var dbC_copy = new CouchDB("test_suite_rep_db_c_copy");
-    var repDoc1, repDoc2, repDoc3;
-    var xhr, i, doc, copy, new_doc;
-
-    populate_db(dbA, docs1);
-    populate_db(dbB, docs1);
-    populate_db(dbC, docs1);
-    populate_db(dbA_copy, []);
-    populate_db(dbB_copy, []);
-    populate_db(dbC_copy, []);
-    populate_db(repDb2, []);
-
-    repDoc1 = {
-      _id: "rep1",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbA_copy.name,
-      continuous: true
-    };
-    repDoc2 = {
-      _id: "rep2",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbB.name,
-      target: dbB_copy.name,
-      continuous: true
-    };
-    repDoc3 = {
-      _id: "rep3",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbC.name,
-      target: dbC_copy.name,
-      continuous: true
-    };
-
-    TEquals(true, repDb.save(repDoc1).ok);
-    TEquals(true, repDb.save(repDoc2).ok);
-
-    waitForSeq(dbA, dbA_copy);
-    waitForSeq(dbB, dbB_copy);
-
-    xhr = CouchDB.request("PUT", "/_config/replicator/db",{
-      body : JSON.stringify(repDb2.name),
-      headers: {"X-Couch-Persist": "false"}
-    });
-    TEquals(200, xhr.status);
-
-    // Temporary band-aid, give the replicator db some
-    // time to make the switch
-    wait(500);
-
-    new_doc = {
-      _id: "foo666",
-      value: 666
-    };
-
-    TEquals(true, dbA.save(new_doc).ok);
-    TEquals(true, dbB.save(new_doc).ok);
-    waitForSeq(dbA, dbA_copy);
-    waitForSeq(dbB, dbB_copy);
-
-    TEquals(true, repDb2.save(repDoc3).ok);
-    waitForSeq(dbC, dbC_copy);
-
-    for (i = 0; i < docs1.length; i++) {
-      doc = docs1[i];
-      copy = dbA_copy.open(doc._id);
-      T(copy !== null);
-      TEquals(doc.value, copy.value);
-      copy = dbB_copy.open(doc._id);
-      T(copy !== null);
-      TEquals(doc.value, copy.value);
-      copy = dbC_copy.open(doc._id);
-      T(copy !== null);
-      TEquals(doc.value, copy.value);
-    }
-
-    // replications rep1 and rep2 should have been stopped when the replicator
-    // database was swapped
-    copy = dbA_copy.open(new_doc._id);
-    TEquals(null, copy);
-    copy = dbB_copy.open(new_doc._id);
-    TEquals(null, copy);
-
-    xhr = CouchDB.request("PUT", "/_config/replicator/db",{
-      body : JSON.stringify(repDb.name),
-      headers: {"X-Couch-Persist": "false"}
-    });
-    TEquals(200, xhr.status);
-
-    // after setting the replicator database to the former, replications rep1
-    // and rep2 should have been resumed, while rep3 was stopped
-    TEquals(true, dbC.save(new_doc).ok);
-    wait(1000);
-
-    waitForSeq(dbA, dbA_copy);
-    waitForSeq(dbB, dbB_copy);
-
-    copy = dbA_copy.open(new_doc._id);
-    T(copy !== null);
-    TEquals(new_doc.value, copy.value);
-    copy = dbB_copy.open(new_doc._id);
-    T(copy !== null);
-    TEquals(new_doc.value, copy.value);
-    copy = dbC_copy.open(new_doc._id);
-    TEquals(null, copy);
-  }
-
-
-  function compact_rep_db() {
-    var dbA_copy = new CouchDB("test_suite_rep_db_a_copy");
-    var dbB_copy = new CouchDB("test_suite_rep_db_b_copy");
-    var repDoc1, repDoc2;
-    var xhr, i, doc, copy, new_doc;
-    var docs = makeDocs(1, 50);
-
-    populate_db(dbA, docs);
-    populate_db(dbB, docs);
-    populate_db(dbA_copy, []);
-    populate_db(dbB_copy, []);
-
-    repDoc1 = {
-      _id: "rep1",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbA_copy.name,
-      continuous: true
-    };
-    repDoc2 = {
-      _id: "rep2",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbB.name,
-      target: dbB_copy.name,
-      continuous: true
-    };
-
-    TEquals(true, repDb.save(repDoc1).ok);
-    TEquals(true, repDb.save(repDoc2).ok);
-
-    TEquals(true, repDb.compact().ok);
-    TEquals(202, repDb.last_req.status);
-
-    waitForSeq(dbA, dbA_copy);
-    waitForSeq(dbB, dbB_copy);
-
-    while (repDb.info().compact_running) {};
-
-    for (i = 0; i < docs.length; i++) {
-      copy = dbA_copy.open(docs[i]._id);
-      T(copy !== null);
-      copy = dbB_copy.open(docs[i]._id);
-      T(copy !== null);
-    }
-
-    new_doc = {
-      _id: "foo666",
-      value: 666
-    };
-
-    TEquals(true, dbA.save(new_doc).ok);
-    TEquals(true, dbB.save(new_doc).ok);
-
-    waitForSeq(dbA, dbA_copy);
-    waitForSeq(dbB, dbB_copy);
-
-    copy = dbA.open(new_doc._id);
-    T(copy !== null);
-    TEquals(666, copy.value);
-    copy = dbB.open(new_doc._id);
-    T(copy !== null);
-    TEquals(666, copy.value);
-  }
-
-
-  function error_state_replication() {
-    populate_db(dbA, docs1);
-
-    var repDoc = {
-      _id: "foo_error_rep",
-      source: dbA.name,
-      target: "nonexistent_test_db"
-    };
-    T(repDb.save(repDoc).ok);
-
-    waitForRep(repDb, repDoc, "error");
-    var repDoc1 = repDb.open(repDoc._id);
-    T(repDoc1 !== null);
-    T(repDoc1._replication_state === "error");
-    T(typeof repDoc1._replication_state_time === "string");
-    T(typeof repDoc1._replication_id  === "string");
-  }
-
-
-  function rep_doc_field_validation() {
-    var docs = makeDocs(1, 5);
-
-    populate_db(dbA, docs);
-    populate_db(dbB, []);
-
-    var repDoc = {
-       _id: "rep1",
-       target: dbB.name
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because source field is missing");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: 123,
-       target: dbB.name
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because source field is a number");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because target field is missing");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: null
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because target field is null");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: { url: 123 }
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because target.url field is not a string");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: { url: dbB.name, auth: null }
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because target.auth field is null");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: { url: dbB.name, auth: "foo:bar" }
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because target.auth field is not an object");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: dbB.name,
-       continuous: "true"
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because continuous is not a boolean");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-
-    repDoc = {
-       _id: "rep1",
-       source: dbA.name,
-       target: dbB.name,
-       filter: 123
-    };
-
-    try {
-      repDb.save(repDoc);
-      T(false, "should have failed because filter is not a string");
-    } catch (x) {
-      TEquals("forbidden", x.error);
-    }
-  }
-
-
-  function test_invalid_filter() {
-    // COUCHDB-1199 - replication document with a filter field that was invalid
-    // crashed the CouchDB server.
-    var repDoc1 = {
-       _id: "rep1",
-       source: "couch_foo_test_db",
-       target: "couch_bar_test_db",
-       filter: "test/foofilter"
-    };
-
-    TEquals(true, repDb.save(repDoc1).ok);
-
-    waitForRep(repDb, repDoc1, "error");
-    repDoc1 = repDb.open(repDoc1._id);
-    TEquals("undefined", typeof repDoc1._replication_id);
-    TEquals("error", repDoc1._replication_state);
-
-    populate_db(dbA, docs1);
-    populate_db(dbB, []);
-
-    var repDoc2 = {
-       _id: "rep2",
-       source: dbA.name,
-       target: dbB.name,
-       filter: "test/foofilter"
-    };
-
-    TEquals(true, repDb.save(repDoc2).ok);
-
-    waitForRep(repDb, repDoc2, "error");
-    repDoc2 = repDb.open(repDoc2._id);
-    TEquals("undefined", typeof repDoc2._replication_id);
-    TEquals("error", repDoc2._replication_state);
-
-    var ddoc = {
-      _id: "_design/mydesign",
-      language : "javascript",
-      filters : {
-        myfilter : (function(doc, req) {
-          return true;
-        }).toString()
-      }
-    };
-
-    TEquals(true, dbA.save(ddoc).ok);
-
-    var repDoc3 = {
-       _id: "rep3",
-       source: dbA.name,
-       target: dbB.name,
-       filter: "mydesign/myfilter"
-    };
-
-    TEquals(true, repDb.save(repDoc3).ok);
-
-    waitForRep(repDb, repDoc3, "completed");
-    repDoc3 = repDb.open(repDoc3._id);
-    TEquals("string", typeof repDoc3._replication_id);
-    TEquals("completed", repDoc3._replication_state);
-  }
-
-
-  function test_rep_db_update_security() {
-    var dbA_copy = new CouchDB("test_suite_rep_db_a_copy");
-    var dbB_copy = new CouchDB("test_suite_rep_db_b_copy");
-    var repDoc1, repDoc2;
-    var xhr, i, doc, copy, new_doc;
-    var docs = makeDocs(1, 3);
-
-    populate_db(dbA, docs);
-    populate_db(dbB, docs);
-    populate_db(dbA_copy, []);
-    populate_db(dbB_copy, []);
-
-    repDoc1 = {
-      _id: "rep1",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
-      target: dbA_copy.name
-    };
-    repDoc2 = {
-      _id: "rep2",
-      source: CouchDB.protocol + CouchDB.host + "/" + dbB.name,
-      target: dbB_copy.name
-    };
-
-    TEquals(true, repDb.save(repDoc1).ok);
-    waitForRep(repDb, repDoc1, "completed");
-
-    T(repDb.setSecObj({
-      readers: {
-        names: ["joe"]
-      }
-    }).ok);
-
-    TEquals(true, repDb.save(repDoc2).ok);
-    waitForRep(repDb, repDoc2, "completed");
-  }
-
-  // run all the tests
-  var server_config = [
-    {
-      section: "couch_httpd_auth",
-      key: "iterations",
-      value: "1"
-    },
-    {
-      section: "replicator",
-      key: "db",
-      value: repDb.name
-    }
-  ];
-
-  repDb.deleteDb();
-  run_on_modified_server(server_config, simple_replication);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, filtered_replication);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, continuous_replication);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, by_doc_ids_replication);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, successive_identical_replications);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, identical_rep_docs);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, identical_continuous_rep_docs);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, rep_db_write_authorization);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, rep_doc_with_bad_rep_id);
-
-  var server_config_2 = server_config.concat([
-    {
-      section: "couch_httpd_auth",
-      key: "authentication_db",
-      value: usersDb.name
-    }
-  ]);
-
-  repDb.deleteDb();
-  usersDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config_2, test_user_ctx_validation);
-
-  repDb.deleteDb();
-  usersDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config_2, test_replication_credentials_delegation);
-
-  repDb.deleteDb();
-  restartServer();
-  continuous_replication_survives_restart();
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, swap_rep_db);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, compact_rep_db);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, rep_doc_field_validation);
-
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, test_invalid_filter);
-
-  repDb.deleteDb();
-  restartServer();
-  run_on_modified_server(server_config, test_rep_db_update_security);
-
-/*
- * Disabled, since error state would be set on the document only after
- * the exponential backoff retry done by the replicator database listener
- * terminates, which takes too much time for a unit test.
- */
-/*
- * repDb.deleteDb();
- * restartServer();
- * run_on_modified_server(server_config, error_state_replication);
- */
-
-
-  // cleanup
-  repDb.deleteDb();
-  usersDb.deleteDb();
-  dbA.deleteDb();
-  dbB.deleteDb();
-  (new CouchDB("test_suite_rep_db_2")).deleteDb();
-  (new CouchDB("test_suite_rep_db_c")).deleteDb();
-  (new CouchDB("test_suite_rep_db_a_copy")).deleteDb();
-  (new CouchDB("test_suite_rep_db_b_copy")).deleteDb();
-  (new CouchDB("test_suite_rep_db_c_copy")).deleteDb();
-};

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_bad_rep_id.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_bad_rep_id.js b/share/www/script/test/replicator_db_bad_rep_id.js
new file mode 100644
index 0000000..285b863
--- /dev/null
+++ b/share/www/script/test/replicator_db_bad_rep_id.js
@@ -0,0 +1,77 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_bad_rep_id = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+
+  function rep_doc_with_bad_rep_id() {
+    populate_db(dbA, docs1);
+    populate_db(dbB, []);
+
+    var repDoc = {
+      _id: "foo_rep",
+      source: dbA.name,
+      target: dbB.name,
+      replication_id: "1234abc"
+    };
+    T(repDb.save(repDoc).ok);
+
+    waitForRep(repDb, repDoc, "completed");
+    for (var i = 0; i < docs1.length; i++) {
+      var doc = docs1[i];
+      var copy = dbB.open(doc._id);
+      T(copy !== null);
+      T(copy.value === doc.value);
+    }
+
+    var repDoc1 = repDb.open(repDoc._id);
+    T(repDoc1 !== null);
+    T(repDoc1.source === repDoc.source);
+    T(repDoc1.target === repDoc.target);
+    T(repDoc1._replication_state === "completed",
+      "replication document with bad replication id failed");
+    T(typeof repDoc1._replication_state_time === "string");
+    T(typeof repDoc1._replication_id  === "string");
+    T(repDoc1._replication_id !== "1234abc");
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, rep_doc_with_bad_rep_id);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_by_doc_id.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_by_doc_id.js b/share/www/script/test/replicator_db_by_doc_id.js
new file mode 100644
index 0000000..1e1a385
--- /dev/null
+++ b/share/www/script/test/replicator_db_by_doc_id.js
@@ -0,0 +1,99 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_by_doc_id = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+
+  function by_doc_ids_replication() {
+    // to test that we can replicate docs with slashes in their IDs
+    var docs2 = docs1.concat([
+      {
+        _id: "_design/mydesign",
+        language : "javascript"
+      }
+    ]);
+
+    populate_db(dbA, docs2);
+    populate_db(dbB, []);
+
+    var repDoc = {
+      _id: "foo_cont_rep_doc",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name,
+      doc_ids: ["foo666", "foo3", "_design/mydesign", "foo999", "foo1"]
+    };
+    T(repDb.save(repDoc).ok);
+
+    waitForRep(repDb, repDoc, "completed");
+    var copy = dbB.open("foo1");
+    T(copy !== null);
+    T(copy.value === 11);
+
+    copy = dbB.open("foo2");
+    T(copy === null);
+
+    copy = dbB.open("foo3");
+    T(copy !== null);
+    T(copy.value === 33);
+
+    copy = dbB.open("foo666");
+    T(copy === null);
+
+    copy = dbB.open("foo999");
+    T(copy === null);
+
+    copy = dbB.open("_design/mydesign");
+    T(copy === null);
+
+    repDoc = repDb.open(repDoc._id);
+    T(typeof repDoc._replication_stats === "object", "doc has stats");
+    var stats = repDoc._replication_stats;
+    TEquals(3, stats.revisions_checked, "right # of revisions_checked");
+    TEquals(3, stats.missing_revisions_found, "right # of missing_revisions_found");
+    TEquals(3, stats.docs_read, "right # of docs_read");
+    TEquals(2, stats.docs_written, "right # of docs_written");
+    TEquals(1, stats.doc_write_failures, "right # of doc_write_failures");
+    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
+      "right checkpointed_source_seq");
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, by_doc_ids_replication);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_compact_rep_db.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_compact_rep_db.js b/share/www/script/test/replicator_db_compact_rep_db.js
new file mode 100644
index 0000000..0dea0ed
--- /dev/null
+++ b/share/www/script/test/replicator_db_compact_rep_db.js
@@ -0,0 +1,119 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_compact_rep_db = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var usersDb = replicator_db.usersDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+  var wait_rep_doc = replicator_db.wait_rep_doc;
+
+  function compact_rep_db() {
+    var dbA_copy = new CouchDB("test_suite_rep_db_a_copy");
+    var dbB_copy = new CouchDB("test_suite_rep_db_b_copy");
+    var repDoc1, repDoc2;
+    var xhr, i, doc, copy, new_doc;
+    var docs = makeDocs(1, 50);
+
+    populate_db(dbA, docs);
+    populate_db(dbB, docs);
+    populate_db(dbA_copy, []);
+    populate_db(dbB_copy, []);
+
+    repDoc1 = {
+      _id: "rep1",
+      source: CouchDB.protocol + CouchDB.host + "/" + dbA.name,
+      target: dbA_copy.name,
+      continuous: true
+    };
+    repDoc2 = {
+      _id: "rep2",
+      source: CouchDB.protocol + CouchDB.host + "/" + dbB.name,
+      target: dbB_copy.name,
+      continuous: true
+    };
+
+    TEquals(true, repDb.save(repDoc1).ok);
+    TEquals(true, repDb.save(repDoc2).ok);
+
+    TEquals(true, repDb.compact().ok);
+    TEquals(202, repDb.last_req.status);
+
+    waitForSeq(dbA, dbA_copy);
+    waitForSeq(dbB, dbB_copy);
+
+    while (repDb.info().compact_running) {};
+
+    for (i = 0; i < docs.length; i++) {
+      copy = dbA_copy.open(docs[i]._id);
+      T(copy !== null);
+      copy = dbB_copy.open(docs[i]._id);
+      T(copy !== null);
+    }
+
+    new_doc = {
+      _id: "foo666",
+      value: 666
+    };
+
+    TEquals(true, dbA.save(new_doc).ok);
+    TEquals(true, dbB.save(new_doc).ok);
+
+    waitForSeq(dbA, dbA_copy);
+    waitForSeq(dbB, dbB_copy);
+
+    copy = dbA.open(new_doc._id);
+    T(copy !== null);
+    TEquals(666, copy.value);
+    copy = dbB.open(new_doc._id);
+    T(copy !== null);
+    TEquals(666, copy.value);
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    },
+    {
+      section: "couch_httpd_auth",
+      key: "authentication_db",
+      value: usersDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, compact_rep_db);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+  usersDb.deleteDb();
+  (new CouchDB("test_suite_rep_db_a_copy")).deleteDb();
+  (new CouchDB("test_suite_rep_db_b_copy")).deleteDb();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_continuous.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_continuous.js b/share/www/script/test/replicator_db_continuous.js
new file mode 100644
index 0000000..a2b17d0
--- /dev/null
+++ b/share/www/script/test/replicator_db_continuous.js
@@ -0,0 +1,137 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_continuous = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+
+  function continuous_replication() {
+    populate_db(dbA, docs1);
+    populate_db(dbB, []);
+
+    var repDoc = {
+      _id: "foo_cont_rep_doc",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name,
+      continuous: true,
+      user_ctx: {
+        roles: ["_admin"]
+      }
+    };
+
+    T(repDb.save(repDoc).ok);
+
+    waitForSeq(dbA, dbB);
+    for (var i = 0; i < docs1.length; i++) {
+      var doc = docs1[i];
+      var copy = dbB.open(doc._id);
+      T(copy !== null);
+      T(copy.value === doc.value);
+    }
+
+    var tasks = JSON.parse(CouchDB.request("GET", "/_active_tasks").responseText);
+    TEquals(1, tasks.length, "1 active task");
+    TEquals(repDoc._id, tasks[0].doc_id, "replication doc id in active tasks");
+
+    // add another doc to source, it will be replicated to target
+    var docX = {
+      _id: "foo1000",
+      value: 1001
+    };
+
+    T(dbA.save(docX).ok);
+
+    waitForSeq(dbA, dbB);
+    var copy = dbB.open("foo1000");
+    T(copy !== null);
+    T(copy.value === 1001);
+
+    var repDoc1 = repDb.open(repDoc._id);
+    T(repDoc1 !== null);
+    T(repDoc1.source === repDoc.source);
+    T(repDoc1.target === repDoc.target);
+    T(repDoc1._replication_state === "triggered");
+    T(typeof repDoc1._replication_state_time === "string");
+    T(typeof repDoc1._replication_id  === "string");
+
+    // Design documents are only replicated to local targets if the respective
+    // replication document has a user_ctx filed with the "_admin" role in it.
+    var ddoc = {
+      _id: "_design/foobar",
+      language: "javascript"
+    };
+
+    T(dbA.save(ddoc).ok);
+
+    waitForSeq(dbA, dbB);
+    var ddoc_copy = dbB.open("_design/foobar");
+    T(ddoc_copy !== null);
+    T(ddoc.language === "javascript");
+
+    // update the design doc on source, test that the new revision is replicated
+    ddoc.language = "erlang";
+    T(dbA.save(ddoc).ok);
+    T(ddoc._rev.indexOf("2-") === 0);
+
+    waitForSeq(dbA, dbB);
+    ddoc_copy = dbB.open("_design/foobar");
+    T(ddoc_copy !== null);
+    T(ddoc_copy._rev === ddoc._rev);
+    T(ddoc.language === "erlang");
+
+    // stop replication by deleting the replication document
+    T(repDb.deleteDoc(repDoc1).ok);
+
+    // add another doc to source, it will NOT be replicated to target
+    var docY = {
+      _id: "foo666",
+      value: 999
+    };
+
+    T(dbA.save(docY).ok);
+
+    wait(200); // is there a way to avoid wait here?
+    var copy = dbB.open("foo666");
+    T(copy === null);
+  }
+
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, continuous_replication);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_credential_delegation.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_credential_delegation.js b/share/www/script/test/replicator_db_credential_delegation.js
new file mode 100644
index 0000000..7dd9d18
--- /dev/null
+++ b/share/www/script/test/replicator_db_credential_delegation.js
@@ -0,0 +1,149 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_credential_delegation = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var usersDb = replicator_db.usersDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+  var wait_rep_doc = replicator_db.wait_rep_doc;
+
+  function test_replication_credentials_delegation() {
+    populate_db(usersDb, []);
+
+    var joeUserDoc = CouchDB.prepareUserDoc({
+      name: "joe",
+      roles: ["god", "erlanger"]
+    }, "erly");
+    T(usersDb.save(joeUserDoc).ok);
+
+    var ddoc = {
+      _id: "_design/beer",
+      language: "javascript"
+    };
+    populate_db(dbA, docs1.concat([ddoc]));
+    populate_db(dbB, []);
+
+    T(dbB.setSecObj({
+      admins: {
+        names: [],
+        roles: ["god"]
+      }
+    }).ok);
+
+    var server_admins_config = [
+      {
+        section: "couch_httpd_auth",
+        key: "iterations",
+        value: "1"
+      },
+      {
+        section: "admins",
+        key: "fdmanana",
+        value: "qwerty"
+      }
+    ];
+
+    run_on_modified_server(server_admins_config, function() {
+
+      T(CouchDB.login("fdmanana", "qwerty").ok);
+      T(CouchDB.session().userCtx.name === "fdmanana");
+      T(CouchDB.session().userCtx.roles.indexOf("_admin") !== -1);
+
+      var repDoc = {
+        _id: "foo_rep_del_doc_1",
+        source: dbA.name,
+        target: dbB.name,
+        user_ctx: {
+          name: "joe",
+          roles: ["erlanger"]
+        }
+      };
+
+      T(repDb.save(repDoc).ok);
+
+      waitForRep(repDb, repDoc, "completed");
+      for (var i = 0; i < docs1.length; i++) {
+        var doc = docs1[i];
+        var copy = dbB.open(doc._id);
+        T(copy !== null);
+        T(copy.value === doc.value);
+      }
+
+      // design doc was not replicated, because joe is not an admin of db B
+      var doc = dbB.open(ddoc._id);
+      T(doc === null);
+
+      // now test the same replication but putting the role "god" in the
+      // delegation user context property
+      var repDoc2 = {
+        _id: "foo_rep_del_doc_2",
+        source: dbA.name,
+        target: dbB.name,
+        user_ctx: {
+          name: "joe",
+          roles: ["erlanger", "god"]
+        }
+      };
+      T(repDb.save(repDoc2).ok);
+
+      waitForRep(repDb, repDoc2, "completed");
+      for (var i = 0; i < docs1.length; i++) {
+        var doc = docs1[i];
+        var copy = dbB.open(doc._id);
+        T(copy !== null);
+        T(copy.value === doc.value);
+      }
+
+      // because anyone with a 'god' role is an admin of db B, a replication
+      // that is delegated to a 'god' role can write design docs to db B
+      doc = dbB.open(ddoc._id);
+      T(doc !== null);
+      T(doc.language === ddoc.language);
+    });
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    },
+    {
+      section: "couch_httpd_auth",
+      key: "authentication_db",
+      value: usersDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, test_replication_credentials_delegation);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+  usersDb.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_field_validation.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_field_validation.js b/share/www/script/test/replicator_db_field_validation.js
new file mode 100644
index 0000000..167bdcc
--- /dev/null
+++ b/share/www/script/test/replicator_db_field_validation.js
@@ -0,0 +1,178 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_field_validation = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var usersDb = replicator_db.usersDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+  var wait_rep_doc = replicator_db.wait_rep_doc;
+
+  function rep_doc_field_validation() {
+    var docs = makeDocs(1, 5);
+
+    populate_db(dbA, docs);
+    populate_db(dbB, []);
+
+    var repDoc = {
+       _id: "rep1",
+       target: dbB.name
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because source field is missing");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: 123,
+       target: dbB.name
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because source field is a number");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because target field is missing");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: null
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because target field is null");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: { url: 123 }
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because target.url field is not a string");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: { url: dbB.name, auth: null }
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because target.auth field is null");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: { url: dbB.name, auth: "foo:bar" }
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because target.auth field is not an object");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: dbB.name,
+       continuous: "true"
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because continuous is not a boolean");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+
+    repDoc = {
+       _id: "rep1",
+       source: dbA.name,
+       target: dbB.name,
+       filter: 123
+    };
+
+    try {
+      repDb.save(repDoc);
+      T(false, "should have failed because filter is not a string");
+    } catch (x) {
+      TEquals("forbidden", x.error);
+    }
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    },
+    {
+      section: "couch_httpd_auth",
+      key: "authentication_db",
+      value: usersDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, rep_doc_field_validation);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+  usersDb.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_filtered.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_filtered.js b/share/www/script/test/replicator_db_filtered.js
new file mode 100644
index 0000000..31c78a7
--- /dev/null
+++ b/share/www/script/test/replicator_db_filtered.js
@@ -0,0 +1,105 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_filtered = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var waitForRep = replicator_db.waitForRep;
+
+  function filtered_replication() {
+    var docs2 = docs1.concat([
+      {
+        _id: "_design/mydesign",
+        language : "javascript",
+        filters : {
+          myfilter : (function(doc, req) {
+            return (doc.value % 2) !== Number(req.query.myparam);
+          }).toString()
+        }
+      }
+    ]);
+
+    populate_db(dbA, docs2);
+    populate_db(dbB, []);
+
+    var repDoc = {
+      _id: "foo_filt_rep_doc",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name,
+      filter: "mydesign/myfilter",
+      query_params: {
+        myparam: 1
+      }
+    };
+    T(repDb.save(repDoc).ok);
+
+    waitForRep(repDb, repDoc, "completed");
+    for (var i = 0; i < docs2.length; i++) {
+      var doc = docs2[i];
+      var copy = dbB.open(doc._id);
+
+      if (typeof doc.value === "number") {
+        if ((doc.value % 2) !== 1) {
+          T(copy !== null);
+          T(copy.value === doc.value);
+        } else {
+          T(copy === null);
+        }
+      }
+    }
+
+    var repDoc1 = repDb.open(repDoc._id);
+    T(repDoc1 !== null);
+    T(repDoc1.source === repDoc.source);
+    T(repDoc1.target === repDoc.target);
+    T(repDoc1._replication_state === "completed", "filtered");
+    T(typeof repDoc1._replication_state_time === "string");
+    T(typeof repDoc1._replication_id  === "string");
+    T(typeof repDoc1._replication_stats === "object", "doc has stats");
+    var stats = repDoc1._replication_stats;
+    TEquals(2, stats.revisions_checked, "right # of revisions_checked");
+    TEquals(2, stats.missing_revisions_found, "right # of missing_revisions_found");
+    TEquals(2, stats.docs_read, "right # of docs_read");
+    TEquals(1, stats.docs_written, "right # of docs_written");
+    TEquals(1, stats.doc_write_failures, "right # of doc_write_failures");
+    TEquals(dbA.info().update_seq, stats.checkpointed_source_seq,
+      "right checkpointed_source_seq");
+  }
+
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, filtered_replication);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_identical.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_identical.js b/share/www/script/test/replicator_db_identical.js
new file mode 100644
index 0000000..cdf4592
--- /dev/null
+++ b/share/www/script/test/replicator_db_identical.js
@@ -0,0 +1,87 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_identical = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+
+  // test the case where multiple replication docs (different IDs)
+  // describe in fact the same replication (source, target, etc)
+  function identical_rep_docs() {
+    populate_db(dbA, docs1);
+    populate_db(dbB, []);
+
+    var repDoc1 = {
+      _id: "foo_dup_rep_doc_1",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name
+    };
+    var repDoc2 = {
+      _id: "foo_dup_rep_doc_2",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name
+    };
+
+    T(repDb.save(repDoc1).ok);
+    T(repDb.save(repDoc2).ok);
+
+    waitForRep(repDb, repDoc1, "completed");
+    for (var i = 0; i < docs1.length; i++) {
+      var doc = docs1[i];
+      var copy = dbB.open(doc._id);
+      T(copy !== null);
+      T(copy.value === doc.value);
+    }
+
+    repDoc1 = repDb.open("foo_dup_rep_doc_1");
+    T(repDoc1 !== null);
+    T(repDoc1._replication_state === "completed", "identical");
+    T(typeof repDoc1._replication_state_time === "string");
+    T(typeof repDoc1._replication_id  === "string");
+
+    repDoc2 = repDb.open("foo_dup_rep_doc_2");
+    T(repDoc2 !== null);
+    T(typeof repDoc2._replication_state === "undefined");
+    T(typeof repDoc2._replication_state_time === "undefined");
+    T(repDoc2._replication_id === repDoc1._replication_id);
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, identical_rep_docs);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ae048910/share/www/script/test/replicator_db_identical_continuous.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/replicator_db_identical_continuous.js b/share/www/script/test/replicator_db_identical_continuous.js
new file mode 100644
index 0000000..5e7f151
--- /dev/null
+++ b/share/www/script/test/replicator_db_identical_continuous.js
@@ -0,0 +1,132 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replicator_db_identical_continuous = function(debug) {
+
+  if (debug) debugger;
+
+  var populate_db = replicator_db.populate_db;
+  var docs1 = replicator_db.docs1;
+  var dbA = replicator_db.dbA;
+  var dbB = replicator_db.dbB;
+  var repDb = replicator_db.repDb;
+  var wait = replicator_db.wait;
+  var waitForRep = replicator_db.waitForRep;
+  var waitForSeq = replicator_db.waitForSeq;
+  var wait_rep_doc = replicator_db.wait_rep_doc;
+
+  // test the case where multiple replication docs (different IDs)
+  // describe in fact the same continuous replication (source, target, etc)
+  function identical_continuous_rep_docs() {
+    populate_db(dbA, docs1);
+    populate_db(dbB, []);
+
+    var repDoc1 = {
+      _id: "foo_dup_cont_rep_doc_1",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name,
+      continuous: true
+    };
+    var repDoc2 = {
+      _id: "foo_dup_cont_rep_doc_2",
+      source: "http://" + CouchDB.host + "/" + dbA.name,
+      target: dbB.name,
+      continuous: true
+    };
+
+    T(repDb.save(repDoc1).ok);
+    T(repDb.save(repDoc2).ok);
+
+    waitForSeq(dbA, dbB);
+    for (var i = 0; i < docs1.length; i++) {
+      var doc = docs1[i];
+      var copy = dbB.open(doc._id);
+      T(copy !== null);
+      T(copy.value === doc.value);
+    }
+
+    repDoc1 = repDb.open("foo_dup_cont_rep_doc_1");
+    T(repDoc1 !== null);
+    T(repDoc1._replication_state === "triggered");
+    T(typeof repDoc1._replication_state_time === "string");
+    T(typeof repDoc1._replication_id  === "string");
+
+    repDoc2 = repDb.open("foo_dup_cont_rep_doc_2");
+    T(repDoc2 !== null);
+    T(typeof repDoc2._replication_state === "undefined");
+    T(typeof repDoc2._replication_state_time === "undefined");
+    T(repDoc2._replication_id === repDoc1._replication_id);
+
+    var newDoc = {
+      _id: "foo666",
+      value: 999
+    };
+    T(dbA.save(newDoc).ok);
+
+    waitForSeq(dbA, dbB);
+    var copy = dbB.open("foo666");
+    T(copy !== null);
+    T(copy.value === 999);
+
+    // deleting second replication doc, doesn't affect the 1st one and
+    // neither it stops the replication
+    T(repDb.deleteDoc(repDoc2).ok);
+    repDoc1 = repDb.open("foo_dup_cont_rep_doc_1");
+    T(repDoc1 !== null);
+    T(repDoc1._replication_state === "triggered");
+    T(typeof repDoc1._replication_state_time === "string");
+
+    var newDoc2 = {
+        _id: "foo5000",
+        value: 5000
+    };
+    T(dbA.save(newDoc2).ok);
+
+    waitForSeq(dbA, dbB);
+    var copy = dbB.open("foo5000");
+    T(copy !== null);
+    T(copy.value === 5000);
+
+    // deleting the 1st replication document stops the replication
+    T(repDb.deleteDoc(repDoc1).ok);
+    var newDoc3 = {
+        _id: "foo1983",
+        value: 1983
+    };
+    T(dbA.save(newDoc3).ok);
+
+    wait(wait_rep_doc); //how to remove wait?
+    var copy = dbB.open("foo1983");
+    T(copy === null);
+  }
+
+  var server_config = [
+    {
+      section: "couch_httpd_auth",
+      key: "iterations",
+      value: "1"
+    },
+    {
+      section: "replicator",
+      key: "db",
+      value: repDb.name
+    }
+  ];
+
+  repDb.deleteDb();
+  run_on_modified_server(server_config, identical_continuous_rep_docs);
+
+  // cleanup
+  repDb.deleteDb();
+  dbA.deleteDb();
+  dbB.deleteDb();
+}
\ No newline at end of file


Mime
View raw message