You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2019/07/31 19:28:23 UTC

[couchdb] 01/01: Switch to only using elixir replication integration test

This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch jenkins-switch-to-elixir-replicator-only
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 9bad779542f2c2204b298046d1fba235b3eb484d
Author: Nick Vatamaniuc <va...@apache.org>
AuthorDate: Wed Jul 31 15:24:26 2019 -0400

    Switch to only using elixir replication integration test
    
    And remove the js version. Elixir test has been running decently on Travis from
    what I observed. However, it was disabled on jenkins runs. With a recent
    hardware upgrade, perhaps there is chance this test will start passing there
    too.
---
 test/elixir/test/replication_test.exs |    2 -
 test/javascript/tests/replication.js  | 1920 ---------------------------------
 2 files changed, 1922 deletions(-)

diff --git a/test/elixir/test/replication_test.exs b/test/elixir/test/replication_test.exs
index 6d4360d..11687ab 100644
--- a/test/elixir/test/replication_test.exs
+++ b/test/elixir/test/replication_test.exs
@@ -16,8 +16,6 @@ defmodule ReplicationTest do
   # happens for JavaScript tests.
   @moduletag config: [{"replicator", "startup_jitter", "0"}]
 
-  @moduletag :skip_on_jenkins
-
   test "source database not found with host" do
     name = random_db_name()
     src_url = "http://127.0.0.1:15984/" <> name <> "_src"
diff --git a/test/javascript/tests/replication.js b/test/javascript/tests/replication.js
deleted file mode 100644
index ba586b4..0000000
--- a/test/javascript/tests/replication.js
+++ /dev/null
@@ -1,1920 +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.replication = function(debug) {
-//  return console.log('TODO');
-  if (debug) debugger;
-
-  var host = CouchDB.host;
-  // as we change names during execution, do NOT use test_suite_db or a
-  // pre-computed value like ''+sourceDb.name (compute only on use)
-  var sourceDb;
-  var targetDb;
-
-  var dbPairsPrefixes = [
-    {
-      source: "",
-      target: ""
-    },
-    {
-      source: CouchDB.protocol + host + "/",
-      target: ""
-    },
-    {
-      source: "",
-      target: CouchDB.protocol + host + "/"
-    },
-    {
-      source: CouchDB.protocol + host + "/",
-      target: CouchDB.protocol + host + "/"
-    }
-  ];
-
-  var att1_data = CouchDB.request("GET", "/_utils/script/test/lorem.txt");
-  att1_data = att1_data.responseText;
-
-  var att2_data = CouchDB.request("GET", "/_utils/script/test/lorem_b64.txt");
-  att2_data = att2_data.responseText;
-
-  var sourceInfo, targetInfo;
-  var docs, doc, copy;
-  var repResult;
-  var i, j, k;
-
-
-  function makeAttData(minSize) {
-    var data = att1_data;
-
-    while (data.length < minSize) {
-      data = data + att1_data;
-    }
-    return data;
-  }
-
-
-  function runAllNodes(callback) {
-    // new and fancy: clustered version: pull cluster_members and walk over all of them
-    var xhr = CouchDB.request("GET", "/_membership");
-    T(xhr.status === 200);
-    JSON.parse(xhr.responseText).cluster_nodes.forEach(callback);
-  }
-
-  function runFirstNode(callback) {
-    // new and fancy: clustered version: pull cluster_members and walk over all of them
-    var xhr = CouchDB.request("GET", "/_membership");
-    T(xhr.status === 200);
-    var node = JSON.parse(xhr.responseText).cluster_nodes[0];
-    return callback(node);
-  }
-
-  function getCompressionInfo() {
-    return runFirstNode(function(node) {
-      var xhr = CouchDB.request(
-        "GET",
-        "_node/" + node + "/_config/attachments"
-      );
-      T(xhr.status === 200);
-      var res = JSON.parse(xhr.responseText);
-      return {"level": res.compression_level, "types": res.compressible_types};
-    });
-  }
-
-  function enableAttCompression(level, types) {
-    runAllNodes(function(node) {
-      var xhr = CouchDB.request(
-        "PUT",
-        "_node/" + node + "/_config/attachments/compression_level",
-        {
-          body: JSON.stringify(level),
-          headers: {"X-Couch-Persist": "false"}
-        }
-      );
-      T(xhr.status === 200);
-      xhr = CouchDB.request(
-        "PUT",
-        "_node/" + node + "/_config/attachments/compressible_types",
-        {
-          body: JSON.stringify(types),
-          headers: {"X-Couch-Persist": "false"}
-        }
-      );
-      T(xhr.status === 200);
-    });
-  }
-
-  function disableAttCompression() {
-    runAllNodes(function(node) {
-      var xhr = CouchDB.request(
-        "PUT",
-        "_node/" + node + "/_config/attachments/compression_level",
-        {
-          body: JSON.stringify("0"),
-          headers: {"X-Couch-Persist": "false"}
-        }
-      );
-      T(xhr.status === 200);
-    });
-  }
-
-
-  function populateSourceDb(docs, dontRecreateDb) {
-    if(dontRecreateDb !== true) {
-      if(sourceDb) {
-        sourceDb.deleteDb();
-      }
-      sourceDb = new CouchDB(get_random_db_name() + "_src",{"X-Couch-Full-Commit":"false"});
-      sourceDb.createDb();
-    }
-    for (var i = 0; i < docs.length; i++) {
-      var doc = docs[i];
-      delete doc._rev;
-    }
-    if (docs.length > 0) {
-      sourceDb.bulkSave(docs);
-    }
-  }
-  function populateTargetDb(docs, dontRecreateDb) {
-    if(dontRecreateDb !== true) {
-      if(targetDb) {
-        targetDb.deleteDb();
-      }
-      targetDb = new CouchDB(get_random_db_name() + "_tgt",{"X-Couch-Full-Commit":"false"});
-      targetDb.createDb();
-    }
-    for (var i = 0; i < docs.length; i++) {
-      var doc = docs[i];
-      delete doc._rev;
-    }
-    if (docs.length > 0) {
-      targetDb.bulkSave(docs);
-    }
-  }
-
-
-  function addAtt(db, doc, attName, attData, type) {
-    var uri = "/" + db.name + "/" + encodeURIComponent(doc._id) + "/" + attName;
-
-    if (doc._rev) {
-      uri += "?rev=" + doc._rev;
-    }
-
-    var xhr = CouchDB.request("PUT", uri, {
-      headers: {
-        "Content-Type": type
-      },
-      body: attData
-    });
-
-    T(xhr.status === 201);
-    doc._rev = JSON.parse(xhr.responseText).rev;
-  }
-
-
-  function compareObjects(o1, o2) {
-    for (var p in o1) {
-      if (o1[p] === null && o2[p] !== null) {
-        return false;
-      } else if (typeof o1[p] === "object") {
-        if ((typeof o2[p] !== "object") || o2[p] === null) {
-          return false;
-        }
-        if (!arguments.callee(o1[p], o2[p])) {
-          return false;
-        }
-      } else {
-        if (o1[p] !== o2[p]) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
-
-  function getTask(rep_id, delay) {
-    var t0 = new Date();
-    var t1;
-    do {
-      var xhr = CouchDB.request("GET", "/_active_tasks");
-      var tasks = JSON.parse(xhr.responseText);
-      for(var i = 0; i < tasks.length; i++) {
-        if(tasks[i].replication_id == repResult._local_id) {
-          return tasks[i];
-        }
-      }
-      sleep(500);
-      t1 = new Date();
-    } while((t1 - t0) <= delay);
-
-    return null;
-  }
-
-  function getSourceLastSeq(sourceDb) {
-      return sourceDb.changes({"since":"now"}).last_seq;
-  }
-
-  function waitForSeq(sourceDb, targetDb, rep_id) {
-    var sourceSeq = getSourceLastSeq(sourceDb),
-        t0 = new Date(),
-        t1,
-        ms = 30000;
-
-    do {
-      var task = getTask(rep_id, 0);
-      if(task && task["through_seq"] == sourceSeq) {
-        return;
-      }
-      t1 = new Date();
-      sleep(500);
-    } while (((t1 - t0) <= ms));
-    throw(Error('Timeout waiting for replication through_seq = source update seq'));
-  }
-
-  function waitReplicationTaskStop(rep_id) {
-      var t0 = new Date(),
-          t1,
-          ms = 30000;
-      do {
-        var task = getTask(rep_id, 0);
-        if(task == null) {
-            return;
-        }
-        t1 = new Date();
-        sleep(500);
-      } while (((t1 - t0) <= ms));
-      throw(Error('Timeout waiting for replication task stop' + rep_id));
-  }
-
-  // test simple replications (not continuous, not filtered), including
-  // conflict creation
-  docs = makeDocs(1, 21);
-  docs.push({
-    _id: "_design/foo",
-    language: "javascript",
-    value: "ddoc"
-  });
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    populateTargetDb([]);
-
-    // add some attachments
-    for (j = 10; j < 15; j++) {
-      addAtt(sourceDb, docs[j], "readme.txt", att1_data, "text/plain");
-    }
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    TEquals('string', typeof repResult.session_id);
-    // we can't rely on sequences in a cluster
-    //TEquals(repResult.source_last_seq, sourceInfo.update_seq);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(1, repResult.history.length);
-    TEquals(repResult.history[0].session_id, repResult.session_id);
-    TEquals('string', typeof repResult.history[0].start_time);
-    TEquals('string', typeof repResult.history[0].end_time);
-    TEquals(0, repResult.history[0].start_last_seq);
-    // we can't rely on sequences in a cluster
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(sourceInfo.doc_count, repResult.history[0].missing_checked);
-    TEquals(sourceInfo.doc_count, repResult.history[0].missing_found);
-    TEquals(sourceInfo.doc_count, repResult.history[0].docs_read);
-    TEquals(sourceInfo.doc_count, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      T(copy !== null);
-      TEquals(true, compareObjects(doc, copy));
-
-      if (j >= 10 && j < 15) {
-        var atts = copy._attachments;
-        TEquals('object', typeof atts);
-        TEquals('object', typeof atts["readme.txt"]);
-        TEquals(2, atts["readme.txt"].revpos);
-        TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-        TEquals(true, atts["readme.txt"].stub);
-
-        var att_copy = CouchDB.request(
-          "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-        ).responseText;
-        TEquals(att1_data.length, att_copy.length);
-        TEquals(att1_data, att_copy);
-      }
-    }
-
-
-    // add one more doc to source, more attachments to some existing docs
-    // and replicate again
-    var newDoc = {
-      _id: "foo666",
-      value: "d"
-    };
-    TEquals(true, sourceDb.save(newDoc).ok);
-
-    // add some more attachments
-    for (j = 10; j < 15; j++) {
-      addAtt(sourceDb, docs[j], "data.dat", att2_data, "application/binary");
-    }
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(targetInfo.doc_count, sourceInfo.doc_count);
-
-    TEquals('string', typeof repResult.session_id);
-    // we can't rely on sequences in a cluster
-    //TEquals(sourceInfo.update_seq, repResult.source_last_seq);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(2, repResult.history.length);
-    TEquals(repResult.history[0].session_id, repResult.session_id);
-    TEquals('string', typeof repResult.history[0].start_time);
-    TEquals('string', typeof repResult.history[0].end_time);
-    // we can't rely on sequences in a cluster
-    //TEquals((sourceInfo.update_seq - 6), repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(6, repResult.history[0].missing_checked);
-    TEquals(6, repResult.history[0].missing_found);
-    TEquals(6, repResult.history[0].docs_read);
-    TEquals(6, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(newDoc._id);
-    T(copy !== null);
-    TEquals(newDoc._id, copy._id);
-    TEquals(newDoc.value, copy.value);
-
-    for (j = 10; j < 15; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      T(copy !== null);
-      TEquals(true, compareObjects(doc, copy));
-
-      var atts = copy._attachments;
-      TEquals('object', typeof atts);
-      TEquals('object', typeof atts["readme.txt"]);
-      TEquals(2, atts["readme.txt"].revpos);
-      TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-      TEquals(true, atts["readme.txt"].stub);
-
-      var att1_copy = CouchDB.request(
-        "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-      ).responseText;
-      TEquals(att1_data.length, att1_copy.length);
-      TEquals(att1_data, att1_copy);
-
-      TEquals('object', typeof atts["data.dat"]);
-      TEquals(3, atts["data.dat"].revpos);
-      TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
-      TEquals(true, atts["data.dat"].stub);
-
-      var att2_copy = CouchDB.request(
-        "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
-      ).responseText;
-      TEquals(att2_data.length, att2_copy.length);
-      TEquals(att2_data, att2_copy);
-    }
-
-    // test deletion is replicated
-    doc = sourceDb.open(docs[1]._id);
-    TEquals(true, sourceDb.deleteDoc(doc).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(targetInfo.doc_count, sourceInfo.doc_count);
-    TEquals(targetInfo.doc_del_count, sourceInfo.doc_del_count);
-    TEquals(1, targetInfo.doc_del_count);
-
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(3, repResult.history.length);
-    // we can't rely on sequences in a cluster
-    //TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(1, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(docs[1]._id);
-    TEquals(null, copy);
-
-    var changes = targetDb.changes({since: 0});
-    // there is no guarantee of ordering also
-    // however: the doc has to appear somewhere
-    //var idx = changes.results.length - 1;
-    var changesResDoc1 = changes.results.filter(function(c){return c.id == docs[1]._id;});
-    TEquals(1, changesResDoc1.length);
-    TEquals(docs[1]._id, changesResDoc1[0].id);
-    TEquals(true, changesResDoc1[0].deleted);
-
-    // test conflict
-    doc = sourceDb.open(docs[0]._id);
-    doc.value = "white";
-    TEquals(true, sourceDb.save(doc).ok);
-
-    copy = targetDb.open(docs[0]._id);
-    copy.value = "black";
-    TEquals(true, targetDb.save(copy).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(4, repResult.history.length);
-    // we can't rely on sequences in a cluster
-    //TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(1, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(docs[0]._id, {conflicts: true});
-
-    TEquals(0, copy._rev.indexOf("2-"));
-    TEquals(true, copy._conflicts instanceof Array);
-    TEquals(1, copy._conflicts.length);
-    TEquals(0, copy._conflicts[0].indexOf("2-"));
-
-    // replicate again with conflict
-    doc.value = "yellow";
-    TEquals(true, sourceDb.save(doc).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(5, repResult.history.length);
-    // we can't rely on sequences in a cluster
-    //TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(1, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(docs[0]._id, {conflicts: true});
-
-    TEquals(0, copy._rev.indexOf("3-"));
-    TEquals(true, copy._conflicts instanceof Array);
-    TEquals(1, copy._conflicts.length);
-    TEquals(0, copy._conflicts[0].indexOf("2-"));
-
-    // resolve the conflict
-    TEquals(true, targetDb.deleteDoc({_id: copy._id, _rev: copy._conflicts[0]}).ok);
-
-    // replicate again, check there are no more conflicts
-    doc.value = "rainbow";
-    TEquals(true, sourceDb.save(doc).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(6, repResult.history.length);
-    // we can't rely on sequences in a cluster
-    //TEquals((sourceInfo.update_seq - 1), repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(1, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(docs[0]._id, {conflicts: true});
-
-    TEquals(0, copy._rev.indexOf("4-"));
-    TEquals('undefined', typeof copy._conflicts);
-
-    // test that revisions already in a target are not copied
-    TEquals(true, sourceDb.save({_id: "foo1", value: 111}).ok);
-    TEquals(true, targetDb.save({_id: "foo1", value: 111}).ok);
-    TEquals(true, sourceDb.save({_id: "foo2", value: 222}).ok);
-    TEquals(true, sourceDb.save({_id: "foo3", value: 333}).ok);
-    TEquals(true, targetDb.save({_id: "foo3", value: 333}).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    // we can't rely on sequences in a cluster
-    //TEquals(sourceInfo.update_seq, repResult.source_last_seq);
-    //TEquals(sourceInfo.update_seq - 3, repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(3, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    TEquals(true, sourceDb.save({_id: "foo4", value: 444}).ok);
-    TEquals(true, targetDb.save({_id: "foo4", value: 444}).ok);
-    TEquals(true, sourceDb.save({_id: "foo5", value: 555}).ok);
-    TEquals(true, targetDb.save({_id: "foo5", value: 555}).ok);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    // we can't rely on sequences in a cluster
-    //TEquals(sourceInfo.update_seq, repResult.source_last_seq);
-    //TEquals(sourceInfo.update_seq - 2, repResult.history[0].start_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].end_last_seq);
-    //TEquals(sourceInfo.update_seq, repResult.history[0].recorded_seq);
-    TEquals(2, repResult.history[0].missing_checked);
-    TEquals(0, repResult.history[0].missing_found);
-    TEquals(0, repResult.history[0].docs_read);
-    TEquals(0, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-    TEquals(true, repResult.no_changes);
-    sourceInfo = sourceDb.info();
-    // we can't rely on sequences in a cluster
-    //TEquals(sourceInfo.update_seq, repResult.source_last_seq);
-  }
-
-
-  // test error when source database does not exist
-  try {
-    CouchDB.replicate("foobar", "test_suite_db");
-    T(false, "should have failed with db_not_found error");
-  } catch (x) {
-    TEquals("db_not_found", x.error);
-  }
-
-  // validate COUCHDB-317
-  try {
-    CouchDB.replicate("/foobar", "test_suite_db");
-    T(false, "should have failed with db_not_found error");
-  } catch (x) {
-    TEquals("db_not_found", x.error);
-  }
-
-  try {
-    CouchDB.replicate(CouchDB.protocol + host + "/foobar", "test_suite_db");
-    T(false, "should have failed with db_not_found error");
-  } catch (x) {
-    TEquals("db_not_found", x.error);
-  }
-
-
-  // test since_seq parameter
-  docs = makeDocs(1, 6);
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    populateTargetDb([]);
-    // sequences are no longer simple numbers - so pull #3 from a feed
-    var since_seq = sourceDb.changes().results[2].seq;
-
-    var expected_ids = [];
-    var changes = sourceDb.changes({since: JSON.stringify(since_seq)});
-    for (j = 0; j < changes.results.length; j++) {
-      expected_ids.push(changes.results[j].id);
-    }
-    TEquals(2, expected_ids.length, "2 documents since since_seq");
-
-    // For OTP < R14B03, temporary child specs are kept in the supervisor
-    // after the child terminates, so cancel the replication to delete the
-    // child spec in those OTP releases, otherwise since_seq will have no
-    // effect.
-    try {
-      CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {body: {cancel: true}}
-      );
-    } catch (x) {
-      // OTP R14B03 onwards
-      TEquals("not_found", x.error);
-    }
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {body: {since_seq: since_seq}}
-    );
-    // Same reason as before. But here we don't want since_seq to affect
-    // subsequent replications, so we need to delete the child spec from the
-    // supervisor (since_seq is not used to calculate the replication ID).
-    try {
-      CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {body: {cancel: true}}
-      );
-    } catch (x) {
-      // OTP R14B03 onwards
-      TEquals("not_found", x.error);
-    }
-    TEquals(true, repResult.ok);
-    TEquals(2, repResult.history[0].missing_checked);
-    TEquals(2, repResult.history[0].missing_found);
-    TEquals(2, repResult.history[0].docs_read);
-    TEquals(2, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      if (expected_ids.indexOf(doc._id) === -1) {
-        T(copy === null);
-      } else {
-        T(copy !== null);
-        TEquals(true, compareObjects(doc, copy));
-      }
-    }
-  }
-
-
-  // test errors due to doc validate_doc_update functions in the target endpoint
-  docs = makeDocs(1, 8);
-  docs[2]["_attachments"] = {
-    "hello.txt": {
-      "content_type": "text/plain",
-      "data": "aGVsbG8gd29ybGQ="  // base64:encode("hello world")
-    }
-  };
-  var ddoc = {
-    _id: "_design/test",
-    language: "javascript",
-    validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
-      if ((newDoc.integer % 2) !== 0) {
-        throw {forbidden: "I only like multiples of 2."};
-      }
-    }).toString()
-  };
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    populateTargetDb([ddoc]);
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name
-    );
-    TEquals(true, repResult.ok);
-    TEquals(7, repResult.history[0].missing_checked);
-    TEquals(7, repResult.history[0].missing_found);
-    TEquals(7, repResult.history[0].docs_read);
-    TEquals(3, repResult.history[0].docs_written);
-    TEquals(4, repResult.history[0].doc_write_failures);
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      if (doc.integer % 2 === 0) {
-        T(copy !== null);
-        TEquals(copy.integer, doc.integer);
-      } else {
-        T(copy === null);
-      }
-    }
-  }
-
-
-  // test create_target option
-  docs = makeDocs(1, 2);
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    targetDb.deleteDb();
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {body: {create_target: true}}
-    );
-    TEquals(true, repResult.ok);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-    TEquals(sourceInfo.update_seq, targetInfo.update_seq);
-  }
-
-
-  // test filtered replication
-  docs = makeDocs(1, 31);
-  docs.push({
-    _id: "_design/mydesign",
-    language: "javascript",
-    filters: {
-      myfilter: (function(doc, req) {
-        var modulus = Number(req.query.modulus);
-        var special = req.query.special;
-        return (doc.integer % modulus === 0) || (doc.string === special);
-      }).toString()
-    }
-  });
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    populateTargetDb([]);
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          filter: "mydesign/myfilter",
-          query_params: {
-            modulus: "2",
-            special: "7"
-          }
-        }
-      }
-    );
-
-    TEquals(true, repResult.ok);
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      if ((doc.integer && (doc.integer % 2 === 0)) || (doc.string === "7")) {
-
-        T(copy !== null);
-        TEquals(true, compareObjects(doc, copy));
-      } else {
-        TEquals(null, copy);
-      }
-    }
-
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(1, repResult.history.length);
-    // We (incorrectly) don't record update sequences for things
-    // that don't pass the changse feed filter. Historically the
-    // last document to pass was the second to last doc which has
-    // an update sequence of 30. Work that has been applied to avoid
-    // conflicts from duplicate IDs breaking _bulk_docs updates added
-    // a sort to the logic which changes this. Now the last document
-    // to pass has an doc id of "8" and is at update_seq 29 (because only
-    // "9" and the design doc are after it).
-    //
-    // In the future the fix ought to be that we record that update
-    // sequence of the database. BigCouch has some existing work on
-    // this in the clustered case because if you have very few documents
-    // that pass the filter then (given single node's behavior) you end
-    // up having to rescan a large portion of the database.
-    // we can't rely on sequences in a cluster
-    // not only can one figure appear twice (at least for n>1), there's also hashes involved now - so comparing seq==29 is lottery (= cutting off hashes is nonsense)
-    // above, there was brute-force comparing all attrs of all docs - now we did check if excluded docs did NOT make it
-    // in any way, we can't rely on sequences in a cluster (so leave out)
-    //TEquals(29, repResult.source_last_seq);
-    //TEquals(0, repResult.history[0].start_last_seq);
-    //TEquals(29, repResult.history[0].end_last_seq);
-    //TEquals(29, repResult.history[0].recorded_seq);
-    // 16 => 15 docs with even integer field  + 1 doc with string field "7"
-    TEquals(16, repResult.history[0].missing_checked);
-    TEquals(16, repResult.history[0].missing_found);
-    TEquals(16, repResult.history[0].docs_read);
-    TEquals(16, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-
-    // add new docs to source and resume the same replication
-    var newDocs = makeDocs(50, 56);
-    populateSourceDb(newDocs, true);
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          filter: "mydesign/myfilter",
-          query_params: {
-            modulus: "2",
-            special: "7"
-          }
-        }
-      }
-    );
-
-    TEquals(true, repResult.ok);
-
-    for (j = 0; j < newDocs.length; j++) {
-      doc = newDocs[j];
-      copy = targetDb.open(doc._id);
-
-      if (doc.integer && (doc.integer % 2 === 0)) {
-
-        T(copy !== null);
-        TEquals(true, compareObjects(doc, copy));
-      } else {
-        TEquals(null, copy);
-      }
-    }
-
-    // last doc has even integer field, so last replicated seq is 36
-    // cluster - so no seq (ditto above)
-    //TEquals(36, repResult.source_last_seq);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(2, repResult.history.length);
-    //TEquals(29, repResult.history[0].start_last_seq);
-    //TEquals(36, repResult.history[0].end_last_seq);
-    //TEquals(36, repResult.history[0].recorded_seq);
-    TEquals(3, repResult.history[0].missing_checked);
-    TEquals(3, repResult.history[0].missing_found);
-    TEquals(3, repResult.history[0].docs_read);
-    TEquals(3, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-  }
-
-
-  // test filtered replication works as expected after changing the filter's
-  // code (ticket COUCHDB-892)
-  var filterFun1 = (function(doc, req) {
-    if (doc.value < Number(req.query.maxvalue)) {
-      return true;
-    } else {
-      return false;
-    }
-  }).toString();
-
-  var filterFun2 = (function(doc, req) {
-    return true;
-  }).toString();
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateTargetDb([]);
-    populateSourceDb([]);
-
-    TEquals(true, sourceDb.save({_id: "foo1", value: 1}).ok);
-    TEquals(true, sourceDb.save({_id: "foo2", value: 2}).ok);
-    TEquals(true, sourceDb.save({_id: "foo3", value: 3}).ok);
-    TEquals(true, sourceDb.save({_id: "foo4", value: 4}).ok);
-
-    var ddoc = {
-      "_id": "_design/mydesign",
-      "language": "javascript",
-      "filters": {
-        "myfilter": filterFun1
-      }
-    };
-
-    TEquals(true, sourceDb.save(ddoc).ok);
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          filter: "mydesign/myfilter",
-          query_params: {
-            maxvalue: "3"
-          }
-        }
-      }
-    );
-
-    TEquals(true, repResult.ok);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(1, repResult.history.length);
-    TEquals(2, repResult.history[0].docs_written);
-    TEquals(2, repResult.history[0].docs_read);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    var docFoo1 = targetDb.open("foo1");
-    T(docFoo1 !== null);
-    TEquals(1, docFoo1.value);
-
-    var docFoo2 = targetDb.open("foo2");
-    T(docFoo2 !== null);
-    TEquals(2, docFoo2.value);
-
-    var docFoo3 = targetDb.open("foo3");
-    TEquals(null, docFoo3);
-
-    var docFoo4 = targetDb.open("foo4");
-    TEquals(null, docFoo4);
-
-    // replication should start from scratch after the filter's code changed
-
-    ddoc.filters.myfilter = filterFun2;
-    TEquals(true, sourceDb.save(ddoc).ok);
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          filter: "mydesign/myfilter",
-          query_params : {
-            maxvalue: "3"
-          }
-        }
-      }
-    );
-
-    TEquals(true, repResult.ok);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(1, repResult.history.length);
-    TEquals(3, repResult.history[0].docs_written);
-    TEquals(3, repResult.history[0].docs_read);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    docFoo1 = targetDb.open("foo1");
-    T(docFoo1 !== null);
-    TEquals(1, docFoo1.value);
-
-    docFoo2 = targetDb.open("foo2");
-    T(docFoo2 !== null);
-    TEquals(2, docFoo2.value);
-
-    docFoo3 = targetDb.open("foo3");
-    T(docFoo3 !== null);
-    TEquals(3, docFoo3.value);
-
-    docFoo4 = targetDb.open("foo4");
-    T(docFoo4 !== null);
-    TEquals(4, docFoo4.value);
-
-    T(targetDb.open("_design/mydesign") !== null);
-  }
-
-
-  // test replication by doc IDs
-  docs = makeDocs(1, 11);
-  docs.push({
-    _id: "_design/foo",
-    language: "javascript",
-    integer: 1
-  });
-
-  var target_doc_ids = [
-    { initial: ["1", "2", "10"], after: [], conflict_id: "2" },
-    { initial: ["1", "2"], after: ["7"], conflict_id: "1" },
-    { initial: ["1", "foo_666", "10"], after: ["7"], conflict_id: "10" },
-    { initial: ["_design/foo", "8"], after: ["foo_5"], conflict_id: "8" },
-    { initial: ["_design%2Ffoo", "8"], after: ["foo_5"], conflict_id: "8" },
-    { initial: [], after: ["foo_1000", "_design/foo", "1"], conflict_id: "1" }
-  ];
-  var doc_ids, after_doc_ids;
-  var id, num_inexistent_docs, after_num_inexistent_docs;
-  var total, after_total;
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-
-    for (j = 0; j < target_doc_ids.length; j++) {
-      doc_ids = target_doc_ids[j].initial;
-      num_inexistent_docs = 0;
-
-      for (k = 0; k < doc_ids.length; k++) {
-        id = doc_ids[k];
-        if (id.indexOf("foo_") === 0) {
-          num_inexistent_docs += 1;
-        }
-      }
-
-      populateSourceDb(docs);
-      populateTargetDb([]);
-
-      repResult = CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {
-          body: {
-            doc_ids: doc_ids
-          }
-        }
-      );
-
-      total = doc_ids.length - num_inexistent_docs;
-      TEquals(true, repResult.ok);
-      if (total === 0) {
-        TEquals(true, repResult.no_changes);
-      } else {
-        TEquals('string', typeof repResult.start_time);
-        TEquals('string', typeof repResult.end_time);
-        TEquals(total, repResult.docs_read);
-        TEquals(total, repResult.docs_written);
-        TEquals(0, repResult.doc_write_failures);
-      }
-
-      for (k = 0; k < doc_ids.length; k++) {
-        id = decodeURIComponent(doc_ids[k]);
-        doc = sourceDb.open(id);
-        copy = targetDb.open(id);
-
-        if (id.indexOf("foo_") === 0) {
-          TEquals(null, doc);
-          TEquals(null, copy);
-        } else {
-          T(doc !== null);
-          T(copy !== null);
-          TEquals(true, compareObjects(doc, copy));
-        }
-      }
-
-      // be absolutely sure that other docs were not replicated
-      for (k = 0; k < docs.length; k++) {
-        var base_id = docs[k]._id;
-        id = encodeURIComponent(base_id);
-        doc = targetDb.open(base_id);
-
-        if ((doc_ids.indexOf(id) >= 0) || (doc_ids.indexOf(base_id) >= 0)) {
-            T(doc !== null);
-        } else {
-            TEquals(null, doc);
-        }
-      }
-
-      targetInfo = targetDb.info();
-      TEquals(total, targetInfo.doc_count);
-
-
-      // add more docs throught replication by doc IDs
-      after_doc_ids = target_doc_ids[j].after;
-      after_num_inexistent_docs = 0;
-
-      for (k = 0; k < after_doc_ids.length; k++) {
-        id = after_doc_ids[k];
-        if (id.indexOf("foo_") === 0) {
-          after_num_inexistent_docs += 1;
-        }
-      }
-
-      repResult = CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {
-          body: {
-            doc_ids: after_doc_ids
-          }
-        }
-      );
-
-      after_total = after_doc_ids.length - after_num_inexistent_docs;
-      TEquals(true, repResult.ok);
-      if (after_total === 0) {
-        TEquals(true, repResult.no_changes);
-      } else {
-        TEquals('string', typeof repResult.start_time);
-        TEquals('string', typeof repResult.end_time);
-        TEquals(after_total, repResult.docs_read);
-        TEquals(after_total, repResult.docs_written);
-        TEquals(0, repResult.doc_write_failures);
-      }
-
-      for (k = 0; k < after_doc_ids.length; k++) {
-        id = after_doc_ids[k];
-        doc = sourceDb.open(id);
-        copy = targetDb.open(id);
-
-        if (id.indexOf("foo_") === 0) {
-          TEquals(null, doc);
-          TEquals(null, copy);
-        } else {
-          T(doc !== null);
-          T(copy !== null);
-          TEquals(true, compareObjects(doc, copy));
-        }
-      }
-
-      // be absolutely sure that other docs were not replicated
-      for (k = 0; k < docs.length; k++) {
-        var base_id = docs[k]._id;
-        id = encodeURIComponent(base_id);
-        doc = targetDb.open(base_id);
-
-        if ((doc_ids.indexOf(id) >= 0) || (after_doc_ids.indexOf(id) >= 0) ||
-            (doc_ids.indexOf(base_id) >= 0) ||
-            (after_doc_ids.indexOf(base_id) >= 0)) {
-            T(doc !== null);
-        } else {
-            TEquals(null, doc);
-        }
-      }
-
-      targetInfo = targetDb.info();
-      TEquals((total + after_total), targetInfo.doc_count);
-
-
-      // replicate again the same doc after updated on source (no conflict)
-      id = target_doc_ids[j].conflict_id;
-      doc = sourceDb.open(id);
-      T(doc !== null);
-      doc.integer = 666;
-      TEquals(true, sourceDb.save(doc).ok);
-      addAtt(sourceDb, doc, "readme.txt", att1_data, "text/plain");
-      addAtt(sourceDb, doc, "data.dat", att2_data, "application/binary");
-
-      repResult = CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {
-          body: {
-            doc_ids: [id]
-          }
-        }
-      );
-
-      TEquals(true, repResult.ok);
-      TEquals(1, repResult.docs_read);
-      TEquals(1, repResult.docs_written);
-      TEquals(0, repResult.doc_write_failures);
-
-      copy = targetDb.open(id, {conflicts: true});
-
-      TEquals(666, copy.integer);
-      TEquals(0, copy._rev.indexOf("4-"));
-      TEquals('undefined', typeof copy._conflicts);
-
-      var atts = copy._attachments;
-      TEquals('object', typeof atts);
-      TEquals('object', typeof atts["readme.txt"]);
-      TEquals(3, atts["readme.txt"].revpos);
-      TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-      TEquals(true, atts["readme.txt"].stub);
-
-      var att1_copy = CouchDB.request(
-        "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-      ).responseText;
-      TEquals(att1_data.length, att1_copy.length);
-      TEquals(att1_data, att1_copy);
-
-      TEquals('object', typeof atts["data.dat"]);
-      TEquals(4, atts["data.dat"].revpos);
-      TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
-      TEquals(true, atts["data.dat"].stub);
-
-      var att2_copy = CouchDB.request(
-        "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
-      ).responseText;
-      TEquals(att2_data.length, att2_copy.length);
-      TEquals(att2_data, att2_copy);
-
-
-      // generate a conflict throught replication by doc IDs
-      id = target_doc_ids[j].conflict_id;
-      doc = sourceDb.open(id);
-      copy = targetDb.open(id);
-      T(doc !== null);
-      T(copy !== null);
-      doc.integer += 100;
-      copy.integer += 1;
-      TEquals(true, sourceDb.save(doc).ok);
-      TEquals(true, targetDb.save(copy).ok);
-
-      repResult = CouchDB.replicate(
-        dbPairsPrefixes[i].source+sourceDb.name,
-        dbPairsPrefixes[i].target+targetDb.name,
-        {
-          body: {
-            doc_ids: [id]
-          }
-        }
-      );
-
-      TEquals(true, repResult.ok);
-      TEquals(1, repResult.docs_read);
-      TEquals(1, repResult.docs_written);
-      TEquals(0, repResult.doc_write_failures);
-
-      copy = targetDb.open(id, {conflicts: true});
-
-      TEquals(0, copy._rev.indexOf("5-"));
-      TEquals(true, copy._conflicts instanceof Array);
-      TEquals(1, copy._conflicts.length);
-      TEquals(0, copy._conflicts[0].indexOf("5-"));
-    }
-  }
-
-
-  docs = makeDocs(1, 25);
-  docs.push({
-    _id: "_design/foo",
-    language: "javascript",
-    filters: {
-      myfilter: (function(doc, req) { return true; }).toString()
-    }
-  });
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb(docs);
-    populateTargetDb([]);
-
-    // add some attachments
-    for (j = 10; j < 15; j++) {
-      addAtt(sourceDb, docs[j], "readme.txt", att1_data, "text/plain");
-    }
-
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          continuous: true
-        }
-      }
-    );
-    TEquals(true, repResult.ok);
-    TEquals('string', typeof repResult._local_id);
-
-    var rep_id = repResult._local_id;
-
-    waitForSeq(sourceDb, targetDb, rep_id);
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      T(copy !== null);
-      TEquals(true, compareObjects(doc, copy));
-
-      if (j >= 10 && j < 15) {
-        var atts = copy._attachments;
-        TEquals('object', typeof atts);
-        TEquals('object', typeof atts["readme.txt"]);
-        TEquals(2, atts["readme.txt"].revpos);
-        TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-        TEquals(true, atts["readme.txt"].stub);
-
-        var att_copy = CouchDB.request(
-          "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-        ).responseText;
-        TEquals(att1_data.length, att_copy.length);
-        TEquals(att1_data, att_copy);
-      }
-    }
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    // add attachments to docs in source
-    for (j = 10; j < 15; j++) {
-      addAtt(sourceDb, docs[j], "data.dat", att2_data, "application/binary");
-    }
-
-    var ddoc = docs[docs.length - 1]; // design doc
-    addAtt(sourceDb, ddoc, "readme.txt", att1_data, "text/plain");
-
-    waitForSeq(sourceDb, targetDb, rep_id);
-
-    var modifDocs = docs.slice(10, 15).concat([ddoc]);
-    for (j = 0; j < modifDocs.length; j++) {
-      doc = modifDocs[j];
-      copy = targetDb.open(doc._id);
-
-      T(copy !== null);
-      TEquals(true, compareObjects(doc, copy));
-
-      var atts = copy._attachments;
-      TEquals('object', typeof atts);
-      TEquals('object', typeof atts["readme.txt"]);
-      TEquals(2, atts["readme.txt"].revpos);
-      TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-      TEquals(true, atts["readme.txt"].stub);
-
-      var att1_copy = CouchDB.request(
-        "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-      ).responseText;
-      TEquals(att1_data.length, att1_copy.length);
-      TEquals(att1_data, att1_copy);
-
-      if (doc._id.indexOf("_design/") === -1) {
-        TEquals('object', typeof atts["data.dat"]);
-        TEquals(3, atts["data.dat"].revpos);
-        TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
-        TEquals(true, atts["data.dat"].stub);
-
-        var att2_copy = CouchDB.request(
-          "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
-        ).responseText;
-        TEquals(att2_data.length, att2_copy.length);
-        TEquals(att2_data, att2_copy);
-      }
-    }
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    // add another attachment to the ddoc on source
-    addAtt(sourceDb, ddoc, "data.dat", att2_data, "application/binary");
-
-    waitForSeq(sourceDb, targetDb, rep_id);
-
-    copy = targetDb.open(ddoc._id);
-    var atts = copy._attachments;
-    TEquals('object', typeof atts);
-    TEquals('object', typeof atts["readme.txt"]);
-    TEquals(2, atts["readme.txt"].revpos);
-    TEquals(0, atts["readme.txt"].content_type.indexOf("text/plain"));
-    TEquals(true, atts["readme.txt"].stub);
-
-    var att1_copy = CouchDB.request(
-      "GET", "/" + targetDb.name + "/" + copy._id + "/readme.txt"
-    ).responseText;
-    TEquals(att1_data.length, att1_copy.length);
-    TEquals(att1_data, att1_copy);
-
-    TEquals('object', typeof atts["data.dat"]);
-    TEquals(3, atts["data.dat"].revpos);
-    TEquals(0, atts["data.dat"].content_type.indexOf("application/binary"));
-    TEquals(true, atts["data.dat"].stub);
-
-    var att2_copy = CouchDB.request(
-      "GET", "/" + targetDb.name + "/" + copy._id + "/data.dat"
-    ).responseText;
-    TEquals(att2_data.length, att2_copy.length);
-    TEquals(att2_data, att2_copy);
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-
-    // add more docs to source
-    var newDocs = makeDocs(25, 35);
-    populateSourceDb(newDocs, true);
-
-    waitForSeq(sourceDb, targetDb, rep_id);
-
-    for (j = 0; j < newDocs.length; j++) {
-      doc = newDocs[j];
-      copy = targetDb.open(doc._id);
-
-      T(copy !== null);
-      TEquals(true, compareObjects(doc, copy));
-    }
-
-    sourceInfo = sourceDb.info();
-    targetInfo = targetDb.info();
-
-    TEquals(sourceInfo.doc_count, targetInfo.doc_count);
-
-    // delete docs from source
-    TEquals(true, sourceDb.deleteDoc(newDocs[0]).ok);
-    TEquals(true, sourceDb.deleteDoc(newDocs[6]).ok);
-
-    waitForSeq(sourceDb, targetDb, rep_id);
-
-    copy = targetDb.open(newDocs[0]._id);
-    TEquals(null, copy);
-    copy = targetDb.open(newDocs[6]._id);
-    TEquals(null, copy);
-
-    var changes = targetDb.changes({since: targetInfo.update_seq});
-    // quite unfortunately, there is no way on relying on ordering in a cluster
-    // but we can assume a length of 2
-    var line1 = changes.results[changes.results.length - 2];
-    var line2 = changes.results[changes.results.length - 1];
-    T(newDocs[0]._id == line1.id || newDocs[0]._id == line2.id);
-    T(newDocs[6]._id == line1.id || newDocs[6]._id == line2.id);
-    T(line1.deleted && line2.deleted);
-
-    // cancel the replication
-    repResult = CouchDB.replicate(
-      dbPairsPrefixes[i].source+sourceDb.name,
-      dbPairsPrefixes[i].target+targetDb.name,
-      {
-        body: {
-          continuous: true,
-          cancel: true
-        }
-      }
-    );
-    TEquals(true, repResult.ok);
-    TEquals(rep_id, repResult._local_id);
-
-    doc = {
-      _id: 'foobar',
-      value: 666
-    };
-    TEquals(true, sourceDb.save(doc).ok);
-
-    waitReplicationTaskStop(rep_id);
-
-    copy = targetDb.open(doc._id);
-    TEquals(null, copy);
-  }
-
-  // COUCHDB-1093 - filtered and continuous _changes feed dies when the
-  // database is compacted
-  // no more relevant when clustering, you can't compact (per se at least)
-  /*
-  docs = makeDocs(1, 10);
-  docs.push({
-    _id: "_design/foo",
-    language: "javascript",
-    filters: {
-      myfilter: (function(doc, req) { return true; }).toString()
-    }
-  });
-  populateSourceDb(docs);
-  populateTargetDb([]);
-
-  repResult = CouchDB.replicate(
-    CouchDB.protocol + host + "/" + sourceDb.name,
-    targetDb.name,
-    {
-      body: {
-        continuous: true,
-        filter: "foo/myfilter"
-      }
-    }
-  );
-  TEquals(true, repResult.ok);
-  TEquals('string', typeof repResult._local_id);
-
-  TEquals(true, sourceDb.compact().ok);
-  while (sourceDb.info().compact_running) {};
-
-  TEquals(true, sourceDb.save(makeDocs(30, 31)[0]).ok);
-
-  var task = getTask(repResult._local_id, 1000);
-  T(task != null);
-
-  waitForSeq(sourceDb, targetDb, repResult._local_id);
-  T(sourceDb.open("30") !== null);
-
-  // cancel replication
-  repResult = CouchDB.replicate(
-    CouchDB.protocol + host + "/" + sourceDb.name,
-    targetDb.name,
-    {
-      body: {
-        continuous: true,
-        filter: "foo/myfilter",
-        cancel: true
-      }
-    }
-  );
-  TEquals(true, repResult.ok);
-  TEquals('string', typeof repResult._local_id);
-  */
-
-  //
-  // test replication of compressed attachments
-  //
-  doc = {
-    _id: "foobar"
-  };
-  var bigTextAtt = makeAttData(128 * 1024);
-  var attName = "readme.txt";
-  var oldSettings = getCompressionInfo();
-  var compressionLevel = oldSettings.level;
-  var compressibleTypes = oldSettings.types;
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    populateSourceDb([doc]);
-    populateTargetDb([]);
-
-    // enable compression of text types
-    enableAttCompression("8", "text/*");
-
-    // add text attachment to foobar doc
-    xhr = CouchDB.request(
-      "PUT",
-      "/" + sourceDb.name + "/" + doc._id + "/" + attName + "?rev=" + doc._rev,
-      {
-        body: bigTextAtt,
-        headers: {"Content-Type": "text/plain"}
-      }
-    );
-    TEquals(201, xhr.status);
-
-    // disable compression and replicate
-    disableAttCompression();
-
-    repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-    TEquals(true, repResult.ok);
-    TEquals(true, repResult.history instanceof Array);
-    TEquals(1, repResult.history.length);
-    TEquals(1, repResult.history[0].missing_checked);
-    TEquals(1, repResult.history[0].missing_found);
-    TEquals(1, repResult.history[0].docs_read);
-    TEquals(1, repResult.history[0].docs_written);
-    TEquals(0, repResult.history[0].doc_write_failures);
-
-    copy = targetDb.open(
-      doc._id,
-      {att_encoding_info: true, bypass_cache: Math.round(Math.random() * 1000)}
-    );
-    T(copy !== null);
-    T(attName in copy._attachments);
-    TEquals("gzip", copy._attachments[attName].encoding);
-    TEquals("number", typeof copy._attachments[attName].length);
-    TEquals("number", typeof copy._attachments[attName].encoded_length);
-    T(copy._attachments[attName].encoded_length < copy._attachments[attName].length);
-  }
-
-  delete bigTextAtt;
-  // restore original settings
-  enableAttCompression(compressionLevel, compressibleTypes);
-
-  //
-  // test replication triggered by non admins
-  //
-
-  // case 1) user triggering the replication is not a DB admin of the target DB
-  var joeUserDoc = CouchDB.prepareUserDoc({
-    name: "joe",
-    roles: ["erlanger"]
-  }, "erly");
-  var defaultUsersDb = new CouchDB("_users", {"X-Couch-Full-Commit":"false"});
-  try { defaultUsersDb.createDb(); } catch (e) { /* ignore if exists*/ }
-  //var usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
-  /*var server_config = [
-    {
-      section: "couch_httpd_auth",
-      key: "authentication_db",
-      value: usersDb.name
-    }
-  ];*/
-
-  docs = makeDocs(1, 6);
-  docs.push({
-    _id: "_design/foo",
-    language: "javascript"
-  });
-
-  dbPairsPrefixes = [
-    {
-      source: "",
-      target: ""
-    },
-    {
-      source: CouchDB.protocol + host + "/",
-      target: ""
-    },
-    {
-      source: "",
-      target: CouchDB.protocol + "joe:erly@" + host + "/"
-    },
-    {
-      source: CouchDB.protocol + host + "/",
-      target: CouchDB.protocol + "joe:erly@" + host + "/"
-    }
-  ];
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    //usersDb.deleteDb();
-    populateSourceDb(docs);
-    populateTargetDb([]);
-
-    TEquals(true, targetDb.setSecObj({
-      admins: {
-        names: ["superman"],
-        roles: ["god"]
-      }
-    }).ok);
-
-    // do NOT run on modified server b/c we use the default DB
-    //run_on_modified_server(server_config, function() {
-      delete joeUserDoc._rev;
-      var prevJoeUserDoc = defaultUsersDb.open(joeUserDoc._id);
-      if (prevJoeUserDoc) {
-        joeUserDoc._rev = prevJoeUserDoc._rev;
-      }
-      if(i == 0) {
-        TEquals(true, defaultUsersDb.save(joeUserDoc).ok);
-        wait(5000);
-      }
-      TEquals(true, CouchDB.login("joe", "erly").ok);
-      TEquals('joe', CouchDB.session().userCtx.name);
-
-      repResult = CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-
-      TEquals(true, CouchDB.logout().ok);
-
-      TEquals(true, repResult.ok);
-      TEquals(docs.length, repResult.history[0].docs_read);
-      TEquals((docs.length - 1), repResult.history[0].docs_written); // 1 ddoc
-      TEquals(1, repResult.history[0].doc_write_failures);
-    //});
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-
-      if (doc._id.indexOf("_design/") === 0) {
-        TEquals(null, copy);
-      } else {
-        T(copy !== null);
-        TEquals(true, compareObjects(doc, copy));
-      }
-    }
-  }
-
-  // case 2) user triggering the replication is not a reader (nor admin) of the source DB
-  dbPairsPrefixes = [
-    {
-      source: "",
-      target: ""
-    },
-    {
-      source: CouchDB.protocol + "joe:erly@" + host + "/",
-      target: ""
-    },
-    {
-      source: "",
-      target: CouchDB.protocol + host + "/"
-    },
-    {
-      source: CouchDB.protocol + "joe:erly@" + host + "/",
-      target: CouchDB.protocol + host + "/"
-    }
-  ];
-
-  for (i = 0; i < dbPairsPrefixes.length; i++) {
-    //usersDb.deleteDb();
-    populateSourceDb(docs);
-    populateTargetDb([]);
-
-    TEquals(true, sourceDb.setSecObj({
-      admins: {
-        names: ["superman"],
-        roles: ["god"]
-      },
-      readers: {
-        names: ["john"],
-        roles: ["secret"]
-      }
-    }).ok);
-    // check that we start OK (plus give time for sec object apply 2 avoid Heisenbugs)
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-      TEquals(null, copy);
-    }
-
-    // do NOT run on modified server b/c we use the default DB
-    //run_on_modified_server(server_config, function() {
-      delete joeUserDoc._rev;
-      var prevJoeUserDoc = defaultUsersDb.open(joeUserDoc._id);
-      if (prevJoeUserDoc) {
-        joeUserDoc._rev = prevJoeUserDoc._rev;
-      }
-      if(i == 0) {
-        TEquals(true, defaultUsersDb.save(joeUserDoc).ok);
-        wait(5000);
-      }
-
-      TEquals(true, CouchDB.login("joe", "erly").ok);
-      TEquals('joe', CouchDB.session().userCtx.name);
-
-      try {
-        CouchDB.replicate(dbPairsPrefixes[i].source+sourceDb.name, dbPairsPrefixes[i].target+targetDb.name);
-        T(false, "should have raised an exception");
-      } catch (x) {
-        // TODO: small thing: DB exists but is no more found - at least we have an exception, so it's rather minor
-        //TEquals("unauthorized", x.error);
-        T(!!x);
-      }
-
-      TEquals(true, CouchDB.logout().ok);
-    //});
-
-    for (j = 0; j < docs.length; j++) {
-      doc = docs[j];
-      copy = targetDb.open(doc._id);
-      TEquals(null, copy);
-    }
-  }
-
-
-  // COUCHDB-885 - push replication of a doc with attachment causes a
-  //               conflict in the target.
-  populateSourceDb([]);
-  populateTargetDb([]);
-
-  doc = {
-    _id: "doc1"
-  };
-  TEquals(true, sourceDb.save(doc).ok);
-
-  repResult = CouchDB.replicate(
-    sourceDb.name,
-    CouchDB.protocol + host + "/" + targetDb.name
-  );
-  TEquals(true, repResult.ok);
-  TEquals(true, repResult.history instanceof Array);
-  TEquals(1, repResult.history.length);
-  TEquals(1, repResult.history[0].docs_written);
-  TEquals(1, repResult.history[0].docs_read);
-  TEquals(0, repResult.history[0].doc_write_failures);
-
-  doc["_attachments"] = {
-    "hello.txt": {
-      "content_type": "text/plain",
-      "data": "aGVsbG8gd29ybGQ="  // base64:encode("hello world")
-    },
-    "foo.dat": {
-      "content_type": "not/compressible",
-      "data": "aSBhbSBub3QgZ3ppcGVk"  // base64:encode("i am not gziped")
-    }
-  };
-
-  TEquals(true, sourceDb.save(doc).ok);
-  repResult = CouchDB.replicate(
-    sourceDb.name,
-    CouchDB.protocol + host + "/" + targetDb.name
-  );
-  TEquals(true, repResult.ok);
-  TEquals(true, repResult.history instanceof Array);
-  TEquals(2, repResult.history.length);
-  TEquals(1, repResult.history[0].docs_written);
-  TEquals(1, repResult.history[0].docs_read);
-  TEquals(0, repResult.history[0].doc_write_failures);
-
-  copy = targetDb.open(doc._id, {
-    conflicts: true, deleted_conflicts: true,
-    attachments: true, att_encoding_info: true});
-  T(copy !== null);
-  TEquals("undefined", typeof copy._conflicts);
-  TEquals("undefined", typeof copy._deleted_conflicts);
-  TEquals("text/plain", copy._attachments["hello.txt"]["content_type"]);
-  TEquals("aGVsbG8gd29ybGQ=", copy._attachments["hello.txt"]["data"]);
-  TEquals("gzip", copy._attachments["hello.txt"]["encoding"]);
-  TEquals("not/compressible", copy._attachments["foo.dat"]["content_type"]);
-  TEquals("aSBhbSBub3QgZ3ppcGVk", copy._attachments["foo.dat"]["data"]);
-  TEquals("undefined", typeof copy._attachments["foo.dat"]["encoding"]);
-  // end of test for COUCHDB-885
-
-  // Test for COUCHDB-1242 (reject non-string query_params)
-  // TODO: non-String params crash CouchDB alltogether
-  /*
-  try {
-    CouchDB.replicate(sourceDb, targetDb, {
-      body: {
-        filter : "mydesign/myfilter",
-        query_params : {
-          "maxvalue": 4
-        }
-      }
-    });
-  } catch (e) {
-    TEquals("bad_request", e.error);
-  }
-  */
-
-
-  // Test that we can cancel a replication just by POSTing an object
-  // like  {"replication_id": Id, "cancel": true}. The replication ID
-  // can be obtained from a continuous replication request response
-  // (_local_id field), from _active_tasks or from the log
-  populateSourceDb(makeDocs(1, 6));
-  populateTargetDb([]);
-
-  repResult = CouchDB.replicate(
-    CouchDB.protocol + host + "/" + sourceDb.name,
-    targetDb.name,
-    {
-      body: {
-        continuous: true,
-        create_target: true
-      }
-    }
-  );
-  TEquals(true, repResult.ok);
-  TEquals('string', typeof repResult._local_id);
-  var repId = repResult._local_id;
-
-  var task = getTask(repId, 3000);
-  T(task != null);
-
-  TEquals(task["replication_id"], repId, "Replication found in _active_tasks");
-  xhr = CouchDB.request(
-    "POST", "/_replicate", {
-      body: JSON.stringify({"replication_id": repId, "cancel": true}),
-      headers: {"Content-Type": "application/json"}
-  });
-  TEquals(200, xhr.status, "Replication cancel request success");
-  waitReplicationTaskStop(repId);
-  task = getTask(repId);
-  TEquals(null, task, "Replication was canceled");
-
-  xhr = CouchDB.request(
-    "POST", "/_replicate", {
-      body: JSON.stringify({"replication_id": repId, "cancel": true}),
-      headers: {"Content-Type": "application/json"}
-  });
-  TEquals(404, xhr.status, "2nd replication cancel failed");
-
-  // Non-admin user can not cancel replications triggered by other users
-  var userDoc = CouchDB.prepareUserDoc({
-    name: "tony",
-    roles: ["mafia"]
-  }, "soprano");
-  // again, due doe _security not there, we use the default users DB
-  defaultUsersDb = new CouchDB("_users", {"X-Couch-Full-Commit":"false"});
-  //usersDb = new CouchDB("test_suite_auth", {"X-Couch-Full-Commit":"false"});
-  // (and leave the server alone)
-  /*server_config = [
-    {
-      section: "couch_httpd_auth",
-      key: "authentication_db",
-      value: usersDb.name
-    }
-  ];*/
-
-  //run_on_modified_server(server_config, function() {
-    populateSourceDb(makeDocs(1, 6));
-    populateTargetDb([]);
-    var prevUserDoc = defaultUsersDb.open(userDoc._id);
-    if(prevUserDoc) {
-      userDoc._rev = prevUserDoc._rev;
-    }
-    TEquals(true, defaultUsersDb.save(userDoc).ok);
-
-    repResult = CouchDB.replicate(
-      CouchDB.protocol + host + "/" + sourceDb.name,
-      targetDb.name,
-      {
-        body: {
-          continuous: true
-        }
-      }
-    );
-    TEquals(true, repResult.ok);
-    TEquals('string', typeof repResult._local_id);
-
-    TEquals(true, CouchDB.login("tony", "soprano").ok);
-    TEquals('tony', CouchDB.session().userCtx.name);
-
-    xhr = CouchDB.request(
-      "POST", "/_replicate", {
-        body: JSON.stringify({"replication_id": repResult._local_id, "cancel": true}),
-        headers: {"Content-Type": "application/json"}
-    });
-    TEquals(401, xhr.status, "Unauthorized to cancel replication");
-    TEquals("unauthorized", JSON.parse(xhr.responseText).error);
-
-    TEquals(true, CouchDB.logout().ok);
-
-    xhr = CouchDB.request(
-      "POST", "/_replicate", {
-        body: JSON.stringify({"replication_id": repResult._local_id, "cancel": true}),
-        headers: {"Content-Type": "application/json"}
-    });
-    TEquals(200, xhr.status, "Authorized to cancel replication");
-  //});
-
-  // cleanup
-  //usersDb.deleteDb();
-  sourceDb.deleteDb();
-  targetDb.deleteDb();
-  // (not sure what this is - cleanup after 'file not found tests' poss. - not harmful anyway)
-  (new CouchDB("test_suite_db")).deleteDb();
-};