summaryrefslogtreecommitdiff
path: root/share
diff options
context:
space:
mode:
authorJohn Christopher Anderson <jchris@apache.org>2009-03-13 22:15:34 +0000
committerJohn Christopher Anderson <jchris@apache.org>2009-03-13 22:15:34 +0000
commit9007e2d21dea8b0185c0096b30364a8ee40a3867 (patch)
tree7d8dacb2c8cd619f18dfab8fdb40d146ac28c85a /share
parent65608e14e8911b33c30178d717d745edc9f66c17 (diff)
Commit Damien's rep_security branch to trunk.
Changes bulk_docs conflict checking. Breaks file format, see mailing list for data upgrade procedure, or http://wiki.apache.org/couchdb/Breaking_changes git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@753448 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'share')
-rw-r--r--share/Makefile.am1
-rw-r--r--share/www/script/couch.js44
-rw-r--r--share/www/script/couch_tests.js1
-rw-r--r--share/www/script/test/attachment_names.js2
-rw-r--r--share/www/script/test/attachments.js4
-rw-r--r--share/www/script/test/basics.js12
-rw-r--r--share/www/script/test/bulk_docs.js71
-rw-r--r--share/www/script/test/compact.js8
-rw-r--r--share/www/script/test/design_docs.js2
-rw-r--r--share/www/script/test/etags_views.js3
-rw-r--r--share/www/script/test/invalid_docids.js14
-rw-r--r--share/www/script/test/list_views.js6
-rw-r--r--share/www/script/test/lots_of_docs.js2
-rw-r--r--share/www/script/test/purge.js2
-rw-r--r--share/www/script/test/reduce.js6
-rw-r--r--share/www/script/test/reduce_false.js2
-rw-r--r--share/www/script/test/replication.js41
-rw-r--r--share/www/script/test/rev_stemming.js93
-rw-r--r--share/www/script/test/security_validation.js165
-rw-r--r--share/www/script/test/view_include_docs.js2
-rw-r--r--share/www/script/test/view_multi_key_all_docs.js2
-rw-r--r--share/www/script/test/view_multi_key_design.js2
-rw-r--r--share/www/script/test/view_multi_key_temp.js2
-rw-r--r--share/www/script/test/view_pagination.js2
24 files changed, 393 insertions, 96 deletions
diff --git a/share/Makefile.am b/share/Makefile.am
index 0f8daaab..3cabe166 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -104,6 +104,7 @@ nobase_dist_localdata_DATA = \
www/script/test/view_sandboxing.js \
www/script/test/view_xml.js \
www/script/test/replication.js \
+ www/script/test/rev_stemming.js \
www/script/test/etags_head.js \
www/script/test/etags_views.js \
www/script/test/show_documents.js \
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 821fb694..4586cca8 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -98,15 +98,26 @@ function CouchDB(name, httpHeaders) {
if (docs[i]._id == undefined)
docs[i]._id = newUuids.pop();
}
- this.last_req = this.request("POST", this.uri + "_bulk_docs" + encodeOptions(options), {
- body: JSON.stringify({"docs": docs})
+ var json = {"docs": docs};
+ // put any options in the json
+ for (var option in options) {
+ json[option] = options[option];
+ }
+ this.last_req = this.request("POST", this.uri + "_bulk_docs", {
+ body: JSON.stringify(json)
});
- CouchDB.maybeThrowError(this.last_req);
- var result = JSON.parse(this.last_req.responseText);
- for (var i = 0; i < docs.length; i++) {
- docs[i]._rev = result.new_revs[i].rev;
+ if (this.last_req.status == 417) {
+ return {errors: JSON.parse(this.last_req.responseText)};
+ }
+ else {
+ CouchDB.maybeThrowError(this.last_req);
+ var results = JSON.parse(this.last_req.responseText);
+ for (var i = 0; i < docs.length; i++) {
+ if(results[i].rev)
+ docs[i]._rev = results[i].rev;
+ }
+ return results;
}
- return result;
}
this.ensureFullCommit = function() {
@@ -203,6 +214,20 @@ function CouchDB(name, httpHeaders) {
return JSON.parse(this.last_req.responseText);
}
+ this.setDbProperty = function(propId, propValue) {
+ this.last_req = this.request("PUT", this.uri + propId,{
+ body:JSON.stringify(propValue)
+ });
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
+ }
+
+ this.getDbProperty = function(propId) {
+ this.last_req = this.request("GET", this.uri + propId);
+ CouchDB.maybeThrowError(this.last_req);
+ return JSON.parse(this.last_req.responseText);
+ }
+
this.setAdmins = function(adminsArray) {
this.last_req = this.request("PUT", this.uri + "_admins",{
body:JSON.stringify(adminsArray)
@@ -283,8 +308,11 @@ CouchDB.getVersion = function() {
return JSON.parse(CouchDB.last_req.responseText).version;
}
-CouchDB.replicate = function(source, target) {
+CouchDB.replicate = function(source, target, rep_options) {
+ rep_options = rep_options || {};
+ var headers = rep_options.headers || {};
CouchDB.last_req = CouchDB.request("POST", "/_replicate", {
+ headers: headers,
body: JSON.stringify({source: source, target: target})
});
CouchDB.maybeThrowError(CouchDB.last_req);
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 64c366b6..8ba1f76d 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -68,6 +68,7 @@ loadTest("purge.js");
loadTest("config.js");
loadTest("security_validation.js");
loadTest("stats.js");
+loadTest("rev_stemming.js");
function makeDocs(start, end, templateDoc) {
var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}"
diff --git a/share/www/script/test/attachment_names.js b/share/www/script/test/attachment_names.js
index 802abc08..3c694dd0 100644
--- a/share/www/script/test/attachment_names.js
+++ b/share/www/script/test/attachment_names.js
@@ -10,7 +10,7 @@
// License for the specific language governing permissions and limitations under
// the License.
-couchTests.attatchment_names = function(debug) {
+couchTests.attachment_names = function(debug) {
var db = new CouchDB("test_suite_db");
db.deleteDb();
db.createDb();
diff --git a/share/www/script/test/attachments.js b/share/www/script/test/attachments.js
index 409426c7..4b43e31a 100644
--- a/share/www/script/test/attachments.js
+++ b/share/www/script/test/attachments.js
@@ -32,8 +32,8 @@ couchTests.attachments= function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt");
T(xhr.responseText == "This is a base64 encoded text");
T(xhr.getResponseHeader("Content-Type") == "text/plain");
- T(xhr.getResponseHeader("Etag") == save_response.rev);
-
+ T(xhr.getResponseHeader("Etag") == '"' + save_response.rev + '"');
+
// empty attachment
var binAttDoc2 = {
_id: "bin_doc2",
diff --git a/share/www/script/test/basics.js b/share/www/script/test/basics.js
index 89e99a64..2837b77a 100644
--- a/share/www/script/test/basics.js
+++ b/share/www/script/test/basics.js
@@ -14,14 +14,14 @@
couchTests.basics = function(debug) {
var result = JSON.parse(CouchDB.request("GET", "/").responseText);
T(result.couchdb == "Welcome");
-
+
var db = new CouchDB("test_suite_db");
db.deleteDb();
// bug COUCHDB-100: DELETE on non-existent DB returns 500 instead of 404
db.deleteDb();
-
-db.createDb();
+
+ db.createDb();
// PUT on existing DB should return 412 instead of 500
xhr = CouchDB.request("PUT", "/test_suite_db/");
@@ -122,14 +122,14 @@ db.createDb();
// make sure we can still open the old rev of the deleted doc
T(db.open(existingDoc._id, {rev: existingDoc._rev}) != null);
-
+
// make sure restart works
T(db.ensureFullCommit().ok);
restartServer();
-
+
// make sure we can still open
T(db.open(existingDoc._id, {rev: existingDoc._rev}) != null);
-
+
// test that the POST response has a Location header
var xhr = CouchDB.request("POST", "/test_suite_db", {
body: JSON.stringify({"foo":"bar"})
diff --git a/share/www/script/test/bulk_docs.js b/share/www/script/test/bulk_docs.js
index d781b2c6..40a36331 100644
--- a/share/www/script/test/bulk_docs.js
+++ b/share/www/script/test/bulk_docs.js
@@ -19,38 +19,73 @@ couchTests.bulk_docs = function(debug) {
var docs = makeDocs(5);
// Create the docs
- var result = db.bulkSave(docs);
- T(result.ok);
- T(result.new_revs.length == 5);
+ var results = db.bulkSave(docs);
+
+ T(results.length == 5);
for (var i = 0; i < 5; i++) {
- T(result.new_revs[i].id == docs[i]._id);
- T(result.new_revs[i].rev);
+ T(results[i].id == docs[i]._id);
+ T(results[i].rev);
+ // Update the doc
docs[i].string = docs[i].string + ".00";
}
- // Update the docs
- result = db.bulkSave(docs);
- T(result.ok);
- T(result.new_revs.length == 5);
+ // Save the docs
+ results = db.bulkSave(docs);
+ T(results.length == 5);
for (i = 0; i < 5; i++) {
- T(result.new_revs[i].id == i.toString());
+ T(results[i].id == i.toString());
+
+ // set the delete flag to delete the docs in the next step
docs[i]._deleted = true;
}
+
+ // now test a bulk update with a conflict
+ // open and save
+ var doc = db.open("0");
+ db.save(doc);
- // Delete the docs
- result = db.bulkSave(docs);
- T(result.ok);
- T(result.new_revs.length == 5);
- for (i = 0; i < 5; i++) {
+ // Now bulk delete the docs
+ results = db.bulkSave(docs);
+
+ // doc "0" should be a conflict
+ T(results.length == 5);
+ T(results[0].id == "0");
+ T(results[0].error == "conflict");
+ T(results[0].rev === undefined); // no rev member when a conflict
+
+ // but the rest are not
+ for (i = 1; i < 5; i++) {
+ T(results[i].id == i.toString());
+ T(results[i].rev)
T(db.open(docs[i]._id) == null);
}
+
+ // now force a conflict to to save
+
+ // save doc 0, this will cause a conflict when we save docs[0]
+ var doc = db.open("0");
+ docs[0] = db.open("0")
+ db.save(doc);
+
+ docs[0].shooby = "dooby";
+
+ // Now save the bulk docs, When we use all_or_nothing, we don't get conflict
+ // checking, all docs are saved regardless of conflict status, or none are
+ // saved.
+ results = db.bulkSave(docs,{all_or_nothing:true});
+ T(results.error === undefined);
+
+ var doc = db.open("0", {conflicts:true});
+ var docConflict = db.open("0", {rev:doc._conflicts[0]});
+
+ T(doc.shooby == "dooby" || docConflict.shooby == "dooby");
// verify creating a document with no id returns a new id
var req = CouchDB.request("POST", "/test_suite_db/_bulk_docs", {
body: JSON.stringify({"docs": [{"foo":"bar"}]})
});
- result = JSON.parse(req.responseText);
+ results = JSON.parse(req.responseText);
- T(result.new_revs[0].id != "");
- T(result.new_revs[0].rev != "");
+ T(results[0].id != "");
+ T(results[0].rev != "");
};
diff --git a/share/www/script/test/compact.js b/share/www/script/test/compact.js
index 4612621e..51a57051 100644
--- a/share/www/script/test/compact.js
+++ b/share/www/script/test/compact.js
@@ -16,8 +16,7 @@ couchTests.compact = function(debug) {
db.createDb();
if (debug) debugger;
var docs = makeDocs(0, 10);
- var saveResult = db.bulkSave(docs);
- T(saveResult.ok);
+ db.bulkSave(docs);
var binAttDoc = {
_id: "bin_doc",
@@ -36,12 +35,11 @@ couchTests.compact = function(debug) {
for(var i in docs) {
db.deleteDoc(docs[i]);
}
- db.setAdmins(["Foo bar"]);
var deletesize = db.info().disk_size;
T(deletesize > originalsize);
- var xhr = CouchDB.request("POST", "/test_suite_db/_compact");
- T(xhr.status == 202);
+ T(db.compact().ok);
+ T(db.last_req.status == 202);
// compaction isn't instantaneous, loop until done
while (db.info().compact_running) {};
diff --git a/share/www/script/test/design_docs.js b/share/www/script/test/design_docs.js
index aedf741a..ebf0e74d 100644
--- a/share/www/script/test/design_docs.js
+++ b/share/www/script/test/design_docs.js
@@ -43,7 +43,7 @@ couchTests.design_docs = function(debug) {
}
T(db.save(designDoc).ok);
- T(db.bulkSave(makeDocs(1, numDocs + 1)).ok);
+ db.bulkSave(makeDocs(1, numDocs + 1));
// test that the _all_docs view returns correctly with keys
var results = db.allDocs({startkey:"_design", endkey:"_design0"});
diff --git a/share/www/script/test/etags_views.js b/share/www/script/test/etags_views.js
index cb38ccf6..018bdc25 100644
--- a/share/www/script/test/etags_views.js
+++ b/share/www/script/test/etags_views.js
@@ -42,8 +42,7 @@ couchTests.etags_views = function(debug) {
T(db.save(designDoc).ok);
var xhr;
var docs = makeDocs(0, 10);
- var saveResult = db.bulkSave(docs);
- T(saveResult.ok);
+ db.bulkSave(docs);
// verify get w/Etag on map view
xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
diff --git a/share/www/script/test/invalid_docids.js b/share/www/script/test/invalid_docids.js
index 6ed01f0b..814d1a21 100644
--- a/share/www/script/test/invalid_docids.js
+++ b/share/www/script/test/invalid_docids.js
@@ -23,32 +23,32 @@ couchTests.invalid_docids = function(debug) {
//Test non-string
try {
db.save({"_id": 1});
- T(1 == 0);
+ T(1 == 0, "doc id must be string");
} catch(e) {
T(db.last_req.status == 400);
- T(e.error == "invalid_doc");
+ T(e.error == "bad_request");
}
// Test invalid _prefix
try {
db.save({"_id": "_invalid"});
- T(1 == 0);
+ T(1 == 0, "doc id may not start with underscore");
} catch(e) {
T(db.last_req.status == 400);
- T(e.error == "invalid_doc");
+ T(e.error == "bad_request");
}
// Test _bulk_docs explicitly.
var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}];
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
docs.forEach(function(d) {T(db.open(d._id)._id == d._id);});
docs = [{"_id": "_invalid"}];
try {
db.bulkSave(docs);
- T(1 == 0);
+ T(1 == 0, "doc id may not start with underscore, even in bulk docs");
} catch(e) {
T(db.last_req.status == 400);
- T(e.error == "invalid_doc");
+ T(e.error == "bad_request");
}
};
diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js
index 055ef51c..e71ebc4e 100644
--- a/share/www/script/test/list_views.js
+++ b/share/www/script/test/list_views.js
@@ -137,8 +137,7 @@ couchTests.list_views = function(debug) {
T(db.save(designDoc).ok);
var docs = makeDocs(0, 10);
- var saveResult = db.bulkSave(docs);
- T(saveResult.ok);
+ db.bulkSave(docs);
var view = db.view('lists/basicView');
T(view.total_rows == 10);
@@ -207,8 +206,7 @@ couchTests.list_views = function(debug) {
// verify the etags expire correctly
var docs = makeDocs(11, 12);
- var saveResult = db.bulkSave(docs);
- T(saveResult.ok);
+ db.bulkSave(docs);
xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
headers: {"if-none-match": etag}
diff --git a/share/www/script/test/lots_of_docs.js b/share/www/script/test/lots_of_docs.js
index 702317e5..4101e6a7 100644
--- a/share/www/script/test/lots_of_docs.js
+++ b/share/www/script/test/lots_of_docs.js
@@ -25,7 +25,7 @@ couchTests.lots_of_docs = function(debug) {
for(var i=0; i < numDocsToCreate; i += 100) {
var createNow = Math.min(numDocsToCreate - i, 100);
var docs = makeDocs(i, i + createNow);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
}
// query all documents, and return the doc.integer member as a key.
diff --git a/share/www/script/test/purge.js b/share/www/script/test/purge.js
index e05ac024..0b47c0d0 100644
--- a/share/www/script/test/purge.js
+++ b/share/www/script/test/purge.js
@@ -34,7 +34,7 @@ couchTests.purge = function(debug) {
T(db.save(designDoc).ok);
- T(db.bulkSave(makeDocs(1, numDocs + 1)).ok);
+ db.bulkSave(makeDocs(1, numDocs + 1));
// go ahead and validate the views before purging
var rows = db.view("test/all_docs_twice").rows;
diff --git a/share/www/script/test/reduce.js b/share/www/script/test/reduce.js
index d3245f98..c8bdcd92 100644
--- a/share/www/script/test/reduce.js
+++ b/share/www/script/test/reduce.js
@@ -17,7 +17,7 @@ couchTests.reduce = function(debug) {
if (debug) debugger;
var numDocs = 500
var docs = makeDocs(1,numDocs + 1);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var summate = function(N) {return (N+1)*N/2;};
var map = function (doc) {
@@ -65,7 +65,7 @@ couchTests.reduce = function(debug) {
docs.push({keys:["d", "a"]});
docs.push({keys:["d", "b"]});
docs.push({keys:["d", "c"]});
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
}
@@ -157,7 +157,7 @@ couchTests.reduce = function(debug) {
docs.push({val:80});
docs.push({val:90});
docs.push({val:100});
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
}
var results = db.query(map, reduceCombine);
diff --git a/share/www/script/test/reduce_false.js b/share/www/script/test/reduce_false.js
index 41f0c669..22ef2e8b 100644
--- a/share/www/script/test/reduce_false.js
+++ b/share/www/script/test/reduce_false.js
@@ -18,7 +18,7 @@ couchTests.reduce_false = function(debug) {
var numDocs = 5;
var docs = makeDocs(1,numDocs + 1);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var summate = function(N) {return (N+1)*N/2;};
var designDoc = {
diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js
index 59dc4700..944c8e47 100644
--- a/share/www/script/test/replication.js
+++ b/share/www/script/test/replication.js
@@ -59,7 +59,7 @@ couchTests.replication = function(debug) {
simple_test: new function () {
this.init = function(dbA, dbB) {
var docs = makeDocs(0, numDocs);
- T(dbA.bulkSave(docs).ok);
+ dbA.bulkSave(docs);
};
this.afterAB1 = function(dbA, dbB) {
@@ -160,7 +160,7 @@ couchTests.replication = function(debug) {
dbA.deleteDoc({_id:"foo", _rev:docA._conflicts[0]});
};
- this.afterBA2 = function(dbA, dbB) {
+ this.afterBA2 = function(dbA, dbB) {
// open documents and include the conflict meta data
var docA = dbA.open("foo", {conflicts: true});
var docB = dbB.open("foo", {conflicts: true});
@@ -179,29 +179,56 @@ couchTests.replication = function(debug) {
}
}
- T(CouchDB.replicate(A, B).ok);
+ var result = CouchDB.replicate(A, B);
+
+ var seqA = result.source_last_seq;
+ T(0 == result.history[0].start_last_seq);
+ T(result.history[1] === undefined)
for(test in repTests) {
if(repTests[test].afterAB1) repTests[test].afterAB1(dbA, dbB);
}
- T(CouchDB.replicate(B, A).ok);
+ result = CouchDB.replicate(B, A);
+
+ var seqB = result.source_last_seq;
+ T(0 == result.history[0].start_last_seq);
+ T(result.history[1] === undefined)
for(test in repTests) {
if(repTests[test].afterBA1) repTests[test].afterBA1(dbA, dbB);
}
- T(CouchDB.replicate(A, B).ok);
+ var result2 = CouchDB.replicate(A, B);
+
+ // each successful replication produces a new session id
+ T(result2.session_id != result.session_id);
+
+ T(seqA < result2.source_last_seq);
+ T(seqA == result2.history[0].start_last_seq);
+ T(result2.history[1].end_last_seq == seqA)
+
+ seqA = result2.source_last_seq;
for(test in repTests) {
if(repTests[test].afterAB2) repTests[test].afterAB2(dbA, dbB);
}
- T(CouchDB.replicate(B, A).ok);
+ result = CouchDB.replicate(B, A)
+
+ T(seqB < result.source_last_seq);
+ T(seqB == result.history[0].start_last_seq);
+ T(result.history[1].end_last_seq == seqB)
+
+ seqB = result.source_last_seq;
for(test in repTests) {
if(repTests[test].afterBA2) repTests[test].afterBA2(dbA, dbB);
}
-
+
+ // do an replication where nothing has changed
+ result2 = CouchDB.replicate(B, A);
+ T(result2.no_changes == true);
+ T(result2.session_id == result.session_id);
}
};
diff --git a/share/www/script/test/rev_stemming.js b/share/www/script/test/rev_stemming.js
new file mode 100644
index 00000000..f629c299
--- /dev/null
+++ b/share/www/script/test/rev_stemming.js
@@ -0,0 +1,93 @@
+// 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.rev_stemming = function(debug) {
+ var db = new CouchDB("test_suite_db_a");
+ db.deleteDb();
+ db.createDb();
+ var dbB = new CouchDB("test_suite_db_b");
+ dbB.deleteDb();
+ dbB.createDb();
+ if (debug) debugger;
+
+ var newLimit = 5;
+
+ T(db.getDbProperty("_revs_limit") == 1000);
+
+ var doc = {_id:"foo",foo:0}
+ for( var i=0; i < newLimit + 1; i++) {
+ doc.foo++;
+ T(db.save(doc).ok);
+ }
+ var doc0 = db.open("foo", {revs:true});
+ T(doc0._revisions.ids.length == newLimit + 1);
+
+ var docBar = {_id:"bar",foo:0}
+ for( var i=0; i < newLimit + 1; i++) {
+ docBar.foo++;
+ T(db.save(docBar).ok);
+ }
+ T(db.open("bar", {revs:true})._revisions.ids.length == newLimit + 1);
+
+ T(db.setDbProperty("_revs_limit", newLimit).ok);
+
+ for( var i=0; i < newLimit + 1; i++) {
+ doc.foo++;
+ T(db.save(doc).ok);
+ }
+ doc0 = db.open("foo", {revs:true});
+ T(doc0._revisions.ids.length == newLimit);
+
+
+ // If you replicate after you make more edits than the limit, you'll
+ // cause a spurious edit conflict.
+ CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
+ var docB1 = dbB.open("foo",{conflicts:true})
+ T(docB1._conflicts == null);
+
+ for( var i=0; i < newLimit - 1; i++) {
+ doc.foo++;
+ T(db.save(doc).ok);
+ }
+
+ // one less edit than limit, no conflict
+ CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
+ var docB1 = dbB.open("foo",{conflicts:true})
+ T(docB1._conflicts == null);
+
+ //now we hit the limit
+ for( var i=0; i < newLimit; i++) {
+ doc.foo++;
+ T(db.save(doc).ok);
+ }
+
+ CouchDB.replicate("test_suite_db_a", "test_suite_db_b");
+
+ var docB2 = dbB.open("foo",{conflicts:true});
+
+ // we have a conflict, but the previous replicated rev is always the losing
+ // conflict
+ T(docB2._conflicts[0] == docB1._rev)
+
+ // We having already updated bar before setting the limit, so it's still got
+ // a long rev history. compact to stem the revs.
+
+ T(db.open("bar", {revs:true})._revisions.ids.length == newLimit + 1);
+
+ T(db.compact().ok);
+
+ // compaction isn't instantaneous, loop until done
+ while (db.info().compact_running) {};
+
+ T(db.open("bar", {revs:true})._revisions.ids.length == newLimit);
+
+};
diff --git a/share/www/script/test/security_validation.js b/share/www/script/test/security_validation.js
index 61f56b39..a41d8d70 100644
--- a/share/www/script/test/security_validation.js
+++ b/share/www/script/test/security_validation.js
@@ -31,28 +31,27 @@ couchTests.security_validation = function(debug) {
// the "username:password" string, it's sent completely plain text.
// Firefox and Safari both deal with this correctly (which is to say
// they correctly do nothing special).
-
-
+
+
var db = new CouchDB("test_suite_db");
db.deleteDb();
db.createDb();
if (debug) debugger;
-
+
run_on_modified_server(
[{section: "httpd",
key: "authentication_handler",
value: "{couch_httpd, special_test_authentication_handler}"},
- {section:"httpd",
+ {section:"httpd",
key: "WWW-Authenticate",
value: "X-Couch-Test-Auth"}],
-
- function () {
+ function () {
// try saving document usin the wrong credentials
var wrongPasswordDb = new CouchDB("test_suite_db",
{"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
);
-
+
try {
wrongPasswordDb.save({foo:1,author:"Damien Katz"});
T(false && "Can't get here. Should have thrown an error 1");
@@ -60,8 +59,8 @@ couchTests.security_validation = function(debug) {
T(e.error == "unauthorized");
T(wrongPasswordDb.last_req.status == 401);
}
-
-
+
+
// Create the design doc that will run custom validation code
var designDoc = {
_id:"_design/test",
@@ -83,9 +82,9 @@ couchTests.security_validation = function(debug) {
var userDb = new CouchDB("test_suite_db",
{"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
);
-
+
T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
-
+
// Attempt to save the design as a non-admin
try {
userDb.save(designDoc);
@@ -94,17 +93,17 @@ couchTests.security_validation = function(debug) {
T(e.error == "unauthorized");
T(userDb.last_req.status == 401);
}
-
- // add user as admin
- db.setAdmins(["Damien Katz"]);
-
+
+ // set user as the admin
+ T(db.setDbProperty("_admins", ["Damien Katz"]).ok);
+
T(userDb.save(designDoc).ok);
-
+
// update the document
var doc = userDb.open("testdoc");
doc.foo=2;
T(userDb.save(doc).ok);
-
+
// Save a document that's missing an author field.
try {
userDb.save({foo:1});
@@ -113,12 +112,12 @@ couchTests.security_validation = function(debug) {
T(e.error == "forbidden");
T(userDb.last_req.status == 403);
}
-
+
// Now attempt to update the document as a different user, Jan
var user2Db = new CouchDB("test_suite_db",
{"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
);
-
+
var doc = user2Db.open("testdoc");
doc.foo=3;
try {
@@ -128,17 +127,17 @@ couchTests.security_validation = function(debug) {
T(e.error == "unauthorized");
T(user2Db.last_req.status == 401);
}
-
+
// Now have Damien change the author to Jan
doc = userDb.open("testdoc");
doc.author="Jan Lehnardt";
T(userDb.save(doc).ok);
-
+
// Now update the document as Jan
doc = user2Db.open("testdoc");
doc.foo = 3;
T(user2Db.save(doc).ok);
-
+
// Damien can't delete it
try {
userDb.deleteDoc(doc);
@@ -147,8 +146,126 @@ couchTests.security_validation = function(debug) {
T(e.error == "unauthorized");
T(userDb.last_req.status == 401);
}
-
+
// Now delete document
- T(user2Db.deleteDoc(doc).ok);
+ T(user2Db.deleteDoc(doc).ok);
+
+ // now test bulk docs
+ var docs = [{_id:"bahbah",author:"Damien Katz",foo:"bar"},{_id:"fahfah",foo:"baz"}];
+
+ // Create the docs
+ var results = db.bulkSave(docs);
+
+ T(results[0].rev)
+ T(results[0].error == undefined)
+ T(results[1].rev === undefined)
+ T(results[1].error == "forbidden")
+
+ T(db.open("bahbah"));
+ T(db.open("fahfah") == null);
+
+
+ // now all or nothing with a failure
+ var docs = [{_id:"booboo",author:"Damien Katz",foo:"bar"},{_id:"foofoo",foo:"baz"}];
+
+ // Create the docs
+ var results = db.bulkSave(docs, {all_or_nothing:true});
+
+ T(results.errors.length == 1);
+ T(results.errors[0].error == "forbidden");
+ T(db.open("booboo") == null);
+ T(db.open("foofoo") == null);
+
+
+ // Now test replication
+ var AuthHeaders = {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"};
+ var host = CouchDB.host;
+ var dbPairs = [
+ {source:"test_suite_db_a",
+ target:"test_suite_db_b"},
+
+ {source:"test_suite_db_a",
+ target:{url: "http://" + host + "/test_suite_db_b",
+ headers: AuthHeaders}},
+
+ {source:{url:"http://" + host + "/test_suite_db_a",
+ headers: AuthHeaders},
+ target:"test_suite_db_b"},
+
+ {source:{url:"http://" + host + "/test_suite_db_a",
+ headers: AuthHeaders},
+ target:{url:"http://" + host + "/test_suite_db_b",
+ headers: AuthHeaders}},
+ ]
+ var adminDbA = new CouchDB("test_suite_db_a");
+ var adminDbB = new CouchDB("test_suite_db_b");
+ var dbA = new CouchDB("test_suite_db_a",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
+ var dbB = new CouchDB("test_suite_db_b",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
+ var xhr;
+ for (var testPair = 0; testPair < dbPairs.length; testPair++) {
+ var A = dbPairs[testPair].source
+ var B = dbPairs[testPair].target
+
+ adminDbA.deleteDb();
+ adminDbA.createDb();
+ adminDbB.deleteDb();
+ adminDbB.createDb();
+
+ // save and replicate a documents that will and will not pass our design
+ // doc validation function.
+ dbA.save({_id:"foo1",value:"a",author:"Noah Slater"});
+ dbA.save({_id:"foo2",value:"a",author:"Christopher Lenz"});
+ dbA.save({_id:"bad1",value:"a"});
+
+ T(CouchDB.replicate(A, B, {headers:AuthHeaders}).ok);
+ T(CouchDB.replicate(B, A, {headers:AuthHeaders}).ok);
+
+ T(dbA.open("foo1"));
+ T(dbB.open("foo1"));
+ T(dbA.open("foo2"));
+ T(dbB.open("foo2"));
+
+ // save the design doc to dbA
+ delete designDoc._rev; // clear rev from previous saves
+ adminDbA.save(designDoc);
+
+ // no affect on already saved docs
+ T(dbA.open("bad1"));
+
+ // Update some docs on dbB. Since the design hasn't replicated, anything
+ // is allowed.
+
+ // this edit will fail validation on replication to dbA (no author)
+ T(dbB.save({_id:"bad2",value:"a"}).ok);
+
+ // this edit will fail security on replication to dbA (wrong author
+ // replicating the change)
+ var foo1 = dbB.open("foo1");
+ foo1.value = "b";
+ dbB.save(foo1);
+
+ // this is a legal edit
+ var foo2 = dbB.open("foo2");
+ foo2.value = "b";
+ dbB.save(foo2);
+
+ var results = CouchDB.replicate(B, A, {headers:AuthHeaders});
+
+ T(results.ok);
+
+ T(results.history[0].docs_written == 1);
+ T(results.history[0].doc_write_failures == 2);
+
+ // bad2 should not be on dbA
+ T(dbA.open("bad2") == null);
+
+ // The edit to foo1 should not have replicated.
+ T(dbA.open("foo1").value == "a");
+
+ // The edit to foo2 should have replicated.
+ T(dbA.open("foo2").value == "b");
+ }
});
};
diff --git a/share/www/script/test/view_include_docs.js b/share/www/script/test/view_include_docs.js
index dd8ebb44..2c47b696 100644
--- a/share/www/script/test/view_include_docs.js
+++ b/share/www/script/test/view_include_docs.js
@@ -17,7 +17,7 @@ couchTests.view_include_docs = function(debug) {
if (debug) debugger;
var docs = makeDocs(0, 100);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var designDoc = {
_id:"_design/test",
diff --git a/share/www/script/test/view_multi_key_all_docs.js b/share/www/script/test/view_multi_key_all_docs.js
index f036db11..8e2464b4 100644
--- a/share/www/script/test/view_multi_key_all_docs.js
+++ b/share/www/script/test/view_multi_key_all_docs.js
@@ -17,7 +17,7 @@ couchTests.view_multi_key_all_docs = function(debug) {
if (debug) debugger;
var docs = makeDocs(0, 100);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var keys = ["10","15","30","37","50"];
var rows = db.allDocs({},keys).rows;
diff --git a/share/www/script/test/view_multi_key_design.js b/share/www/script/test/view_multi_key_design.js
index 2464ff1c..c2833910 100644
--- a/share/www/script/test/view_multi_key_design.js
+++ b/share/www/script/test/view_multi_key_design.js
@@ -17,7 +17,7 @@ couchTests.view_multi_key_design = function(debug) {
if (debug) debugger;
var docs = makeDocs(0, 100);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var designDoc = {
_id:"_design/test",
diff --git a/share/www/script/test/view_multi_key_temp.js b/share/www/script/test/view_multi_key_temp.js
index 28cd42b4..545e2520 100644
--- a/share/www/script/test/view_multi_key_temp.js
+++ b/share/www/script/test/view_multi_key_temp.js
@@ -17,7 +17,7 @@ couchTests.view_multi_key_temp = function(debug) {
if (debug) debugger;
var docs = makeDocs(0, 100);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var queryFun = function(doc) { emit(doc.integer, doc.integer) };
var reduceFun = function (keys, values) { return sum(values); };
diff --git a/share/www/script/test/view_pagination.js b/share/www/script/test/view_pagination.js
index 7d466f19..21eab888 100644
--- a/share/www/script/test/view_pagination.js
+++ b/share/www/script/test/view_pagination.js
@@ -17,7 +17,7 @@ couchTests.view_pagination = function(debug) {
if (debug) debugger;
var docs = makeDocs(0, 100);
- T(db.bulkSave(docs).ok);
+ db.bulkSave(docs);
var queryFun = function(doc) { emit(doc.integer, null) };
var i;