summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2013-01-13 15:22:01 +0100
committerJan Lehnardt <jan@apache.org>2013-02-08 19:17:43 +0100
commitae048910ebea89942116fe7734d36baaa7d6cfcf (patch)
tree2b977f5738e327a2b5cdd8b7859518981a74392a
parent3b103eb10b115b4b6a8f0c6e31dc92292c0aeb71 (diff)
downloadcouchdb-ae048910ebea89942116fe7734d36baaa7d6cfcf.tar.gz
split replicator_db tests
-rw-r--r--CHANGES10
-rw-r--r--share/Makefile.am20
-rw-r--r--share/www/script/couch_tests.js19
-rw-r--r--share/www/script/replicator_db_inc.js96
-rw-r--r--share/www/script/test/replicator_db.js1560
-rw-r--r--share/www/script/test/replicator_db_bad_rep_id.js77
-rw-r--r--share/www/script/test/replicator_db_by_doc_id.js99
-rw-r--r--share/www/script/test/replicator_db_compact_rep_db.js119
-rw-r--r--share/www/script/test/replicator_db_continuous.js137
-rw-r--r--share/www/script/test/replicator_db_credential_delegation.js149
-rw-r--r--share/www/script/test/replicator_db_field_validation.js178
-rw-r--r--share/www/script/test/replicator_db_filtered.js105
-rw-r--r--share/www/script/test/replicator_db_identical.js87
-rw-r--r--share/www/script/test/replicator_db_identical_continuous.js132
-rw-r--r--share/www/script/test/replicator_db_invalid_filter.js115
-rw-r--r--share/www/script/test/replicator_db_simple.js114
-rw-r--r--share/www/script/test/replicator_db_successive.js127
-rw-r--r--share/www/script/test/replicator_db_survives.js128
-rw-r--r--share/www/script/test/replicator_db_swap_rep_db.js170
-rw-r--r--share/www/script/test/replicator_db_update_security.js92
-rw-r--r--share/www/script/test/replicator_db_user_ctx.js272
-rw-r--r--share/www/script/test/replicator_db_write_auth.js102
-rw-r--r--test/javascript/run.tpl1
23 files changed, 2347 insertions, 1562 deletions
diff --git a/CHANGES b/CHANGES
index e4b2aab99..3012e3deb 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
-------------
diff --git a/share/Makefile.am b/share/Makefile.am
index 31373ee2a..876c70142 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 \
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index c1cdf75db..99d63e745 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");
diff --git a/share/www/script/replicator_db_inc.js b/share/www/script/replicator_db_inc.js
new file mode 100644
index 000000000..23f858768
--- /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
diff --git a/share/www/script/test/replicator_db.js b/share/www/script/test/replicator_db.js
deleted file mode 100644
index edc85f49f..000000000
--- 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();
-};
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 000000000..285b863e7
--- /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
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 000000000..1e1a38521
--- /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
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 000000000..0dea0ed66
--- /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
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 000000000..a2b17d00a
--- /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
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 000000000..7dd9d1812
--- /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
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 000000000..167bdccb0
--- /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
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 000000000..31c78a7c5
--- /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
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 000000000..cdf4592be
--- /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
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 000000000..5e7f15121
--- /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
diff --git a/share/www/script/test/replicator_db_invalid_filter.js b/share/www/script/test/replicator_db_invalid_filter.js
new file mode 100644
index 000000000..6438d4356
--- /dev/null
+++ b/share/www/script/test/replicator_db_invalid_filter.js
@@ -0,0 +1,115 @@
+// 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_invalid_filter = 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_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);
+ }
+
+ 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_invalid_filter);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_simple.js b/share/www/script/test/replicator_db_simple.js
new file mode 100644
index 000000000..f7acedb37
--- /dev/null
+++ b/share/www/script/test/replicator_db_simple.js
@@ -0,0 +1,114 @@
+// 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_simple = 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 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");
+ }
+
+ 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);
+
+/*
+ * 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.
+ */
+ /*
+ 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");
+ }
+ */
+/*
+ * repDb.deleteDb();
+ * restartServer();
+ * run_on_modified_server(server_config, error_state_replication);
+ */
+
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_successive.js b/share/www/script/test/replicator_db_successive.js
new file mode 100644
index 000000000..4898c3336
--- /dev/null
+++ b/share/www/script/test/replicator_db_successive.js
@@ -0,0 +1,127 @@
+// 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_successive = 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 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");
+ }
+
+ 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, successive_identical_replications);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_survives.js b/share/www/script/test/replicator_db_survives.js
new file mode 100644
index 000000000..dcaa1013f
--- /dev/null
+++ b/share/www/script/test/replicator_db_survives.js
@@ -0,0 +1,128 @@
+// 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_survives = 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 waitForDocPos = replicator_db.waitForDocPos;
+ var wait_rep_doc = replicator_db.wait_rep_doc;
+
+ 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);
+
+ repDb.createDb(); // the config put above should create this db
+
+ 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);
+ }
+
+ 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, continuous_replication_survives_restart);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_swap_rep_db.js b/share/www/script/test/replicator_db_swap_rep_db.js
new file mode 100644
index 000000000..04f4e9fa2
--- /dev/null
+++ b/share/www/script/test/replicator_db_swap_rep_db.js
@@ -0,0 +1,170 @@
+// 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_swap_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 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);
+ }
+ 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, swap_rep_db);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.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();
+
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_update_security.js b/share/www/script/test/replicator_db_update_security.js
new file mode 100644
index 000000000..465151468
--- /dev/null
+++ b/share/www/script/test/replicator_db_update_security.js
@@ -0,0 +1,92 @@
+// 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_update_security = 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_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");
+ }
+
+ 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_rep_db_update_security);
+
+ // 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
diff --git a/share/www/script/test/replicator_db_user_ctx.js b/share/www/script/test/replicator_db_user_ctx.js
new file mode 100644
index 000000000..570fc7d55
--- /dev/null
+++ b/share/www/script/test/replicator_db_user_ctx.js
@@ -0,0 +1,272 @@
+// 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_user_ctx = 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_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);
+ }
+
+ 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_user_ctx_validation);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/replicator_db_write_auth.js b/share/www/script/test/replicator_db_write_auth.js
new file mode 100644
index 000000000..697abf340
--- /dev/null
+++ b/share/www/script/test/replicator_db_write_auth.js
@@ -0,0 +1,102 @@
+// 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_survives = 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 waitForDocPos = replicator_db.waitForDocPos;
+ var wait_rep_doc = replicator_db.wait_rep_doc;
+
+ 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");
+ }
+ });
+ }
+
+ 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_db_write_authorization);
+
+ // cleanup
+ repDb.deleteDb();
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.deleteDb();
+} \ No newline at end of file
diff --git a/test/javascript/run.tpl b/test/javascript/run.tpl
index 647a29a58..39a47a6b8 100644
--- a/test/javascript/run.tpl
+++ b/test/javascript/run.tpl
@@ -70,6 +70,7 @@ $COUCHJS -H -u $COUCH_URI_FILE \
$SCRIPT_DIR/couch.js \
$SCRIPT_DIR/couch_test_runner.js \
$SCRIPT_DIR/couch_tests.js \
+ $SCRIPT_DIR/replicator_db_inc.js \
$TEST_SRC \
$JS_TEST_DIR/couch_http.js \
$JS_TEST_DIR/cli_runner.js