summaryrefslogtreecommitdiff
path: root/share/www/script/test
diff options
context:
space:
mode:
Diffstat (limited to 'share/www/script/test')
-rw-r--r--share/www/script/test/attachment_names.js4
-rw-r--r--share/www/script/test/attachment_paths.js14
-rw-r--r--share/www/script/test/attachment_views.js4
-rw-r--r--share/www/script/test/attachments.js50
-rw-r--r--share/www/script/test/attachments_multipart.js4
-rw-r--r--share/www/script/test/auth_cache.js280
-rw-r--r--share/www/script/test/basics.js19
-rw-r--r--share/www/script/test/batch_save.js5
-rw-r--r--share/www/script/test/bulk_docs.js10
-rw-r--r--share/www/script/test/changes.js280
-rw-r--r--share/www/script/test/compact.js10
-rw-r--r--share/www/script/test/conflicts.js2
-rw-r--r--share/www/script/test/content_negotiation.js9
-rw-r--r--share/www/script/test/cookie_auth.js89
-rw-r--r--share/www/script/test/delayed_commits.js29
-rw-r--r--share/www/script/test/design_docs.js55
-rw-r--r--share/www/script/test/erlang_views.js20
-rw-r--r--share/www/script/test/etags_views.js2
-rw-r--r--share/www/script/test/jsonp.js67
-rw-r--r--share/www/script/test/list_views.js23
-rw-r--r--share/www/script/test/method_override.js40
-rw-r--r--share/www/script/test/oauth.js16
-rw-r--r--share/www/script/test/proxyauth.js130
-rw-r--r--share/www/script/test/purge.js14
-rw-r--r--share/www/script/test/reader_acl.js198
-rw-r--r--share/www/script/test/recreate_doc.js2
-rw-r--r--share/www/script/test/reduce.js9
-rw-r--r--share/www/script/test/reduce_builtin.js41
-rw-r--r--share/www/script/test/replication.js412
-rw-r--r--share/www/script/test/rewrite.js380
-rw-r--r--share/www/script/test/security_validation.js89
-rw-r--r--share/www/script/test/show_documents.js81
-rw-r--r--share/www/script/test/stats.js31
-rw-r--r--share/www/script/test/update_documents.js18
-rw-r--r--share/www/script/test/users_db.js89
-rw-r--r--share/www/script/test/uuids.js18
-rw-r--r--share/www/script/test/view_collation.js12
-rw-r--r--share/www/script/test/view_collation_raw.js12
-rw-r--r--share/www/script/test/view_compaction.js104
-rw-r--r--share/www/script/test/view_errors.js46
-rw-r--r--share/www/script/test/view_multi_key_design.js6
-rw-r--r--share/www/script/test/view_sandboxing.js92
-rw-r--r--share/www/script/test/view_update_seq.js89
-rw-r--r--share/www/script/test/view_xml.js4
44 files changed, 2559 insertions, 350 deletions
diff --git a/share/www/script/test/attachment_names.js b/share/www/script/test/attachment_names.js
index d90c24c4..988dd2d2 100644
--- a/share/www/script/test/attachment_names.js
+++ b/share/www/script/test/attachment_names.js
@@ -24,7 +24,7 @@ couchTests.attachment_names = function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
+ };
// inline attachments
try {
@@ -72,7 +72,7 @@ couchTests.attachment_names = function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
+ };
try {
db.save(binAttDoc);
diff --git a/share/www/script/test/attachment_paths.js b/share/www/script/test/attachment_paths.js
index a2a0f69c..3f6ffb7c 100644
--- a/share/www/script/test/attachment_paths.js
+++ b/share/www/script/test/attachment_paths.js
@@ -33,7 +33,7 @@ couchTests.attachment_paths = function(debug) {
data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
}
}
- }
+ };
T(db.save(binAttDoc).ok);
@@ -73,7 +73,10 @@ couchTests.attachment_paths = function(debug) {
T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
T(binAttDoc._attachments["foo%2Fbaz.txt"] !== undefined);
T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
- T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8");
+ TEquals("text/plain;charset=utf-8", // thank you Safari
+ binAttDoc._attachments["foo/bar2.txt"].content_type.toLowerCase(),
+ "correct content-type"
+ );
T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
//// now repeat the while thing with a design doc
@@ -92,7 +95,7 @@ couchTests.attachment_paths = function(debug) {
data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
}
}
- }
+ };
T(db.save(binAttDoc).ok);
@@ -141,7 +144,10 @@ couchTests.attachment_paths = function(debug) {
T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
- T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8");
+ TEquals("text/plain;charset=utf-8", // thank you Safari
+ binAttDoc._attachments["foo/bar2.txt"].content_type.toLowerCase(),
+ "correct content-type"
+ );
T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
}
};
diff --git a/share/www/script/test/attachment_views.js b/share/www/script/test/attachment_views.js
index fd30dcfc..a92a8ad0 100644
--- a/share/www/script/test/attachment_views.js
+++ b/share/www/script/test/attachment_views.js
@@ -68,11 +68,11 @@ couchTests.attachment_views= function(debug) {
}
emit(parseInt(doc._id), count);
- }
+ };
var reduceFunction = function(key, values) {
return sum(values);
- }
+ };
var result = db.query(mapFunction, reduceFunction);
diff --git a/share/www/script/test/attachments.js b/share/www/script/test/attachments.js
index 36f5a5ad..e16c384f 100644
--- a/share/www/script/test/attachments.js
+++ b/share/www/script/test/attachments.js
@@ -24,7 +24,7 @@ couchTests.attachments= function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
+ };
var save_response = db.save(binAttDoc);
T(save_response.ok);
@@ -43,7 +43,7 @@ couchTests.attachments= function(debug) {
data: ""
}
}
- }
+ };
T(db.save(binAttDoc2).ok);
@@ -68,12 +68,12 @@ couchTests.attachments= function(debug) {
T(binAttDoc2._attachments["foo.txt"] !== undefined);
T(binAttDoc2._attachments["foo2.txt"] !== undefined);
- T(binAttDoc2._attachments["foo2.txt"].content_type == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", binAttDoc2._attachments["foo2.txt"].content_type);
T(binAttDoc2._attachments["foo2.txt"].length == 30);
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo2.txt");
T(xhr.responseText == "This is no base64 encoded text");
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
// test without rev, should fail
var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt");
@@ -96,7 +96,7 @@ couchTests.attachments= function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt");
T(xhr.responseText == bin_data);
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", {
headers:{"Content-Type":"text/plain;charset=utf-8"},
@@ -113,11 +113,11 @@ couchTests.attachments= function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt");
T(xhr.responseText == bin_data);
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
T(xhr.responseText == bin_data);
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
T(xhr.status == 200);
@@ -129,7 +129,7 @@ couchTests.attachments= function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev);
T(xhr.status == 200);
T(xhr.responseText == bin_data);
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
// empty attachments
var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt", {
@@ -156,7 +156,7 @@ couchTests.attachments= function(debug) {
// Attachment sparseness COUCHDB-220
- var docs = []
+ var docs = [];
for (var i = 0; i < 5; i++) {
var doc = {
_id: (i).toString(),
@@ -166,8 +166,8 @@ couchTests.attachments= function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
- docs.push(doc)
+ };
+ docs.push(doc);
}
var saved = db.bulkSave(docs);
@@ -210,7 +210,7 @@ couchTests.attachments= function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc5/lorem.txt");
T(xhr.responseText == lorem);
- T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
+ TEqualsIgnoreCase("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
// test large inline attachment too
var lorem_b64 = CouchDB.request("GET", "/_utils/script/test/lorem_b64.txt").responseText;
@@ -244,4 +244,30 @@ couchTests.attachments= function(debug) {
body: "THIS IS AN ATTACHMENT. BOOYA!"
});
TEquals(400, xhr.status, "should return error code 400 Bad Request");
+
+ // test COUCHDB-809 - stubs should only require the 'stub' field
+ var bin_doc6 = {
+ _id: "bin_doc6",
+ _attachments:{
+ "foo.txt": {
+ content_type:"text/plain",
+ data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+ }
+ }
+ };
+ T(db.save(bin_doc6).ok);
+ // stub out the attachment
+ bin_doc6._attachments["foo.txt"] = { stub: true };
+ T(db.save(bin_doc6).ok == true);
+
+ // wrong rev pos specified
+
+ // stub out the attachment with the wrong revpos
+ bin_doc6._attachments["foo.txt"] = { stub: true, revpos: 10};
+ try {
+ T(db.save(bin_doc6).ok == true);
+ T(false && "Shouldn't get here!");
+ } catch (e) {
+ T(e.error == "missing_stub");
+ }
};
diff --git a/share/www/script/test/attachments_multipart.js b/share/www/script/test/attachments_multipart.js
index 2b79e559..fecf9d01 100644
--- a/share/www/script/test/attachments_multipart.js
+++ b/share/www/script/test/attachments_multipart.js
@@ -58,7 +58,7 @@ couchTests.attachments_multipart= function(debug) {
var result = JSON.parse(xhr.responseText);
- T(result.ok)
+ T(result.ok);
@@ -193,7 +193,7 @@ couchTests.attachments_multipart= function(debug) {
// a certain rev).
xhr = CouchDB.request("GET", "/test_suite_db/multipart?atts_since=[\"" + firstrev + "\"]",
- {headers:{"accept": "multipart/related,*/*;"}});
+ {headers:{"accept": "multipart/related, */*"}});
T(xhr.status == 200);
diff --git a/share/www/script/test/auth_cache.js b/share/www/script/test/auth_cache.js
new file mode 100644
index 00000000..e48f7370
--- /dev/null
+++ b/share/www/script/test/auth_cache.js
@@ -0,0 +1,280 @@
+// 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.auth_cache = function(debug) {
+
+ if (debug) debugger;
+
+ // Simple secret key generator
+ function generateSecret(length) {
+ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
+ "0123456789+/";
+ var secret = '';
+ for (var i = 0; i < length; i++) {
+ secret += tab.charAt(Math.floor(Math.random() * 64));
+ }
+ return secret;
+ }
+
+ var authDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
+ var server_config = [
+ {
+ section: "couch_httpd_auth",
+ key: "authentication_db",
+ value: authDb.name
+ },
+ {
+ section: "couch_httpd_auth",
+ key: "auth_cache_size",
+ value: "3"
+ },
+ {
+ section: "httpd",
+ key: "authentication_handlers",
+ value: "{couch_httpd_auth, default_authentication_handler}"
+ },
+ {
+ section: "couch_httpd_auth",
+ key: "secret",
+ value: generateSecret(64)
+ }
+ ];
+
+
+ function hits() {
+ var hits = CouchDB.requestStats("couchdb", "auth_cache_hits", true);
+ return hits.current || 0;
+ }
+
+
+ function misses() {
+ var misses = CouchDB.requestStats("couchdb", "auth_cache_misses", true);
+ return misses.current || 0;
+ }
+
+
+ function testFun() {
+ var hits_before,
+ misses_before,
+ hits_after,
+ misses_after;
+
+ var fdmanana = CouchDB.prepareUserDoc({
+ name: "fdmanana",
+ roles: ["dev"]
+ }, "qwerty");
+
+ T(authDb.save(fdmanana).ok);
+
+ var chris = CouchDB.prepareUserDoc({
+ name: "chris",
+ roles: ["dev", "mafia", "white_costume"]
+ }, "the_god_father");
+
+ T(authDb.save(chris).ok);
+
+ var joe = CouchDB.prepareUserDoc({
+ name: "joe",
+ roles: ["erlnager"]
+ }, "functional");
+
+ T(authDb.save(joe).ok);
+
+ var johndoe = CouchDB.prepareUserDoc({
+ name: "johndoe",
+ roles: ["user"]
+ }, "123456");
+
+ T(authDb.save(johndoe).ok);
+
+ hits_before = hits();
+ misses_before = misses();
+
+ T(CouchDB.login("fdmanana", "qwerty").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("fdmanana", "qwerty").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 1));
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("chris", "the_god_father").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("joe", "functional").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("johndoe", "123456").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("joe", "functional").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ // it's an MRU cache, joe was removed from cache to add johndoe
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("fdmanana", "qwerty").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 1));
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ var new_salt = CouchDB.newUuids(1)[0];
+ var new_passwd = hex_sha1("foobar" + new_salt);
+ fdmanana.salt = new_salt;
+ fdmanana.password_sha = new_passwd;
+
+ T(authDb.save(fdmanana).ok);
+ T(CouchDB.logout().ok);
+
+ // cache was refreshed
+ T(CouchDB.login("fdmanana", "qwerty").error === "unauthorized");
+ T(CouchDB.login("fdmanana", "foobar").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 2));
+
+ T(CouchDB.logout().ok);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ // and yet another update
+ new_salt = CouchDB.newUuids(1)[0];
+ new_passwd = hex_sha1("javascript" + new_salt);
+ fdmanana.salt = new_salt;
+ fdmanana.password_sha = new_passwd;
+
+ T(authDb.save(fdmanana).ok);
+ T(CouchDB.logout().ok);
+
+ // cache was refreshed
+ T(CouchDB.login("fdmanana", "foobar").error === "unauthorized");
+ T(CouchDB.login("fdmanana", "javascript").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 2));
+
+ T(authDb.deleteDoc(fdmanana).ok);
+ T(CouchDB.logout().ok);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.login("fdmanana", "javascript").error === "unauthorized");
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 1));
+
+ // login, compact authentication DB, login again and verify that
+ // there was a cache hit
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.login("johndoe", "123456").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === (misses_before + 1));
+ T(hits_after === hits_before);
+
+ T(CouchDB.logout().ok);
+ T(authDb.compact().ok);
+
+ while (authDb.info().compact_running);
+
+ hits_before = hits_after;
+ misses_before = misses_after;
+
+ T(CouchDB.login("johndoe", "123456").ok);
+
+ hits_after = hits();
+ misses_after = misses();
+
+ T(misses_after === misses_before);
+ T(hits_after === (hits_before + 1));
+
+ T(CouchDB.logout().ok);
+ }
+
+
+ authDb.deleteDb();
+ run_on_modified_server(server_config, testFun);
+
+ // cleanup
+ authDb.deleteDb();
+} \ No newline at end of file
diff --git a/share/www/script/test/basics.js b/share/www/script/test/basics.js
index 0f9ac44f..8885ba6e 100644
--- a/share/www/script/test/basics.js
+++ b/share/www/script/test/basics.js
@@ -45,7 +45,7 @@ couchTests.basics = function(debug) {
// Get the database info, check the db_name
T(db.info().db_name == "test_suite_db");
- T(CouchDB.allDbs().indexOf("test_suite_db") != -1)
+ T(CouchDB.allDbs().indexOf("test_suite_db") != -1);
// Get the database info, check the doc_count
T(db.info().doc_count == 0);
@@ -91,13 +91,13 @@ couchTests.basics = function(debug) {
emit(null, doc.b);
};
- results = db.query(mapFunction);
+ var results = db.query(mapFunction);
// verify only one document found and the result value (doc.b).
T(results.total_rows == 1 && results.rows[0].value == 16);
// reopen document we saved earlier
- existingDoc = db.open(id);
+ var existingDoc = db.open(id);
T(existingDoc.a==1);
@@ -152,7 +152,8 @@ couchTests.basics = function(debug) {
// test that the POST response has a Location header
var xhr = CouchDB.request("POST", "/test_suite_db", {
- body: JSON.stringify({"foo":"bar"})
+ body: JSON.stringify({"foo":"bar"}),
+ headers: {"Content-Type": "application/json"}
});
var resp = JSON.parse(xhr.responseText);
T(resp.ok);
@@ -164,6 +165,7 @@ couchTests.basics = function(debug) {
// test that that POST's with an _id aren't overriden with a UUID.
var xhr = CouchDB.request("POST", "/test_suite_db", {
+ headers: {"Content-Type": "application/json"},
body: JSON.stringify({"_id": "oppossum", "yar": "matey"})
});
var resp = JSON.parse(xhr.responseText);
@@ -189,12 +191,12 @@ couchTests.basics = function(debug) {
T(xhr.status == 404);
// Check for invalid document members
- bad_docs = [
+ var bad_docs = [
["goldfish", {"_zing": 4}],
["zebrafish", {"_zoom": "hello"}],
["mudfish", {"zane": "goldfish", "_fan": "something smells delicious"}],
["tastyfish", {"_bing": {"wha?": "soda can"}}]
- ]
+ ];
var test_doc = function(info) {
var data = JSON.stringify(info[1]);
xhr = CouchDB.request("PUT", "/test_suite_db/" + info[0], {body: data});
@@ -202,7 +204,10 @@ couchTests.basics = function(debug) {
result = JSON.parse(xhr.responseText);
T(result.error == "doc_validation");
- xhr = CouchDB.request("POST", "/test_suite_db/", {body: data});
+ xhr = CouchDB.request("POST", "/test_suite_db/", {
+ headers: {"Content-Type": "application/json"},
+ body: data
+ });
T(xhr.status == 500);
result = JSON.parse(xhr.responseText);
T(result.error == "doc_validation");
diff --git a/share/www/script/test/batch_save.js b/share/www/script/test/batch_save.js
index 1c8a2be9..a1b00192 100644
--- a/share/www/script/test/batch_save.js
+++ b/share/www/script/test/batch_save.js
@@ -36,7 +36,10 @@ couchTests.batch_save = function(debug) {
// repeat the tests for POST
for(i=0; i < 100; i++) {
- var resp = db.request("POST", db.uri + "?batch=ok", {body: JSON.stringify({a:1})});
+ var resp = db.request("POST", db.uri + "?batch=ok", {
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({a:1})
+ });
T(JSON.parse(resp.responseText).ok);
}
diff --git a/share/www/script/test/bulk_docs.js b/share/www/script/test/bulk_docs.js
index 346aea83..9095e6b3 100644
--- a/share/www/script/test/bulk_docs.js
+++ b/share/www/script/test/bulk_docs.js
@@ -51,12 +51,12 @@ couchTests.bulk_docs = function(debug) {
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
+ T(typeof 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(results[i].rev);
T(db.open(docs[i]._id) == null);
}
@@ -64,7 +64,7 @@ couchTests.bulk_docs = function(debug) {
// save doc 0, this will cause a conflict when we save docs[0]
var doc = db.open("0");
- docs[0] = db.open("0")
+ docs[0] = db.open("0");
db.save(doc);
docs[0].shooby = "dooby";
@@ -93,8 +93,8 @@ couchTests.bulk_docs = function(debug) {
// Regression test for failure on update/delete
var newdoc = {"_id": "foobar", "body": "baz"};
T(db.save(newdoc).ok);
- update = {"_id": newdoc._id, "_rev": newdoc._rev, "body": "blam"};
- torem = {"_id": newdoc._id, "_rev": newdoc._rev, "_deleted": true};
+ var update = {"_id": newdoc._id, "_rev": newdoc._rev, "body": "blam"};
+ var torem = {"_id": newdoc._id, "_rev": newdoc._rev, "_deleted": true};
results = db.bulkSave([update, torem]);
T(results[0].error == "conflict" || results[1].error == "conflict");
};
diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js
index 0cbf3bd6..50649508 100644
--- a/share/www/script/test/changes.js
+++ b/share/www/script/test/changes.js
@@ -17,7 +17,7 @@ function jsonp(obj) {
}
couchTests.changes = function(debug) {
- var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
db.deleteDb();
db.createDb();
if (debug) debugger;
@@ -29,20 +29,28 @@ couchTests.changes = function(debug) {
var docFoo = {_id:"foo", bar:1};
T(db.save(docFoo).ok);
T(db.ensureFullCommit().ok);
+ T(db.open(docFoo._id)._id == docFoo._id);
req = CouchDB.request("GET", "/test_suite_db/_changes");
var resp = JSON.parse(req.responseText);
- T(resp.results.length == 1 && resp.last_seq==1, "one doc db")
+ T(resp.last_seq == 1);
+ T(resp.results.length == 1, "one doc db")
T(resp.results[0].changes[0].rev == docFoo._rev)
// test with callback
- var xhr = CouchDB.request("GET", "/test_suite_db/_changes?callback=jsonp");
- T(xhr.status == 200);
- jsonp_flag = 0;
- eval(xhr.responseText);
- T(jsonp_flag == 1);
-
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "allow_jsonp",
+ value: "true"}],
+ function() {
+ var xhr = CouchDB.request("GET", "/test_suite_db/_changes?callback=jsonp");
+ T(xhr.status == 200);
+ jsonp_flag = 0;
+ eval(xhr.responseText);
+ T(jsonp_flag == 1);
+ });
req = CouchDB.request("GET", "/test_suite_db/_changes?feed=continuous&timeout=10");
var lines = req.responseText.split("\n");
@@ -70,98 +78,109 @@ couchTests.changes = function(debug) {
// WebKit (last checked on nightly #47686) does fail on processing
// the async-request properly while javascript is executed.
- var sleep = function(msecs) {
- // by making a slow sync request, we allow the waiting XHR request data
- // to be received.
- var req = CouchDB.request("GET", "/_sleep?time=" + msecs);
- T(JSON.parse(req.responseText).ok == true);
- }
-
- xhr.open("GET", "/test_suite_db/_changes?feed=continuous", true);
+ xhr.open("GET", "/test_suite_db/_changes?feed=continuous&timeout=500", true);
xhr.send("");
var docBar = {_id:"bar", bar:1};
db.save(docBar);
+
+ var lines, change1, change2;
+ waitForSuccess(function() {
+ lines = xhr.responseText.split("\n");
+ change1 = JSON.parse(lines[0]);
+ change2 = JSON.parse(lines[1]);
+ if (change2.seq != 2) {
+ throw "bad seq, try again"
+ }
+ }, "bar-only");
- sleep(100);
- var lines = xhr.responseText.split("\n");
-
- var change = JSON.parse(lines[0]);
-
- T(change.seq == 1)
- T(change.id == "foo")
-
- change = JSON.parse(lines[1]);
-
- T(change.seq == 2)
- T(change.id == "bar")
- T(change.changes[0].rev == docBar._rev)
-
+ T(change1.seq == 1)
+ T(change1.id == "foo")
+
+ T(change2.seq == 2)
+ T(change2.id == "bar")
+ T(change2.changes[0].rev == docBar._rev)
+
+
var docBaz = {_id:"baz", baz:1};
db.save(docBaz);
- sleep(100);
- var lines = xhr.responseText.split("\n");
-
- change = JSON.parse(lines[2]);
-
- T(change.seq == 3);
- T(change.id == "baz");
- T(change.changes[0].rev == docBaz._rev);
+ var change3;
+ waitForSuccess(function() {
+ lines = xhr.responseText.split("\n");
+ change3 = JSON.parse(lines[2]);
+ if (change3.seq != 3) {
+ throw "bad seq, try again"
+ }
+ });
+
+ T(change3.seq == 3);
+ T(change3.id == "baz");
+ T(change3.changes[0].rev == docBaz._rev);
xhr = CouchDB.newXhr();
//verify the hearbeat newlines are sent
- xhr.open("GET", "/test_suite_db/_changes?feed=continuous&heartbeat=10", true);
+ xhr.open("GET", "/test_suite_db/_changes?feed=continuous&heartbeat=10&timeout=500", true);
xhr.send("");
-
- sleep(100);
-
- var str = xhr.responseText;
+
+ var str;
+ waitForSuccess(function() {
+ str = xhr.responseText;
+ if (str.charAt(str.length - 1) != "\n" || str.charAt(str.length - 2) != "\n") {
+ throw("keep waiting");
+ }
+ }, "heartbeat");
T(str.charAt(str.length - 1) == "\n")
T(str.charAt(str.length - 2) == "\n")
+ // otherwise we'll continue to receive heartbeats forever
+ xhr.abort();
// test longpolling
xhr = CouchDB.newXhr();
xhr.open("GET", "/test_suite_db/_changes?feed=longpoll", true);
xhr.send("");
-
- sleep(100);
- var lines = xhr.responseText.split("\n");
- T(lines[5]=='"last_seq":3}');
-
+
+ waitForSuccess(function() {
+ lines = xhr.responseText.split("\n");
+ if (lines[5] != '"last_seq":3}') {
+ throw("still waiting");
+ }
+ }, "last_seq");
+
xhr = CouchDB.newXhr();
xhr.open("GET", "/test_suite_db/_changes?feed=longpoll&since=3", true);
xhr.send("");
- sleep(100);
-
var docBarz = {_id:"barz", bar:1};
db.save(docBarz);
-
- sleep(100);
-
- var lines = xhr.responseText.split("\n");
-
+
var parse_changes_line = function(line) {
if (line.charAt(line.length-1) == ",") {
- line = line.substring(0, line.length-1);
+ var linetrimmed = line.substring(0, line.length-1);
+ } else {
+ var linetrimmed = line
}
- return JSON.parse(line);
+ return JSON.parse(linetrimmed);
}
-
- change = parse_changes_line(lines[1]);
-
+
+ waitForSuccess(function() {
+ lines = xhr.responseText.split("\n");
+ if (lines[3] != '"last_seq":4}') {
+ throw("still waiting");
+ }
+ }, "change_lines");
+
+ var change = parse_changes_line(lines[1]);
T(change.seq == 4);
T(change.id == "barz");
T(change.changes[0].rev == docBarz._rev);
T(lines[3]=='"last_seq":4}');
-
}
// test the filtered changes
@@ -175,7 +194,16 @@ couchTests.changes = function(debug) {
}),
"userCtx" : stringFun(function(doc, req) {
return doc.user && (doc.user == req.userCtx.name);
- })
+ }),
+ "conflicted" : "function(doc, req) { return (doc._conflicts);}",
+ },
+ options : {
+ local_seq : true
+ },
+ views : {
+ local_seq : {
+ map : "function(doc) {emit(doc._local_seq, null)}"
+ }
}
}
@@ -190,7 +218,7 @@ couchTests.changes = function(debug) {
var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/bop");
var resp = JSON.parse(req.responseText);
- T(resp.results.length == 1);
+ T(resp.results.length == 1, "filtered/bop");
req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/dynamic&field=woox");
resp = JSON.parse(req.responseText);
@@ -198,15 +226,14 @@ couchTests.changes = function(debug) {
req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/dynamic&field=bop");
resp = JSON.parse(req.responseText);
- T(resp.results.length == 1);
+ T(resp.results.length == 1, "changes_filter/dynamic&field=bop");
if (!is_safari && xhr) { // full test requires parallel connections
// filter with longpoll
// longpoll filters full history when run without a since seq
xhr = CouchDB.newXhr();
- xhr.open("GET", "/test_suite_db/_changes?feed=longpoll&filter=changes_filter/bop", true);
+ xhr.open("GET", "/test_suite_db/_changes?feed=longpoll&filter=changes_filter/bop", false);
xhr.send("");
- sleep(100);
var resp = JSON.parse(xhr.responseText);
T(resp.last_seq == 7);
// longpoll waits until a matching change before returning
@@ -215,22 +242,53 @@ couchTests.changes = function(debug) {
xhr.send("");
db.save({"_id":"falsy", "bop" : ""}); // empty string is falsy
db.save({"_id":"bingo","bop" : "bingo"});
- sleep(100);
- var resp = JSON.parse(xhr.responseText);
+
+ waitForSuccess(function() {
+ resp = JSON.parse(xhr.responseText);
+ }, "longpoll-since");
+
T(resp.last_seq == 9);
T(resp.results && resp.results.length > 0 && resp.results[0]["id"] == "bingo", "filter the correct update");
-
- // filter with continuous
- xhr = CouchDB.newXhr();
- xhr.open("GET", "/test_suite_db/_changes?feed=continuous&filter=changes_filter/bop&timeout=200", true);
- xhr.send("");
- db.save({"_id":"rusty", "bop" : "plankton"});
- T(db.ensureFullCommit().ok);
- sleep(300);
- var lines = xhr.responseText.split("\n");
- T(JSON.parse(lines[1]).id == "bingo", lines[1]);
- T(JSON.parse(lines[2]).id == "rusty", lines[2]);
- T(JSON.parse(lines[3]).last_seq == 10, lines[3]);
+ xhr.abort();
+
+ timeout = 500;
+ last_seq = 10
+ while (true) {
+
+ // filter with continuous
+ xhr = CouchDB.newXhr();
+ xhr.open("GET", "/test_suite_db/_changes?feed=continuous&filter=changes_filter/bop&timeout="+timeout, true);
+ xhr.send("");
+
+ db.save({"_id":"rusty", "bop" : "plankton"});
+ T(xhr.readyState != 4, "test client too slow");
+ var rusty = db.open("rusty", {cache_bust : new Date()});
+ T(rusty._id == "rusty");
+
+ waitForSuccess(function() { // throws an error after 5 seconds
+ if (xhr.readyState != 4) {
+ throw("still waiting")
+ }
+ }, "continuous-rusty");
+ lines = xhr.responseText.split("\n");
+ try {
+ JSON.parse(lines[3])
+ good = true;
+ } catch(e) {
+ good = false;
+ }
+ if (good) {
+ T(JSON.parse(lines[1]).id == "bingo", lines[1]);
+ T(JSON.parse(lines[2]).id == "rusty", lines[2]);
+ T(JSON.parse(lines[3]).last_seq == last_seq, lines[3]);
+ break;
+ } else {
+ xhr.abort();
+ db.deleteDoc(rusty);
+ timeout = timeout * 2;
+ last_seq = last_seq + 2;
+ }
+ }
}
// error conditions
@@ -257,7 +315,8 @@ couchTests.changes = function(debug) {
var req = CouchDB.request("GET",
"/test_suite_db/_changes?filter=changes_filter/bop&style=all_docs");
var resp = JSON.parse(req.responseText);
- TEquals(3, resp.results.length, "should return matching rows");
+ var expect = (!is_safari && xhr) ? 3: 1;
+ TEquals(expect, resp.results.length, "should return matching rows");
// test for userCtx
run_on_modified_server(
@@ -286,9 +345,58 @@ couchTests.changes = function(debug) {
resp = JSON.parse(req.responseText);
T(resp.results.length == 1, "userCtx");
T(resp.results[0].id == docResp.id);
- });
-
- req = CouchDB.request("GET", "/test_suite_db/_changes?limit=1");
- resp = JSON.parse(req.responseText);
- TEquals(1, resp.results.length)
+ }
+ );
+
+ req = CouchDB.request("GET", "/test_suite_db/_changes?limit=1");
+ resp = JSON.parse(req.responseText);
+ TEquals(1, resp.results.length)
+
+ //filter includes _conflicts
+ var id = db.save({'food' : 'pizza'}).id;
+ db.bulkSave([{_id: id, 'food' : 'pasta'}], {all_or_nothing:true});
+
+ req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/conflicted");
+ resp = JSON.parse(req.responseText);
+ T(resp.results.length == 1, "filter=changes_filter/conflicted");
+
+ // test with erlang filter function
+ run_on_modified_server([{
+ section: "native_query_servers",
+ key: "erlang",
+ value: "{couch_native_process, start_link, []}"
+ }], function() {
+ var erl_ddoc = {
+ _id: "_design/erlang",
+ language: "erlang",
+ filters: {
+ foo:
+ 'fun({Doc}, Req) -> ' +
+ ' Value = couch_util:get_value(<<"value">>, Doc),' +
+ ' (Value rem 2) =:= 0' +
+ 'end.'
+ }
+ };
+
+ db.deleteDb();
+ db.createDb();
+ T(db.save(erl_ddoc).ok);
+
+ var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=erlang/foo");
+ var resp = JSON.parse(req.responseText);
+ T(resp.results.length === 0);
+
+ T(db.save({_id: "doc1", value : 1}).ok);
+ T(db.save({_id: "doc2", value : 2}).ok);
+ T(db.save({_id: "doc3", value : 3}).ok);
+ T(db.save({_id: "doc4", value : 4}).ok);
+
+ var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=erlang/foo");
+ var resp = JSON.parse(req.responseText);
+ T(resp.results.length === 2);
+ T(resp.results[0].id === "doc2");
+ T(resp.results[1].id === "doc4");
+ });
+
};
+
diff --git a/share/www/script/test/compact.js b/share/www/script/test/compact.js
index f63bfc57..805a3b08 100644
--- a/share/www/script/test/compact.js
+++ b/share/www/script/test/compact.js
@@ -26,11 +26,12 @@ couchTests.compact = function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
+ };
T(db.save(binAttDoc).ok);
var originalsize = db.info().disk_size;
+ var start_time = db.info().instance_start_time;
for(var i in docs) {
db.deleteDoc(docs[i]);
@@ -38,17 +39,20 @@ couchTests.compact = function(debug) {
T(db.ensureFullCommit().ok);
var deletesize = db.info().disk_size;
T(deletesize > originalsize);
+ T(db.setDbProperty("_revs_limit", 666).ok);
T(db.compact().ok);
T(db.last_req.status == 202);
// compaction isn't instantaneous, loop until done
while (db.info().compact_running) {};
+ T(db.info().instance_start_time == start_time);
+ T(db.getDbProperty("_revs_limit") === 666);
T(db.ensureFullCommit().ok);
restartServer();
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.responseText == "This is a base64 encoded text");
+ T(xhr.getResponseHeader("Content-Type") == "text/plain");
T(db.info().doc_count == 1);
T(db.info().disk_size < deletesize);
diff --git a/share/www/script/test/conflicts.js b/share/www/script/test/conflicts.js
index b8b93946..7258bc31 100644
--- a/share/www/script/test/conflicts.js
+++ b/share/www/script/test/conflicts.js
@@ -44,7 +44,7 @@ couchTests.conflicts = function(debug) {
var changes = db.changes();
- T( changes.results.length == 1)
+ T(changes.results.length == 1);
// Now clear out the _rev member and save. This indicates this document is
// new, not based on an existing revision.
diff --git a/share/www/script/test/content_negotiation.js b/share/www/script/test/content_negotiation.js
index 171dbb3d..c79df948 100644
--- a/share/www/script/test/content_negotiation.js
+++ b/share/www/script/test/content_negotiation.js
@@ -17,11 +17,14 @@ couchTests.content_negotiation = function(debug) {
if (debug) debugger;
var xhr;
- xhr = CouchDB.request("GET", "/test_suite_db/");
- TEquals("text/plain;charset=utf-8", xhr.getResponseHeader("Content-Type"));
+ // with no accept header
+ var req = CouchDB.newXhr();
+ req.open("GET", "/test_suite_db/", false);
+ req.send("");
+ TEquals("text/plain;charset=utf-8", req.getResponseHeader("Content-Type"));
// make sure JSON responses end in a newline
- var text = xhr.responseText;
+ var text = req.responseText;
TEquals("\n", text[text.length-1]);
xhr = CouchDB.request("GET", "/test_suite_db/", {
diff --git a/share/www/script/test/cookie_auth.js b/share/www/script/test/cookie_auth.js
index 9eadfee0..ef915602 100644
--- a/share/www/script/test/cookie_auth.js
+++ b/share/www/script/test/cookie_auth.js
@@ -46,39 +46,39 @@ couchTests.cookie_auth = function(debug) {
// Create a user
var jasonUserDoc = CouchDB.prepareUserDoc({
- username: "Jason Davies",
+ name: "Jason Davies",
roles: ["dev"]
}, password);
T(usersDb.save(jasonUserDoc).ok);
var checkDoc = usersDb.open(jasonUserDoc._id);
- T(checkDoc.username == "Jason Davies");
+ T(checkDoc.name == "Jason Davies");
var jchrisUserDoc = CouchDB.prepareUserDoc({
- username: "jchris@apache.org"
+ name: "jchris@apache.org"
}, "funnybone");
T(usersDb.save(jchrisUserDoc).ok);
// make sure we cant create duplicate users
var duplicateJchrisDoc = CouchDB.prepareUserDoc({
- username: "jchris@apache.org"
+ name: "jchris@apache.org"
}, "eh, Boo-Boo?");
try {
- usersDb.save(duplicateJchrisDoc)
+ usersDb.save(duplicateJchrisDoc);
T(false && "Can't create duplicate user names. Should have thrown an error.");
} catch (e) {
T(e.error == "conflict");
T(usersDb.last_req.status == 409);
}
- // we can't create _usernames
+ // we can't create _names
var underscoreUserDoc = CouchDB.prepareUserDoc({
- username: "_why"
+ name: "_why"
}, "copperfield");
try {
- usersDb.save(underscoreUserDoc)
+ usersDb.save(underscoreUserDoc);
T(false && "Can't create underscore user names. Should have thrown an error.");
} catch (e) {
T(e.error == "forbidden");
@@ -87,46 +87,47 @@ couchTests.cookie_auth = function(debug) {
// we can't create docs with malformed ids
var badIdDoc = CouchDB.prepareUserDoc({
- username: "foo"
+ name: "foo"
}, "bar");
badIdDoc._id = "org.apache.couchdb:w00x";
try {
- usersDb.save(badIdDoc)
+ usersDb.save(badIdDoc);
T(false && "Can't create malformed docids. Should have thrown an error.");
} catch (e) {
T(e.error == "forbidden");
T(usersDb.last_req.status == 403);
}
-
- try {
- usersDb.save(underscoreUserDoc)
- T(false && "Can't create underscore user names. Should have thrown an error.");
- } catch (e) {
- T(e.error == "forbidden");
- T(usersDb.last_req.status == 403);
- }
// login works
T(CouchDB.login('Jason Davies', password).ok);
- T(CouchDB.session().name == 'Jason Davies');
+ T(CouchDB.session().userCtx.name == 'Jason Davies');
// update one's own credentials document
jasonUserDoc.foo=2;
T(usersDb.save(jasonUserDoc).ok);
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
+ // can't delete another users doc unless you are admin
+ try {
+ usersDb.deleteDoc(jchrisUserDoc);
+ T(false && "Can't delete other users docs. Should have thrown an error.");
+ } catch (e) {
+ T(e.error == "forbidden");
+ T(usersDb.last_req.status == 403);
+ }
// TODO should login() throw an exception here?
T(!CouchDB.login('Jason Davies', "2.71828").ok);
T(!CouchDB.login('Robert Allen Zimmerman', 'd00d').ok);
// a failed login attempt should log you out
- T(CouchDB.session().name != 'Jason Davies');
+ T(CouchDB.session().userCtx.name != 'Jason Davies');
// test redirect
- xhr = CouchDB.request("POST", "/_session?next=/", {
+ var xhr = CouchDB.request("POST", "/_session?next=/", {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
- body: "username=Jason%20Davies&password="+encodeURIComponent(password)
+ body: "name=Jason%20Davies&password="+encodeURIComponent(password)
});
// should this be a redirect code instead of 200?
// The cURL adapter is returning the expected 302 here.
@@ -134,23 +135,23 @@ couchTests.cookie_auth = function(debug) {
// to follow the redirect, ie, the browser follows and does a
// GET on the returned Location
if (xhr.status == 200) {
- T(/Welcome/.test(xhr.responseText))
+ T(/Welcome/.test(xhr.responseText));
} else {
- T(xhr.status == 302)
- T(xhr.getResponseHeader("Location"))
+ T(xhr.status == 302);
+ T(xhr.getResponseHeader("Location"));
}
// test users db validations
//
// test that you can't update docs unless you are logged in as the user (or are admin)
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
- T(CouchDB.session().name == "jchris@apache.org");
- T(CouchDB.session().roles.length == 0);
+ T(CouchDB.session().userCtx.name == "jchris@apache.org");
+ T(CouchDB.session().userCtx.roles.length == 0);
jasonUserDoc.foo=3;
try {
- usersDb.save(jasonUserDoc)
+ usersDb.save(jasonUserDoc);
T(false && "Can't update someone else's user doc. Should have thrown an error.");
} catch (e) {
T(e.error == "forbidden");
@@ -161,7 +162,7 @@ couchTests.cookie_auth = function(debug) {
jchrisUserDoc.roles = ["foo"];
try {
- usersDb.save(jchrisUserDoc)
+ usersDb.save(jchrisUserDoc);
T(false && "Can't set roles unless you are admin. Should have thrown an error.");
} catch (e) {
T(e.error == "forbidden");
@@ -169,7 +170,7 @@ couchTests.cookie_auth = function(debug) {
}
T(CouchDB.logout().ok);
- T(CouchDB.session().roles[0] == "_admin");
+ T(CouchDB.session().userCtx.roles[0] == "_admin");
jchrisUserDoc.foo = ["foo"];
T(usersDb.save(jchrisUserDoc).ok);
@@ -178,7 +179,7 @@ couchTests.cookie_auth = function(debug) {
jchrisUserDoc.roles = ["_bar"];
try {
- usersDb.save(jchrisUserDoc)
+ usersDb.save(jchrisUserDoc);
T(false && "Can't add system roles to user's db. Should have thrown an error.");
} catch (e) {
T(e.error == "forbidden");
@@ -187,24 +188,24 @@ couchTests.cookie_auth = function(debug) {
// make sure the foo role has been applied
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
- T(CouchDB.session().name == "jchris@apache.org");
- T(CouchDB.session().roles.indexOf("_admin") == -1);
- T(CouchDB.session().roles.indexOf("foo") != -1);
+ T(CouchDB.session().userCtx.name == "jchris@apache.org");
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
+ T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
// now let's make jchris a server admin
T(CouchDB.logout().ok);
- T(CouchDB.session().roles[0] == "_admin");
- T(CouchDB.session().name == null);
+ T(CouchDB.session().userCtx.roles[0] == "_admin");
+ T(CouchDB.session().userCtx.name == null);
// set the -hashed- password so the salt matches
// todo ask on the ML about this
run_on_modified_server([{section: "admins",
key: "jchris@apache.org", value: "funnybone"}], function() {
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
- T(CouchDB.session().name == "jchris@apache.org");
- T(CouchDB.session().roles.indexOf("_admin") != -1);
+ T(CouchDB.session().userCtx.name == "jchris@apache.org");
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
// test that jchris still has the foo role
- T(CouchDB.session().roles.indexOf("foo") != -1);
+ T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
// should work even when user doc has no password
jchrisUserDoc = usersDb.open(jchrisUserDoc._id);
@@ -214,13 +215,13 @@ couchTests.cookie_auth = function(debug) {
T(CouchDB.logout().ok);
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
var s = CouchDB.session();
- T(s.name == "jchris@apache.org");
- T(s.roles.indexOf("_admin") != -1);
+ T(s.userCtx.name == "jchris@apache.org");
+ T(s.userCtx.roles.indexOf("_admin") != -1);
// test session info
- T(s.info.authenticated == "{couch_httpd_auth, cookie_authentication_handler}");
- T(s.info.user_db == "test_suite_users");
+ T(s.info.authenticated == "cookie");
+ T(s.info.authentication_db == "test_suite_users");
// test that jchris still has the foo role
- T(CouchDB.session().roles.indexOf("foo") != -1);
+ T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
});
} finally {
diff --git a/share/www/script/test/delayed_commits.js b/share/www/script/test/delayed_commits.js
index d0c87182..dbb072fb 100644
--- a/share/www/script/test/delayed_commits.js
+++ b/share/www/script/test/delayed_commits.js
@@ -122,4 +122,33 @@ couchTests.delayed_commits = function(debug) {
}
});
+
+ // Test that a conflict can't cause delayed commits to fail
+ run_on_modified_server(
+ [{section: "couchdb",
+ key: "delayed_commits",
+ value: "true"}],
+
+ function() {
+ //First save a document and commit it
+ T(db.save({_id:"6",a:2,b:4}).ok);
+ T(db.ensureFullCommit().ok);
+ //Generate a conflict
+ try {
+ db.save({_id:"6",a:2,b:4});
+ } catch( e) {
+ T(e.error == "conflict");
+ }
+ //Wait for the delayed commit interval to pass
+ var time = new Date();
+ while(new Date() - time < 2000);
+ //Save a new doc
+ T(db.save({_id:"7",a:2,b:4}).ok);
+ //Wait for the delayed commit interval to pass
+ var time = new Date();
+ while(new Date() - time < 2000);
+ //Crash the server and make sure the last doc was written
+ restartServer();
+ T(db.open("7") != null);
+ });
};
diff --git a/share/www/script/test/design_docs.js b/share/www/script/test/design_docs.js
index 9318d2bc..a24167b2 100644
--- a/share/www/script/test/design_docs.js
+++ b/share/www/script/test/design_docs.js
@@ -38,6 +38,13 @@ function() {
var designDoc = {
_id:"_design/test", // turn off couch.js id escaping?
language: "javascript",
+ whatever : {
+ stringzone : "exports.string = 'plankton';",
+ commonjs : {
+ whynot : "exports.test = require('../stringzone'); exports.foo = require('whatever/stringzone');",
+ upper : "exports.testing = require('./whynot').test.string.toUpperCase()+module.id+require('./whynot').foo.string"
+ }
+ },
views: {
all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
no_docs: {map: "function(doc) {}"},
@@ -50,9 +57,12 @@ function() {
reduce:"function (keys, values) { return \"" + makebigstring(16) + "\"; };"}
},
shows: {
- simple: "function() {return 'ok'};"
+ simple: "function() {return 'ok'};",
+ requirey : "function() { var lib = require('whatever/commonjs/upper'); return lib.testing; };",
+ circular : "function() { var lib = require('whatever/commonjs/upper'); return JSON.stringify(this); };"
}
- }
+ };
+
var xhr = CouchDB.request("PUT", "/test_suite_db_a/_design/test", {body: JSON.stringify(designDoc)});
var resp = JSON.parse(xhr.responseText);
@@ -74,15 +84,46 @@ function() {
T(xhr.status == 200);
TEquals("ok", xhr.responseText, 'query server used wrong ddoc');
+ // test commonjs require
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/requirey");
+ T(xhr.status == 200);
+ TEquals("PLANKTONwhatever/commonjs/upperplankton", xhr.responseText);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/circular");
+ T(xhr.status == 200);
+ TEquals("javascript", JSON.parse(xhr.responseText).language);
+
+ var prev_view_sig = db.designInfo("_design/test").view_index.signature;
+
+ db.bulkSave(makeDocs(1, numDocs + 1));
+
// test that we get design doc info back
var dinfo = db.designInfo("_design/test");
TEquals("test", dinfo.name);
var vinfo = dinfo.view_index;
TEquals(51, vinfo.disk_size);
TEquals(false, vinfo.compact_running);
- TEquals("3f88e53b303e2342e49a66c538c30679", vinfo.signature);
+ // test that GET /db/_design/test/_info
+ // hasn't triggered an update of the views
+ TEquals(prev_view_sig, vinfo.signature, 'ddoc sig');
+ for (var loop = 0; loop < 2; loop++) {
+ T(db.view("test/all_docs_twice", {stale: "ok"}).total_rows === 0);
+ T(db.view("test/single_doc", {stale: "ok"}).total_rows === 0);
+ T(db.view("test/summate", {stale: "ok"}).rows.length === 0);
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ };
- db.bulkSave(makeDocs(1, numDocs + 1));
+ // test that POST /db/_view_cleanup
+ // doesn't trigger an update of the views
+ T(db.viewCleanup().ok);
+ for (var loop = 0; loop < 2; loop++) {
+ T(db.view("test/all_docs_twice", {stale: "ok"}).total_rows == 0);
+ T(db.view("test/single_doc", {stale: "ok"}).total_rows == 0);
+ T(db.view("test/summate", {stale: "ok"}).rows.length == 0);
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ };
// test that the _all_docs view returns correctly with keys
var results = db.allDocs({startkey:"_design", endkey:"_design0"});
@@ -93,9 +134,9 @@ function() {
for (var i = 0; i < numDocs; i++) {
T(rows[2*i].key == i+1);
T(rows[(2*i)+1].key == i+1);
- }
- T(db.view("test/no_docs").total_rows == 0)
- T(db.view("test/single_doc").total_rows == 1)
+ };
+ T(db.view("test/no_docs").total_rows == 0);
+ T(db.view("test/single_doc").total_rows == 1);
T(db.ensureFullCommit().ok);
restartServer();
};
diff --git a/share/www/script/test/erlang_views.js b/share/www/script/test/erlang_views.js
index e9e0363f..7eddab40 100644
--- a/share/www/script/test/erlang_views.js
+++ b/share/www/script/test/erlang_views.js
@@ -29,8 +29,8 @@ couchTests.erlang_views = function(debug) {
T(db.save(doc).ok);
var mfun = 'fun({Doc}) -> ' +
- ' K = proplists:get_value(<<"integer">>, Doc, null), ' +
- ' V = proplists:get_value(<<"string">>, Doc, null), ' +
+ ' K = couch_util:get_value(<<"integer">>, Doc, null), ' +
+ ' V = couch_util:get_value(<<"string">>, Doc, null), ' +
' Emit(K, V) ' +
'end.';
@@ -44,7 +44,7 @@ couchTests.erlang_views = function(debug) {
// check simple reduction - another doc with same key.
var doc = {_id: "2", integer: 1, string: "str2"};
T(db.save(doc).ok);
- rfun = "fun(Keys, Values, ReReduce) -> length(Values) end."
+ rfun = "fun(Keys, Values, ReReduce) -> length(Values) end.";
results = db.query(mfun, rfun, null, null, "erlang");
T(results.rows[0].value == 2);
@@ -55,9 +55,9 @@ couchTests.erlang_views = function(debug) {
shows: {
simple:
'fun(Doc, {Req}) -> ' +
- ' {Info} = proplists:get_value(<<"info">>, Req, {[]}), ' +
- ' Purged = proplists:get_value(<<"purge_seq">>, Info, -1), ' +
- ' Verb = proplists:get_value(<<"method">>, Req, <<"not_get">>), ' +
+ ' {Info} = couch_util:get_value(<<"info">>, Req, {[]}), ' +
+ ' Purged = couch_util:get_value(<<"purge_seq">>, Info, -1), ' +
+ ' Verb = couch_util:get_value(<<"method">>, Req, <<"not_get">>), ' +
' R = list_to_binary(io_lib:format("~b - ~s", [Purged, Verb])), ' +
' {[{<<"code">>, 200}, {<<"headers">>, {[]}}, {<<"body">>, R}]} ' +
'end.'
@@ -67,7 +67,7 @@ couchTests.erlang_views = function(debug) {
'fun(Head, {Req}) -> ' +
' Send(<<"head">>), ' +
' Fun = fun({Row}, _) -> ' +
- ' Val = proplists:get_value(<<"value">>, Row, -1), ' +
+ ' Val = couch_util:get_value(<<"value">>, Row, -1), ' +
' Send(list_to_binary(integer_to_list(Val))), ' +
' {ok, nil} ' +
' end, ' +
@@ -117,10 +117,10 @@ couchTests.erlang_views = function(debug) {
T(db.bulkSave(docs).length, 250, "Saved big doc set.");
var mfun = 'fun({Doc}) -> ' +
- 'Words = proplists:get_value(<<"words">>, Doc), ' +
+ 'Words = couch_util:get_value(<<"words">>, Doc), ' +
'lists:foreach(fun({Word}) -> ' +
- 'WordString = proplists:get_value(<<"word">>, Word), ' +
- 'Count = proplists:get_value(<<"count">>, Word), ' +
+ 'WordString = couch_util:get_value(<<"word">>, Word), ' +
+ 'Count = couch_util:get_value(<<"count">>, Word), ' +
'Emit(WordString , Count) ' +
'end, Words) ' +
'end.';
diff --git a/share/www/script/test/etags_views.js b/share/www/script/test/etags_views.js
index a12734f8..7e1537bd 100644
--- a/share/www/script/test/etags_views.js
+++ b/share/www/script/test/etags_views.js
@@ -38,7 +38,7 @@ couchTests.etags_views = function(debug) {
})
}
}
- }
+ };
T(db.save(designDoc).ok);
var xhr;
var docs = makeDocs(0, 10);
diff --git a/share/www/script/test/jsonp.js b/share/www/script/test/jsonp.js
index dfd6a0df..6bec63ab 100644
--- a/share/www/script/test/jsonp.js
+++ b/share/www/script/test/jsonp.js
@@ -32,38 +32,51 @@ couchTests.jsonp = function(debug) {
db.deleteDb();
db.createDb();
if (debug) debugger;
-
+
var doc = {_id:"0",a:0,b:0};
T(db.save(doc).ok);
+
+ // callback param is ignored unless jsonp is configured
+ var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_not_configured");
+ JSON.parse(xhr.responseText);
- // Test unchunked callbacks.
- var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_no_chunk");
- T(xhr.status == 200);
- jsonp_flag = 0;
- eval(xhr.responseText);
- T(jsonp_flag == 1);
- xhr = CouchDB.request("GET", "/test_suite_db/0?callback=foo\"");
- T(xhr.status == 400);
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "allow_jsonp",
+ value: "true"}],
+ function() {
- // Test chunked responses
- var doc = {_id:"1",a:1,b:1};
- T(db.save(doc).ok);
+ // Test unchunked callbacks.
+ var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_no_chunk");
+ T(xhr.status == 200);
+ jsonp_flag = 0;
+ eval(xhr.responseText);
+ T(jsonp_flag == 1);
+ xhr = CouchDB.request("GET", "/test_suite_db/0?callback=foo\"");
+ T(xhr.status == 400);
+
+ // Test chunked responses
+ var doc = {_id:"1",a:1,b:1};
+ T(db.save(doc).ok);
- var designDoc = {
- _id:"_design/test",
- language: "javascript",
- views: {
- all_docs: {map: "function(doc) {if(doc.a) emit(null, doc.a);}"}
+ var designDoc = {
+ _id:"_design/test",
+ language: "javascript",
+ views: {
+ all_docs: {map: "function(doc) {if(doc.a) emit(null, doc.a);}"}
+ }
}
- }
- T(db.save(designDoc).ok);
+ T(db.save(designDoc).ok);
+
+ var url = "/test_suite_db/_design/test/_view/all_docs?callback=jsonp_chunk";
+ xhr = CouchDB.request("GET", url);
+ T(xhr.status == 200);
+ jsonp_flag = 0;
+ eval(xhr.responseText);
+ T(jsonp_flag == 1);
+ xhr = CouchDB.request("GET", url + "\'");
+ T(xhr.status == 400);
+ });
+
- var url = "/test_suite_db/_design/test/_view/all_docs?callback=jsonp_chunk";
- xhr = CouchDB.request("GET", url);
- T(xhr.status == 200);
- jsonp_flag = 0;
- eval(xhr.responseText);
- T(jsonp_flag == 1);
- xhr = CouchDB.request("GET", url + "\'");
- T(xhr.status == 400);
};
diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js
index 54214c2c..44afa899 100644
--- a/share/www/script/test/list_views.js
+++ b/share/www/script/test/list_views.js
@@ -178,7 +178,7 @@ couchTests.list_views = function(debug) {
'fun(Head, {Req}) -> ' +
' Send(<<"[">>), ' +
' Fun = fun({Row}, Sep) -> ' +
- ' Val = proplists:get_value(<<"key">>, Row, 23), ' +
+ ' Val = couch_util:get_value(<<"key">>, Row, 23), ' +
' Send(list_to_binary(Sep ++ integer_to_list(Val))), ' +
' {ok, ","} ' +
' end, ' +
@@ -220,10 +220,13 @@ couchTests.list_views = function(debug) {
T(etag1 != etag2, "POST to map _list generates key-depdendent ETags");
// test the richness of the arguments
- xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView?update_seq=true");
T(xhr.status == 200, "standard get should be 200");
var resp = JSON.parse(xhr.responseText);
- TEquals(resp.head, {total_rows:10, offset:0});
+ TEquals(10, resp.head.total_rows);
+ TEquals(0, resp.head.offset);
+ TEquals(11, resp.head.update_seq);
+
T(resp.rows.length == 10);
TEquals(resp.rows[0], {"id": "0","key": 0,"value": "0"});
@@ -343,13 +346,21 @@ couchTests.list_views = function(debug) {
// T(xhr.getResponseHeader("Content-Type") == "text/plain");
T(xhr.responseText.match(/^head 0 1 2 tail$/) && "basic stop");
- xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView", {
+ headers : {
+ "Accept" : "text/html"
+ }
+ });
T(xhr.responseText.match(/^head 0 1 2 tail$/) && "stop 2");
// aborting iteration with reduce
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true");
T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop");
- xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true", {
+ headers : {
+ "Accept" : "text/html"
+ }
+ });
T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop 2");
// with accept headers for HTML
@@ -383,7 +394,7 @@ couchTests.list_views = function(debug) {
T(/LastKey: 0/.test(xhr.responseText));
// Test we do multi-key requests on lists and views in separate docs.
- var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView"
+ var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView";
xhr = CouchDB.request("POST", url, {
body: '{"keys":[-2,-4,-5,-7]}'
});
diff --git a/share/www/script/test/method_override.js b/share/www/script/test/method_override.js
new file mode 100644
index 00000000..0bb4c61f
--- /dev/null
+++ b/share/www/script/test/method_override.js
@@ -0,0 +1,40 @@
+// 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.
+
+// Allow broken HTTP clients to fake a full method vocabulary with an X-HTTP-METHOD-OVERRIDE header
+couchTests.method_override = function(debug) {
+ var result = JSON.parse(CouchDB.request("GET", "/").responseText);
+ T(result.couchdb == "Welcome");
+
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+
+ db.createDb();
+
+ var doc = {bob : "connie"};
+ xhr = CouchDB.request("POST", "/test_suite_db/fnord", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
+ T(xhr.status == 201);
+
+ doc = db.open("fnord");
+ T(doc.bob == "connie");
+
+ xhr = CouchDB.request("POST", "/test_suite_db/fnord?rev=" + doc._rev, {headers:{"X-HTTP-Method-Override" : "DELETE"}});
+ T(xhr.status == 200);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/fnord2", {body: JSON.stringify(doc), headers:{"X-HTTP-Method-Override" : "PUT"}});
+ // Method Override is ignored when original Method isn't POST
+ T(xhr.status == 404);
+
+ doc = db.open("fnord");
+ T(doc == null);
+
+};
diff --git a/share/www/script/test/oauth.js b/share/www/script/test/oauth.js
index d55d13e8..b439b4db 100644
--- a/share/www/script/test/oauth.js
+++ b/share/www/script/test/oauth.js
@@ -94,8 +94,18 @@ couchTests.oauth = function(debug) {
headers: {"X-Couch-Persist": "false"},
body: JSON.stringify(testadminPassword)
});
-
- CouchDB.request("GET", "/_sleep?time=50");
+ var i = 0;
+ waitForSuccess(function() {
+ //loop until the couch server has processed the password
+ i += 1;
+ var xhr = CouchDB.request("GET", "http://" + host + "/_config/admins/testadmin?foo="+i,{
+ headers: {
+ "Authorization": adminBasicAuthHeaderValue()
+ }});
+ if (xhr.responseText.indexOf("\"-hashed-") != 0) {
+ throw("still waiting");
+ }
+ }, "wait-for-admin");
CouchDB.newUuids(2); // so we have one to make the salt
@@ -116,7 +126,7 @@ couchTests.oauth = function(debug) {
// Create a user
var jasonUserDoc = CouchDB.prepareUserDoc({
- username: "jason",
+ name: "jason",
roles: ["test"]
}, "testpassword");
T(usersDb.save(jasonUserDoc).ok);
diff --git a/share/www/script/test/proxyauth.js b/share/www/script/test/proxyauth.js
new file mode 100644
index 00000000..91e2f221
--- /dev/null
+++ b/share/www/script/test/proxyauth.js
@@ -0,0 +1,130 @@
+// 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.proxyauth = function(debug) {
+ // this test proxy authentification handler
+
+ var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+
+ if (debug) debugger;
+
+ // Simple secret key generator
+ function generateSecret(length) {
+ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var secret = '';
+ for (var i=0; i<length; i++) {
+ secret += tab.charAt(Math.floor(Math.random() * 64));
+ }
+ return secret;
+ }
+
+ var secret = generateSecret(64);
+
+ function TestFun() {
+ usersDb.deleteDb();
+ usersDb.createDb();
+ db.deleteDb();
+ db.createDb();
+
+ var benoitcUserDoc = CouchDB.prepareUserDoc({
+ name: "benoitc@apache.org"
+ }, "test");
+ T(usersDb.save(benoitcUserDoc).ok);
+
+ T(CouchDB.session().userCtx.name == null);
+
+ // test that you can use basic auth aginst the users db
+ var s = CouchDB.session({
+ headers : {
+ "Authorization" : "Basic YmVub2l0Y0BhcGFjaGUub3JnOnRlc3Q="
+ }
+ });
+ T(s.userCtx.name == "benoitc@apache.org");
+ T(s.info.authenticated == "default");
+
+ CouchDB.logout();
+
+ var headers = {
+ "X-Auth-CouchDB-UserName": "benoitc@apache.org",
+ "X-Auth-CouchDB-Roles": "test",
+ "X-Auth-CouchDB-Token": hex_hmac_sha1(secret, "benoitc@apache.org")
+ };
+
+ var designDoc = {
+ _id:"_design/test",
+ language: "javascript",
+
+ shows: {
+ "welcome": stringFun(function(doc,req) {
+ return "Welcome " + req.userCtx["name"];
+ }),
+ "role": stringFun(function(doc, req) {
+ return req.userCtx['roles'][0];
+ })
+ }
+ };
+
+ db.save(designDoc);
+
+ var req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
+ {headers: headers});
+ T(req.responseText == "Welcome benoitc@apache.org");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
+ {headers: headers});
+ T(req.responseText == "test");
+
+ var xhr = CouchDB.request("PUT", "/_config/couch_httpd_auth/proxy_use_secret",{
+ body : JSON.stringify("true"),
+ headers: {"X-Couch-Persist": "false"}
+ });
+ T(xhr.status == 200);
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/welcome",
+ {headers: headers});
+ T(req.responseText == "Welcome benoitc@apache.org");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_show/role",
+ {headers: headers});
+ T(req.responseText == "test");
+
+ }
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "authentication_handlers",
+ value:"{couch_httpd_auth, proxy_authentification_handler}, {couch_httpd_auth, default_authentication_handler}"},
+ {section: "couch_httpd_auth",
+ key: "authentication_db",
+ value: "test_suite_users"},
+ {section: "couch_httpd_auth",
+ key: "secret",
+ value: secret},
+ {section: "couch_httpd_auth",
+ key: "x_auth_username",
+ value: "X-Auth-CouchDB-UserName"},
+ {section: "couch_httpd_auth",
+ key: "x_auth_roles",
+ value: "X-Auth-CouchDB-Roles"},
+ {section: "couch_httpd_auth",
+ key: "x_auth_token",
+ value: "X-Auth-CouchDB-Token"},
+ {section: "couch_httpd_auth",
+ key: "proxy_use_secret",
+ value: "false"}],
+ TestFun
+ );
+
+} \ No newline at end of file
diff --git a/share/www/script/test/purge.js b/share/www/script/test/purge.js
index ca68b363..af72ea4f 100644
--- a/share/www/script/test/purge.js
+++ b/share/www/script/test/purge.js
@@ -30,7 +30,7 @@ couchTests.purge = function(debug) {
all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}
}
- }
+ };
T(db.save(designDoc).ok);
@@ -50,17 +50,19 @@ couchTests.purge = function(debug) {
// purge the documents
var xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
- body: JSON.stringify({"1":[doc1._rev], "2":[doc2._rev]}),
+ body: JSON.stringify({"1":[doc1._rev], "2":[doc2._rev]})
});
T(xhr.status == 200);
+ var result = JSON.parse(xhr.responseText);
var newInfo = db.info();
+
// purging increments the update sequence
T(info.update_seq+1 == newInfo.update_seq);
// and it increments the purge_seq
T(info.purge_seq+1 == newInfo.purge_seq);
+ T(result.purge_seq == newInfo.purge_seq);
- var result = JSON.parse(xhr.responseText);
T(result.purged["1"][0] == doc1._rev);
T(result.purged["2"][0] == doc2._rev);
@@ -81,16 +83,18 @@ couchTests.purge = function(debug) {
var doc4 = db.open("4");
xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
- body: JSON.stringify({"3":[doc3._rev]}),
+ body: JSON.stringify({"3":[doc3._rev]})
});
T(xhr.status == 200);
xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
- body: JSON.stringify({"4":[doc4._rev]}),
+ body: JSON.stringify({"4":[doc4._rev]})
});
T(xhr.status == 200);
+ result = JSON.parse(xhr.responseText);
+ T(result.purge_seq == db.info().purge_seq);
var rows = db.view("test/all_docs_twice").rows;
for (var i = 4; i < numDocs; i++) {
diff --git a/share/www/script/test/reader_acl.js b/share/www/script/test/reader_acl.js
new file mode 100644
index 00000000..cc249ea4
--- /dev/null
+++ b/share/www/script/test/reader_acl.js
@@ -0,0 +1,198 @@
+// 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.reader_acl = function(debug) {
+ // this tests read access control
+
+ var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
+ var secretDb = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ function testFun() {
+ try {
+ usersDb.deleteDb();
+ usersDb.createDb();
+ secretDb.deleteDb();
+ secretDb.createDb();
+
+ // create a user with top-secret-clearance
+ var jchrisUserDoc = CouchDB.prepareUserDoc({
+ name: "jchris@apache.org",
+ roles : ["top-secret"]
+ }, "funnybone");
+ T(usersDb.save(jchrisUserDoc).ok);
+ usersDb.ensureFullCommit();
+
+ T(CouchDB.session().userCtx.name == null);
+
+ // set secret db to be read controlled
+ T(secretDb.save({_id:"baz",foo:"bar"}).ok);
+ T(secretDb.open("baz").foo == "bar");
+
+ T(secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club"],
+ names : ["joe","barb"]
+ }
+ }).ok);
+ } finally {
+ CouchDB.logout();
+ }
+ }
+
+ // split into 2 funs so we can test restart behavior
+ function testFun2() {
+ try {
+ // can't read it as jchris b/c he's missing the needed role
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ T(CouchDB.session().userCtx.name == "jchris@apache.org");
+
+ try {
+ secretDb.open("baz");
+ T(false && "can't open a doc from a secret db") ;
+ } catch(e) {
+ T(true)
+ }
+
+ CouchDB.logout();
+
+ // make anyone with the top-secret role an admin
+ // db admins are automatically readers
+ T(secretDb.setSecObj({
+ "admins" : {
+ roles : ["top-secret"],
+ names : []
+ },
+ "readers" : {
+ roles : ["super-secret-club"],
+ names : ["joe","barb"]
+ }
+ }).ok);
+
+
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+
+ // db admin can read
+ T(secretDb.open("baz").foo == "bar");
+
+ // and run temp views
+ TEquals(secretDb.query(function(doc) {
+ emit(null, null)
+ }).total_rows, 1);
+
+ CouchDB.logout();
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
+
+ // admin now adds the top-secret role to the db's readers
+ // and removes db-admins
+ T(secretDb.setSecObj({
+ "admins" : {
+ roles : [],
+ names : []
+ },
+ "readers" : {
+ roles : ["super-secret-club", "top-secret"],
+ names : ["joe","barb"]
+ }
+ }).ok);
+
+ // server _admin can always read
+ T(secretDb.open("baz").foo == "bar");
+
+ // and run temp views
+ TEquals(secretDb.query(function(doc) {
+ emit(null, null)
+ }).total_rows, 1);
+
+ T(secretDb.save({
+ "_id" : "_design/foo",
+ views : {
+ bar : {
+ map : "function(doc){emit(null, null)}"
+ }
+ }
+ }).ok)
+
+ // now top-secret users can read too
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
+ T(secretDb.open("baz").foo == "bar");
+ // readers can query stored views
+ T(secretDb.view("foo/bar").total_rows == 1);
+
+ // readers can't do temp views
+ try {
+ var results = secretDb.query(function(doc) {
+ emit(null, null);
+ });
+ T(false && "temp view should be admin only");
+ } catch (e) {
+ T(true && "temp view is admin only");
+ }
+
+
+ CouchDB.logout();
+
+ // can't set non string reader names or roles
+ try {
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : ["joe","barb"]
+ }
+ })
+ T(false && "only string roles");
+ } catch (e) {}
+
+ try {
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : ["joe",22]
+ }
+ });
+ T(false && "only string names");
+ } catch (e) {}
+
+ try {
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : "joe"
+ }
+ });
+ T(false && "only lists of names");
+ } catch (e) {}
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "authentication_handlers",
+ value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
+ {section: "couch_httpd_auth",
+ key: "authentication_db", value: "test_suite_users"}],
+ testFun
+ );
+
+ // security changes will always commit synchronously
+ restartServer();
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "authentication_handlers",
+ value: "{couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"},
+ {section: "couch_httpd_auth",
+ key: "authentication_db", value: "test_suite_users"}],
+ testFun2
+ );
+}
diff --git a/share/www/script/test/recreate_doc.js b/share/www/script/test/recreate_doc.js
index a6a64ac0..05843558 100644
--- a/share/www/script/test/recreate_doc.js
+++ b/share/www/script/test/recreate_doc.js
@@ -51,7 +51,7 @@ couchTests.recreate_doc = function(debug) {
data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
- }
+ };
try {
// same as before, but with binary
db.save(binAttDoc);
diff --git a/share/www/script/test/reduce.js b/share/www/script/test/reduce.js
index 9c80fa7f..979a0292 100644
--- a/share/www/script/test/reduce.js
+++ b/share/www/script/test/reduce.js
@@ -15,14 +15,15 @@ couchTests.reduce = function(debug) {
db.deleteDb();
db.createDb();
if (debug) debugger;
- var numDocs = 500
+ var numDocs = 500;
var docs = makeDocs(1,numDocs + 1);
db.bulkSave(docs);
var summate = function(N) {return (N+1)*N/2;};
var map = function (doc) {
emit(doc.integer, doc.integer);
- emit(doc.integer, doc.integer)};
+ emit(doc.integer, doc.integer);
+ };
var reduce = function (keys, values) { return sum(values); };
var result = db.query(map, reduce);
T(result.rows[0].value == 2*summate(numDocs));
@@ -69,7 +70,7 @@ couchTests.reduce = function(debug) {
T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
}
- map = function (doc) {emit(doc.keys, 1)};
+ map = function (doc) { emit(doc.keys, 1); };
reduce = function (keys, values) { return sum(values); };
var results = db.query(map, reduce, {group:true});
@@ -107,7 +108,7 @@ couchTests.reduce = function(debug) {
db.createDb();
- var map = function (doc) {emit(doc.val, doc.val)};
+ var map = function (doc) { emit(doc.val, doc.val); };
var reduceCombine = function (keys, values, rereduce) {
// This computes the standard deviation of the mapped results
var stdDeviation=0.0;
diff --git a/share/www/script/test/reduce_builtin.js b/share/www/script/test/reduce_builtin.js
index 7938a0cf..c9d41fa4 100644
--- a/share/www/script/test/reduce_builtin.js
+++ b/share/www/script/test/reduce_builtin.js
@@ -16,22 +16,37 @@ couchTests.reduce_builtin = function(debug) {
db.createDb();
if (debug) debugger;
- var numDocs = 500
+ var numDocs = 500;
var docs = makeDocs(1,numDocs + 1);
db.bulkSave(docs);
var summate = function(N) {return (N+1)*N/2;};
+ var sumsqr = function(N) {
+ var acc = 0;
+ for (var i=1; i<=N; ++i) {
+ acc += i*i;
+ }
+ return acc;
+ };
+
// this is the same test as the reduce.js test
// only we'll let CouchDB run reduce in Erlang
var map = function (doc) {
emit(doc.integer, doc.integer);
- emit(doc.integer, doc.integer)};
+ emit(doc.integer, doc.integer);
+ };
var result = db.query(map, "_sum");
T(result.rows[0].value == 2*summate(numDocs));
result = db.query(map, "_count");
T(result.rows[0].value == 1000);
+ result = db.query(map, "_stats");
+ T(result.rows[0].value.sum == 2*summate(numDocs));
+ T(result.rows[0].value.count == 1000);
+ T(result.rows[0].value.min == 1);
+ T(result.rows[0].value.max == 500);
+ T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
result = db.query(map, "_sum", {startkey: 4, endkey: 4});
T(result.rows[0].value == 8);
@@ -58,6 +73,26 @@ couchTests.reduce_builtin = function(debug) {
T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
}
+ // test for trailing characters after builtin functions, desired behaviour
+ // is to disregard any trailing characters
+ // I think the behavior should be a prefix test, so that even "_statsorama"
+ // or "_stats\nare\awesome" should work just as "_stats" does. - JChris
+
+ var trailing = ["\u000a", "orama", "\nare\nawesome", " ", " \n "];
+
+ for(var i=0; i < trailing.length; i++) {
+ result = db.query(map, "_sum" + trailing[i]);
+ T(result.rows[0].value == 2*summate(numDocs));
+ result = db.query(map, "_count" + trailing[i]);
+ T(result.rows[0].value == 1000);
+ result = db.query(map, "_stats" + trailing[i]);
+ T(result.rows[0].value.sum == 2*summate(numDocs));
+ T(result.rows[0].value.count == 1000);
+ T(result.rows[0].value.min == 1);
+ T(result.rows[0].value.max == 500);
+ T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
+ }
+
db.deleteDb();
db.createDb();
@@ -81,7 +116,7 @@ couchTests.reduce_builtin = function(debug) {
T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11));
}
- map = function (doc) {emit(doc.keys, 1)};
+ map = function (doc) { emit(doc.keys, 1); };
// with emitted values being 1, count should be the same as sum
var builtins = ["_sum", "_count"];
diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js
index 78678937..7cc1f823 100644
--- a/share/www/script/test/replication.js
+++ b/share/www/script/test/replication.js
@@ -22,14 +22,14 @@ couchTests.replication = function(debug) {
target:"test_suite_db_b"},
{source:"http://" + host + "/test_suite_db_a",
target:"http://" + host + "/test_suite_db_b"}
- ]
+ ];
var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"});
var numDocs = 10;
var xhr;
for (var testPair = 0; testPair < dbPairs.length; testPair++) {
- var A = dbPairs[testPair].source
- var B = dbPairs[testPair].target
+ var A = dbPairs[testPair].source;
+ var B = dbPairs[testPair].target;
dbA.deleteDb();
dbA.createDb();
@@ -41,7 +41,7 @@ couchTests.replication = function(debug) {
test_template: new function () {
this.init = function(dbA, dbB) {
// before anything has happened
- }
+ };
this.afterAB1 = function(dbA, dbB) {
// called after replicating src=A tgt=B first time.
};
@@ -165,20 +165,20 @@ couchTests.replication = function(debug) {
this.afterAB1 = function(dbA, dbB) {
var xhr = CouchDB.request("GET",
"/test_suite_db_a/bin_doc/foo%2Bbar.txt");
- T(xhr.responseText == "This is a base64 encoded text")
+ T(xhr.responseText == "This is a base64 encoded text");
xhr = CouchDB.request("GET",
"/test_suite_db_b/bin_doc/foo%2Bbar.txt");
- T(xhr.responseText == "This is a base64 encoded text")
+ T(xhr.responseText == "This is a base64 encoded text");
// and the design-doc
xhr = CouchDB.request("GET",
"/test_suite_db_a/_design/with_bin/foo%2Bbar.txt");
- T(xhr.responseText == "This is a base64 encoded text")
+ T(xhr.responseText == "This is a base64 encoded text");
xhr = CouchDB.request("GET",
"/test_suite_db_b/_design/with_bin/foo%2Bbar.txt");
- T(xhr.responseText == "This is a base64 encoded text")
+ T(xhr.responseText == "This is a base64 encoded text");
};
},
@@ -209,8 +209,8 @@ couchTests.replication = function(debug) {
var docB = dbB.open("foo", {conflicts: true, deleted_conflicts: true});
// We should have no conflicts this time
- T(docA._conflicts === undefined)
- T(docB._conflicts === undefined);
+ T(typeof docA._conflicts === "undefined");
+ T(typeof docB._conflicts === "undefined");
// They show up as deleted conflicts instead
T(docA._deleted_conflicts[0] == docB._deleted_conflicts[0]);
@@ -229,7 +229,7 @@ couchTests.replication = function(debug) {
var seqA = result.source_last_seq;
T(0 == result.history[0].start_last_seq);
- T(result.history[1] === undefined)
+ T(typeof result.history[1] === "undefined");
for(test in repTests) {
if(repTests[test].afterAB1) repTests[test].afterAB1(dbA, dbB);
@@ -239,7 +239,7 @@ couchTests.replication = function(debug) {
var seqB = result.source_last_seq;
T(0 == result.history[0].start_last_seq);
- T(result.history[1] === undefined)
+ T(typeof result.history[1] === "undefined");
for(test in repTests) {
if(repTests[test].afterBA1) repTests[test].afterBA1(dbA, dbB);
@@ -252,7 +252,7 @@ couchTests.replication = function(debug) {
T(seqA < result2.source_last_seq);
T(seqA == result2.history[0].start_last_seq);
- T(result2.history[1].end_last_seq == seqA)
+ T(result2.history[1].end_last_seq == seqA);
seqA = result2.source_last_seq;
@@ -260,11 +260,11 @@ couchTests.replication = function(debug) {
if(repTests[test].afterAB2) repTests[test].afterAB2(dbA, dbB);
}
- result = CouchDB.replicate(B, A)
+ 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)
+ T(result.history[1].end_last_seq == seqB);
seqB = result.source_last_seq;
@@ -301,4 +301,386 @@ couchTests.replication = function(debug) {
});
TEquals("test_suite_db_b", dbB.info().db_name,
"Target database should exist");
+
+ // continuous
+ var continuousResult = CouchDB.replicate(dbA.name, "test_suite_db_b", {
+ body: {"continuous": true}
+ });
+ T(continuousResult.ok);
+ T(continuousResult._local_id);
+
+ var cancelResult = CouchDB.replicate(dbA.name, "test_suite_db_b", {
+ body: {"cancel": true}
+ });
+ T(cancelResult.ok);
+ T(continuousResult._local_id == cancelResult._local_id);
+
+ try {
+ var cancelResult2 = CouchDB.replicate(dbA.name, "test_suite_db_b", {
+ body: {"cancel": true}
+ });
+ } catch (e) {
+ T(e.error == "not_found");
+ }
+ // test replication object option doc_ids
+
+ var dbA = new CouchDB("test_suite_rep_docs_db_a", {"X-Couch-Full-Commit":"false"});
+ var dbB = new CouchDB("test_suite_rep_docs_db_b", {"X-Couch-Full-Commit":"false"});
+
+ dbA.deleteDb();
+ dbA.createDb();
+
+ var all_docs = [
+ {
+ _id: "foo1",
+ value: "a"
+ },
+ {
+ _id: "foo2",
+ value: "b"
+ },
+ {
+ _id: "foo3",
+ value: "c"
+ },
+ {
+ _id: "slashed/foo",
+ value: "s"
+ },
+ {
+ _id: "_design/foobar",
+ language: "javascript",
+ value: "I am a design doc",
+ filters: {
+ idfilter: (function(doc, req) {
+ return doc.value == Number(req.filter_value);
+ }).toString()
+ },
+ views: {
+ countview: (function(doc) {
+ emit(doc.value, 1);
+ }).toString()
+ }
+ }
+ ];
+
+ for (var i = 0; i < all_docs.length; i++) {
+ T(dbA.save(all_docs[i]).ok);
+ }
+
+ var dbPairs = [
+ {source:"test_suite_rep_docs_db_a",
+ target:"test_suite_rep_docs_db_b"},
+ {source:"test_suite_rep_docs_db_a",
+ target:"http://" + host + "/test_suite_rep_docs_db_b"},
+ {source:"http://" + host + "/test_suite_rep_docs_db_a",
+ target:"test_suite_rep_docs_db_b"},
+ {source:"http://" + host + "/test_suite_rep_docs_db_a",
+ target:"http://" + host + "/test_suite_rep_docs_db_b"}
+ ];
+
+ var target_doc_ids = [
+ ["foo1", "foo3", "foo666"],
+ ["foo1", "foo666"],
+ ["foo666", "foo2"],
+ ["foo2", "foo9999", "foo1"],
+ ["foo3", "slashed/foo"],
+ ["foo3", "slashed%2Ffoo"],
+ ["foo1", "_design/foobar"],
+ ["foo1", "foo1001", "_design%2Ffoobar"]
+ ];
+
+ for (var i = 0; i < dbPairs.length; i++) {
+ var src_db = dbPairs[i].source;
+ var tgt_db = dbPairs[i].target;
+
+ for (var j = 0; j < target_doc_ids.length; j++) {
+ var doc_ids = target_doc_ids[j];
+ var valid_doc_ids = [];
+ var invalid_doc_ids = [];
+
+ $.each(doc_ids, function(index, id) {
+ var found = false;
+
+ for (var k = 0; k < all_docs.length; k++) {
+ var doc = all_docs[k];
+
+ if (id === doc._id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ valid_doc_ids.push(id);
+ } else {
+ invalid_doc_ids.push(id);
+ }
+ });
+
+ dbB.deleteDb();
+ dbB.createDb();
+
+ var repResult = CouchDB.replicate(src_db, tgt_db, {
+ body: {"doc_ids": doc_ids}
+ });
+
+ T(repResult.ok);
+ T(repResult.docs_written === valid_doc_ids.length);
+ T(repResult.docs_read === valid_doc_ids.length);
+ T(repResult.doc_write_failures === 0);
+
+ for (var k = 0; k < all_docs.length; k++) {
+ var doc = all_docs[k];
+ var tgt_doc = dbB.open(doc._id);
+
+ if ($.inArray(doc._id, doc_ids) >= 0) {
+ T(tgt_doc !== null);
+ T(tgt_doc.value === doc.value);
+ } else {
+ T(tgt_doc === null);
+ }
+ }
+
+ for (var k = 0; k < invalid_doc_ids.length; k++) {
+ var tgt_doc = dbB.open(invalid_doc_ids[k]);
+
+ T(tgt_doc === null);
+ }
+ }
+ }
+
+ // test filtered replication
+
+ var sourceDb = new CouchDB(
+ "test_suite_filtered_rep_db_a", {"X-Couch-Full-Commit":"false"}
+ );
+
+ sourceDb.deleteDb();
+ sourceDb.createDb();
+
+ T(sourceDb.save({_id:"foo1",value:1}).ok);
+ T(sourceDb.save({_id:"foo2",value:2}).ok);
+ T(sourceDb.save({_id:"foo3",value:3}).ok);
+ T(sourceDb.save({_id:"foo4",value:4}).ok);
+ T(sourceDb.save({
+ "_id": "_design/mydesign",
+ "language" : "javascript",
+ "filters" : {
+ "myfilter" : (function(doc, req) {
+ if (doc.value < Number(req.query.maxvalue)) {
+ return true;
+ } else {
+ return false;
+ }
+ }).toString()
+ }
+ }).ok);
+
+ var dbPairs = [
+ {source:"test_suite_filtered_rep_db_a",
+ target:"test_suite_filtered_rep_db_b"},
+ {source:"test_suite_filtered_rep_db_a",
+ target:"http://" + host + "/test_suite_filtered_rep_db_b"},
+ {source:"http://" + host + "/test_suite_filtered_rep_db_a",
+ target:"test_suite_filtered_rep_db_b"},
+ {source:"http://" + host + "/test_suite_filtered_rep_db_a",
+ target:"http://" + host + "/test_suite_filtered_rep_db_b"}
+ ];
+
+ for (var i = 0; i < dbPairs.length; i++) {
+ var targetDb = new CouchDB("test_suite_filtered_rep_db_b");
+ targetDb.deleteDb();
+ targetDb.createDb();
+
+ var dbA = dbPairs[i].source;
+ var dbB = dbPairs[i].target;
+
+ var repResult = CouchDB.replicate(dbA, dbB, {
+ body: {
+ "filter" : "mydesign/myfilter",
+ "query_params" : {
+ "maxvalue": "3"
+ }
+ }
+ });
+
+ T(repResult.ok);
+ T($.isArray(repResult.history));
+ T(repResult.history.length === 1);
+ T(repResult.history[0].docs_written === 2);
+ T(repResult.history[0].docs_read === 2);
+ T(repResult.history[0].doc_write_failures === 0);
+
+ var docFoo1 = targetDb.open("foo1");
+ T(docFoo1 !== null);
+ T(docFoo1.value === 1);
+
+ var docFoo2 = targetDb.open("foo2");
+ T(docFoo2 !== null);
+ T(docFoo2.value === 2);
+
+ var docFoo3 = targetDb.open("foo3");
+ T(docFoo3 === null);
+
+ var docFoo4 = targetDb.open("foo4");
+ T(docFoo4 === null);
+ }
+
+ // test for COUCHDB-868 - design docs' attachments not getting replicated
+ // when doing a pull replication with HTTP basic auth
+ dbA = new CouchDB("test_suite_db_a");
+ dbB = new CouchDB("test_suite_db_b");
+ var usersDb = new CouchDB("test_suite_auth");
+ var lorem = CouchDB.request(
+ "GET", "/_utils/script/test/lorem.txt").responseText;
+ var lorem_b64 = CouchDB.request(
+ "GET", "/_utils/script/test/lorem_b64.txt").responseText;
+
+ usersDb.deleteDb();
+ usersDb.createDb();
+ dbA.deleteDb();
+ dbA.createDb();
+ dbB.deleteDb();
+ dbB.createDb();
+
+ var atts_ddoc = {
+ _id: "_design/i_have_atts",
+ language: "javascript"
+ };
+ T(dbA.save(atts_ddoc).ok);
+
+ var rev = atts_ddoc._rev;
+ var att_1_name = "lorem.txt";
+ var att_2_name = "lorem.dat";
+ var xhr = CouchDB.request(
+ "PUT", "/" + dbA.name + "/" + atts_ddoc._id + "/" + att_1_name + "?rev=" + rev, {
+ headers: {"Content-Type": "text/plain;charset=utf-8"},
+ body: lorem
+ });
+ rev = JSON.parse(xhr.responseText).rev;
+ T(xhr.status === 201);
+ xhr = CouchDB.request(
+ "PUT", "/" + dbA.name + "/" + atts_ddoc._id + "/" + att_2_name + "?rev=" + rev, {
+ headers: {"Content-Type": "application/data"},
+ body: lorem_b64
+ });
+ T(xhr.status === 201);
+
+ var fdmananaUserDoc = CouchDB.prepareUserDoc({
+ name: "fdmanana",
+ roles: ["reader"]
+ }, "qwerty");
+ T(usersDb.save(fdmananaUserDoc).ok);
+
+ T(dbA.setSecObj({
+ admins: {
+ names: [],
+ roles: ["admin"]
+ },
+ readers: {
+ names: [],
+ roles: ["reader"]
+ }
+ }).ok);
+ T(dbB.setSecObj({
+ admins: {
+ names: ["fdmanana"],
+ roles: []
+ }
+ }).ok);
+
+ var server_config = [
+ {
+ section: "couch_httpd_auth",
+ key: "authentication_db",
+ value: usersDb.name
+ },
+ // to prevent admin party mode
+ {
+ section: "admins",
+ key: "joe",
+ value: "erlang"
+ }
+ ];
+
+ var test_fun = function() {
+ T(CouchDB.login("fdmanana", "qwerty").ok);
+ T(CouchDB.session().userCtx.name === "fdmanana");
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") === -1);
+
+ var repResult = CouchDB.replicate(
+ "http://fdmanana:qwerty@" + host + "/" + dbA.name,
+ dbB.name
+ );
+ T(repResult.ok === true);
+ T(repResult.history instanceof Array);
+ T(repResult.history.length === 1);
+ T(repResult.history[0].docs_written === 1);
+ T(repResult.history[0].docs_read === 1);
+ T(repResult.history[0].doc_write_failures === 0);
+
+ var atts_ddoc_copy = dbB.open(atts_ddoc._id);
+ T(atts_ddoc_copy !== null);
+ T(typeof atts_ddoc_copy._attachments === "object");
+ T(atts_ddoc_copy._attachments !== null);
+ T(att_1_name in atts_ddoc_copy._attachments);
+ T(att_2_name in atts_ddoc_copy._attachments);
+
+ var xhr = CouchDB.request("GET", "/" + dbB.name + "/" + atts_ddoc._id + "/" + att_1_name);
+ T(xhr.status === 200);
+ T(xhr.responseText === lorem);
+
+ xhr = CouchDB.request("GET", "/" + dbB.name + "/" + atts_ddoc._id + "/" + att_2_name);
+ T(xhr.status === 200);
+ T(xhr.responseText === lorem_b64);
+
+ CouchDB.logout();
+ T(CouchDB.login("joe", "erlang").ok);
+ T(dbA.setSecObj({
+ admins: {
+ names: [],
+ roles: ["bar"]
+ },
+ readers: {
+ names: [],
+ roles: ["foo"]
+ }
+ }).ok);
+ T(dbB.deleteDb().ok === true);
+ T(dbB.createDb().ok === true);
+ T(dbB.setSecObj({
+ admins: {
+ names: ["fdmanana"],
+ roles: []
+ }
+ }).ok);
+ CouchDB.logout();
+
+ T(CouchDB.login("fdmanana", "qwerty").ok);
+ T(CouchDB.session().userCtx.name === "fdmanana");
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") === -1);
+ try {
+ repResult = CouchDB.replicate(
+ "http://fdmanana:qwerty@" + host + "/" + dbA.name,
+ dbB.name
+ );
+ T(false, "replication should have failed");
+ } catch(x) {
+ T(x.error === "unauthorized");
+ }
+
+ atts_ddoc_copy = dbB.open(atts_ddoc._id);
+ T(atts_ddoc_copy === null);
+
+ CouchDB.logout();
+ T(CouchDB.login("joe", "erlang").ok);
+ };
+
+ run_on_modified_server(server_config, test_fun);
+
+ // cleanup
+ dbA.deleteDb();
+ dbB.deleteDb();
+ usersDb.deleteDb();
};
diff --git a/share/www/script/test/rewrite.js b/share/www/script/test/rewrite.js
new file mode 100644
index 00000000..ff2d3822
--- /dev/null
+++ b/share/www/script/test/rewrite.js
@@ -0,0 +1,380 @@
+// 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.rewrite = function(debug) {
+ // this test _rewrite handler
+
+
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+
+
+ if (debug) debugger;
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "authentication_handlers",
+ value: "{couch_httpd_auth, special_test_authentication_handler}"},
+ {section:"httpd",
+ key: "WWW-Authenticate",
+ value: "X-Couch-Test-Auth"}],
+
+ function(){
+ var designDoc = {
+ _id:"_design/test",
+ language: "javascript",
+ _attachments:{
+ "foo.txt": {
+ content_type:"text/plain",
+ data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+ }
+ },
+ rewrites: [
+ {
+ "from": "foo",
+ "to": "foo.txt"
+ },
+ {
+ "from": "foo2",
+ "to": "foo.txt",
+ "method": "GET"
+ },
+ {
+ "from": "hello/:id",
+ "to": "_update/hello/:id",
+ "method": "PUT"
+ },
+ {
+ "from": "/welcome",
+ "to": "_show/welcome"
+ },
+ {
+ "from": "/welcome/:name",
+ "to": "_show/welcome",
+ "query": {
+ "name": ":name"
+ }
+ },
+ {
+ "from": "/welcome2",
+ "to": "_show/welcome",
+ "query": {
+ "name": "user"
+ }
+ },
+ {
+ "from": "/welcome3/:name",
+ "to": "_update/welcome2/:name",
+ "method": "PUT"
+ },
+ {
+ "from": "/welcome3/:name",
+ "to": "_show/welcome2/:name",
+ "method": "GET"
+ },
+ {
+ "from": "simpleForm/basicView",
+ "to": "_list/simpleForm/basicView",
+ },
+ {
+ "from": "simpleForm/basicViewFixed",
+ "to": "_list/simpleForm/basicView",
+ "query": {
+ "startkey": 3,
+ "endkey": 8
+ }
+ },
+ {
+ "from": "simpleForm/basicViewPath/:start/:end",
+ "to": "_list/simpleForm/basicView",
+ "query": {
+ "startkey": ":start",
+ "endkey": ":end"
+ }
+ },
+ {
+ "from": "simpleForm/complexView",
+ "to": "_list/simpleForm/complexView",
+ "query": {
+ "key": [1, 2]
+ }
+ },
+ {
+ "from": "simpleForm/complexView2",
+ "to": "_list/simpleForm/complexView",
+ "query": {
+ "key": ["test", {}]
+ }
+ },
+ {
+ "from": "simpleForm/complexView3",
+ "to": "_list/simpleForm/complexView",
+ "query": {
+ "key": ["test", ["test", "essai"]]
+ }
+ },
+ {
+ "from": "simpleForm/complexView4",
+ "to": "_list/simpleForm/complexView2",
+ "query": {
+ "key": {"c": 1}
+ }
+ },
+ {
+ "from": "simpleForm/complexView5/:a/:b",
+ "to": "_list/simpleForm/complexView3",
+ "query": {
+ "key": [":a", ":b"]
+ }
+ },
+ {
+ "from": "simpleForm/complexView6",
+ "to": "_list/simpleForm/complexView3",
+ "query": {
+ "key": [":a", ":b"]
+ }
+ }
+ ],
+ lists: {
+ simpleForm: stringFun(function(head, req) {
+ log("simpleForm");
+ send('<ul>');
+ var row, row_number = 0, prevKey, firstKey = null;
+ while (row = getRow()) {
+ row_number += 1;
+ if (!firstKey) firstKey = row.key;
+ prevKey = row.key;
+ send('\n<li>Key: '+row.key
+ +' Value: '+row.value
+ +' LineNo: '+row_number+'</li>');
+ }
+ return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
+ }),
+ },
+ shows: {
+ "welcome": stringFun(function(doc,req) {
+ return "Welcome " + req.query["name"];
+ }),
+ "welcome2": stringFun(function(doc, req) {
+ return "Welcome " + doc.name;
+ }),
+ },
+ updates: {
+ "hello" : stringFun(function(doc, req) {
+ if (!doc) {
+ if (req.id) {
+ return [{
+ _id : req.id
+ }, "New World"]
+ }
+ return [null, "Empty World"];
+ }
+ doc.world = "hello";
+ doc.edited_by = req.userCtx;
+ return [doc, "hello doc"];
+ }),
+ "welcome2": stringFun(function(doc, req) {
+ if (!doc) {
+ if (req.id) {
+ return [{
+ _id: req.id,
+ name: req.id
+ }, "New World"]
+ }
+ return [null, "Empty World"];
+ }
+ return [doc, "hello doc"];
+ })
+ },
+ views : {
+ basicView : {
+ map : stringFun(function(doc) {
+ if (doc.integer) {
+ emit(doc.integer, doc.string);
+ }
+
+ })
+ },
+ complexView: {
+ map: stringFun(function(doc) {
+ if (doc.type == "complex") {
+ emit([doc.a, doc.b], doc.string);
+ }
+ })
+ },
+ complexView2: {
+ map: stringFun(function(doc) {
+ if (doc.type == "complex") {
+ emit(doc.a, doc.string);
+ }
+ })
+ },
+ complexView3: {
+ map: stringFun(function(doc) {
+ if (doc.type == "complex") {
+ emit(doc.b, doc.string);
+ }
+ })
+ }
+ }
+ }
+
+ db.save(designDoc);
+
+ var docs = makeDocs(0, 10);
+ db.bulkSave(docs);
+
+ var docs2 = [
+ {"a": 1, "b": 1, "string": "doc 1", "type": "complex"},
+ {"a": 1, "b": 2, "string": "doc 2", "type": "complex"},
+ {"a": "test", "b": {}, "string": "doc 3", "type": "complex"},
+ {"a": "test", "b": ["test", "essai"], "string": "doc 4", "type": "complex"},
+ {"a": {"c": 1}, "b": "", "string": "doc 5", "type": "complex"}
+ ];
+
+ db.bulkSave(docs2);
+
+ // test simple rewriting
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo");
+ T(req.responseText == "This is a base64 encoded text");
+ T(req.getResponseHeader("Content-Type") == "text/plain");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/foo2");
+ T(req.responseText == "This is a base64 encoded text");
+ T(req.getResponseHeader("Content-Type") == "text/plain");
+
+
+ // test POST
+ // hello update world
+
+ var doc = {"word":"plankton", "name":"Rusty"}
+ var resp = db.save(doc);
+ T(resp.ok);
+ var docid = resp.id;
+
+ xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/hello/"+docid);
+ T(xhr.status == 201);
+ T(xhr.responseText == "hello doc");
+ T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))
+
+ doc = db.open(docid);
+ T(doc.world == "hello");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome?name=user");
+ T(req.responseText == "Welcome user");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome/user");
+ T(req.responseText == "Welcome user");
+
+ req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome2");
+ T(req.responseText == "Welcome user");
+
+ xhr = CouchDB.request("PUT", "/test_suite_db/_design/test/_rewrite/welcome3/test");
+ T(xhr.status == 201);
+ T(xhr.responseText == "New World");
+ T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome3/test");
+ T(xhr.responseText == "Welcome test");
+
+
+ // get with query params
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8");
+ T(xhr.status == 200, "with query params");
+ T(!(/Key: 1/.test(xhr.responseText)));
+ T(/FirstKey: 3/.test(xhr.responseText));
+ T(/LastKey: 8/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed");
+ T(xhr.status == 200, "with query params");
+ T(!(/Key: 1/.test(xhr.responseText)));
+ T(/FirstKey: 3/.test(xhr.responseText));
+ T(/LastKey: 8/.test(xhr.responseText));
+
+ // get with query params
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewFixed?startkey=4");
+ T(xhr.status == 200, "with query params");
+ T(!(/Key: 1/.test(xhr.responseText)));
+ T(/FirstKey: 3/.test(xhr.responseText));
+ T(/LastKey: 8/.test(xhr.responseText));
+
+ // get with query params
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewPath/3/8");
+ T(xhr.status == 200, "with query params");
+ T(!(/Key: 1/.test(xhr.responseText)));
+ T(/FirstKey: 3/.test(xhr.responseText));
+ T(/LastKey: 8/.test(xhr.responseText));
+
+ // get with query params
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView");
+ T(xhr.status == 200, "with query params");
+ T(/FirstKey: [1, 2]/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView2");
+ T(xhr.status == 200, "with query params");
+ T(/Value: doc 3/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView3");
+ T(xhr.status == 200, "with query params");
+ T(/Value: doc 4/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView4");
+ T(xhr.status == 200, "with query params");
+ T(/Value: doc 5/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView5/test/essai");
+ T(xhr.status == 200, "with query params");
+ T(/Value: doc 4/.test(xhr.responseText));
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView6?a=test&b=essai");
+ T(xhr.status == 200, "with query params");
+ T(/Value: doc 4/.test(xhr.responseText));
+
+ // test path relative to server
+ designDoc.rewrites.push({
+ "from": "uuids",
+ "to": "../../../_uuids"
+ });
+ T(db.save(designDoc).ok);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids");
+ T(xhr.status == 500);
+ var result = JSON.parse(xhr.responseText);
+ T(result.error == "insecure_rewrite_rule");
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "secure_rewrites",
+ value: "false"}],
+ function() {
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/uuids?cache=bust");
+ T(xhr.status == 200);
+ var result = JSON.parse(xhr.responseText);
+ T(result.uuids.length == 1);
+ var first = result.uuids[0];
+ });
+ });
+
+ // test invalid rewrites
+ // string
+ var ddoc = {
+ _id: "_design/invalid",
+ rewrites: "[{\"from\":\"foo\",\"to\":\"bar\"}]"
+ }
+ db.save(ddoc);
+ var res = CouchDB.request("GET", "/test_suite_db/_design/invalid/_rewrite/foo");
+ TEquals(400, res.status, "should return 400");
+
+}
diff --git a/share/www/script/test/security_validation.js b/share/www/script/test/security_validation.js
index d07195e1..dd3b202e 100644
--- a/share/www/script/test/security_validation.js
+++ b/share/www/script/test/security_validation.js
@@ -13,7 +13,7 @@
couchTests.security_validation = function(debug) {
// This tests couchdb's security and validation features. This does
// not test authentication, except to use test authentication code made
- // specifically for this testing. It is a WWWW-Authenticate scheme named
+ // specifically for this testing. It is a WWW-Authenticate scheme named
// X-Couch-Test-Auth, and the user names and passwords are hard coded
// on the server-side.
//
@@ -21,7 +21,7 @@ couchTests.security_validation = function(debug) {
// implementation for Firefox and Safari, and probably other browsers are
// broken (Firefox always prompts the user on 401 failures, Safari gives
// odd security errors when using different name/passwords, perhaps due
- // to cross site scripting prevention). These problems essentially make Basic
+ // to cross site scripting prevention). These problems essentially make Basic
// authentication testing in the browser impossible. But while hard to
// test automated in the browser, Basic auth may still useful for real
// world use where these bugs/behaviors don't matter.
@@ -69,7 +69,13 @@ couchTests.security_validation = function(debug) {
var designDoc = {
_id:"_design/test",
language: "javascript",
- validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
+ validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx, secObj) {
+ if (secObj.admin_override) {
+ if (userCtx.roles.indexOf('_admin') != -1) {
+ // user is admin, they can do anything
+ return true;
+ }
+ }
// docs should have an author field.
if (!newDoc._deleted && !newDoc.author) {
throw {forbidden:
@@ -99,13 +105,27 @@ couchTests.security_validation = function(debug) {
}
// set user as the admin
- T(db.setDbProperty("_admins", ["Damien Katz"]).ok);
+ T(db.setSecObj({
+ admins : {names : ["Damien Katz"]}
+ }).ok);
T(userDb.save(designDoc).ok);
- // test the _whoami endpoint
+ var user2Db = new CouchDB("test_suite_db",
+ {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
+ );
+ // Attempt to save the design as a non-admin (in replication scenario)
+ try {
+ user2Db.save(designDoc, {new_edits : false});
+ T(false && "Can't get here. Should have thrown an error on design doc");
+ } catch (e) {
+ T(e.error == "unauthorized");
+ T(user2Db.last_req.status == 401);
+ }
+
+ // test the _session API
var resp = userDb.request("GET", "/_session");
- var user = JSON.parse(resp.responseText)
+ var user = JSON.parse(resp.responseText).userCtx;
T(user.name == "Damien Katz");
// test that the roles are listed properly
TEquals(user.roles, []);
@@ -116,20 +136,23 @@ couchTests.security_validation = function(debug) {
doc.foo=2;
T(userDb.save(doc).ok);
- // Save a document that's missing an author field.
- try {
- userDb.save({foo:1});
- T(false && "Can't get here. Should have thrown an error 2");
- } catch (e) {
- T(e.error == "forbidden");
- T(userDb.last_req.status == 403);
+ // Save a document that's missing an author field (before and after compaction)
+ for (var i=0; i<2; i++) {
+ try {
+ userDb.save({foo:1});
+ T(false && "Can't get here. Should have thrown an error 2");
+ } catch (e) {
+ T(e.error == "forbidden");
+ T(userDb.last_req.status == 403);
+ }
+ // compact.
+ T(db.compact().ok);
+ T(db.last_req.status == 202);
+ // compaction isn't instantaneous, loop until done
+ while (db.info().compact_running) {};
}
// 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 {
@@ -158,6 +181,37 @@ couchTests.security_validation = function(debug) {
T(e.error == "unauthorized");
T(userDb.last_req.status == 401);
}
+
+ // admin must save with author field unless admin override
+ var resp = db.request("GET", "/_session");
+ var user = JSON.parse(resp.responseText).userCtx;
+ T(user.name == null);
+ // test that we are admin
+ TEquals(user.roles, ["_admin"]);
+
+ // can't save the doc even though we are admin
+ var doc = db.open("testdoc");
+ doc.foo=3;
+ try {
+ db.save(doc);
+ T(false && "Can't get here. Should have thrown an error 3");
+ } catch (e) {
+ T(e.error == "unauthorized");
+ T(db.last_req.status == 401);
+ }
+
+ // now turn on admin override
+ T(db.setDbProperty("_security", {admin_override : true}).ok);
+ T(db.save(doc).ok);
+
+ // try to do something lame
+ try {
+ db.setDbProperty("_security", ["foo"]);
+ T(false && "can't do this");
+ } catch(e) {}
+
+ // go back to normal
+ T(db.setDbProperty("_security", {admin_override : false}).ok);
// Now delete document
T(user2Db.deleteDoc(doc).ok);
@@ -188,7 +242,6 @@ couchTests.security_validation = function(debug) {
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;
diff --git a/share/www/script/test/show_documents.js b/share/www/script/test/show_documents.js
index f484e029..e06bcadc 100644
--- a/share/www/script/test/show_documents.js
+++ b/share/www/script/test/show_documents.js
@@ -25,7 +25,11 @@ couchTests.show_documents = function(debug) {
if (doc) {
return "Hello World";
} else {
- return "Empty World";
+ if(req.id) {
+ return "New World";
+ } else {
+ return "Empty World";
+ }
}
}),
"just-name" : stringFun(function(doc, req) {
@@ -50,18 +54,28 @@ couchTests.show_documents = function(debug) {
json : req
}
}),
+ "show-deleted" : stringFun(function(doc, req) {
+ if(doc) {
+ return doc._id;
+ } else {
+ return "No doc " + req.id;
+ }
+ }),
"render-error" : stringFun(function(doc, req) {
return noSuchVariable;
}),
"empty" : stringFun(function(doc, req) {
return "";
}),
+ "fail" : stringFun(function(doc, req) {
+ return doc._id;
+ }),
"xml-type" : stringFun(function(doc, req) {
return {
"headers" : {
"Content-Type" : "application/xml"
},
- "body" : new XML('<xml><node foo="bar"/></xml>')
+ "body" : new XML('<xml><node foo="bar"/></xml>').toXMLString()
}
}),
"no-set-etag" : stringFun(function(doc, req) {
@@ -72,6 +86,25 @@ couchTests.show_documents = function(debug) {
"body" : "something"
}
}),
+ "list-api" : stringFun(function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey");
+ }),
+ "list-api-mix" : stringFun(function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey ");
+ return "Dude";
+ }),
+ "list-api-mix-with-header" : stringFun(function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey ");
+ return {
+ headers: {
+ "X-Couch-Test-Header-Awesome": "Oh Yeah!"
+ },
+ body: "Dude"
+ };
+ }),
"accept-switch" : stringFun(function(doc, req) {
if (req.headers["Accept"].match(/image/)) {
return {
@@ -114,12 +147,16 @@ couchTests.show_documents = function(debug) {
// E4X outside of a string. Outside of tests you
// can just use E4X literals.
eval('xml.node.@foo = doc.word');
- return xml;
+ log('xml: '+xml.toSource());
+ return xml.toXMLString();
});
provides("foo", function() {
return "foofoo";
});
+ }),
+ "withSlash": stringFun(function(doc, req) {
+ return { json: doc }
})
}
};
@@ -157,7 +194,7 @@ couchTests.show_documents = function(debug) {
T(xhr.responseText == "");
// // hello template world (non-existing docid)
- xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/nonExistingDoc");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/fail/nonExistingDoc");
T(xhr.status == 404);
var resp = JSON.parse(xhr.responseText);
T(resp.error == "not_found");
@@ -169,8 +206,7 @@ couchTests.show_documents = function(debug) {
// show with missing doc
xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc");
T(xhr.status == 404);
- var resp = JSON.parse(xhr.responseText);
- T(resp.error == "not_found");
+ TEquals("No such doc", xhr.responseText);
// show with missing func
xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/missing/"+docid);
@@ -342,4 +378,37 @@ couchTests.show_documents = function(debug) {
xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/json/foo");
TEquals(1, JSON.parse(xhr.responseText)._conflicts.length);
+ var doc3 = {_id:"a/b/c", a:1};
+ db.save(doc3);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/withSlash/a/b/c");
+ T(xhr.status == 200);
+
+ // hello template world (non-existing docid)
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/nonExistingDoc");
+ T(xhr.responseText == "New World");
+
+ // test list() compatible API
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api/foo");
+ T(xhr.responseText == "Hey");
+ TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix/foo");
+ T(xhr.responseText == "Hey Dude");
+ TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix-with-header/foo");
+ T(xhr.responseText == "Hey Dude");
+ TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool");
+ TEquals("Oh Yeah!", xhr.getResponseHeader("X-Couch-Test-Header-Awesome"), "header should be cool");
+
+ // test deleted docs
+ var doc = {_id:"testdoc",foo:1};
+ db.save(doc);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc");
+ TEquals("testdoc", xhr.responseText, "should return 'testdoc'");
+
+ db.deleteDoc(doc);
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc");
+ TEquals("No doc testdoc", xhr.responseText, "should return 'no doc testdoc'");
+
};
diff --git a/share/www/script/test/stats.js b/share/www/script/test/stats.js
index 793b390d..6fb0fbba 100644
--- a/share/www/script/test/stats.js
+++ b/share/www/script/test/stats.js
@@ -30,7 +30,7 @@ couchTests.stats = function(debug) {
_id:"_design/test", // turn off couch.js id escaping?
language: "javascript",
views: {
- all_docs: {map: "function(doc) {emit(doc.integer, null);}"},
+ all_docs: {map: "function(doc) {emit(doc.integer, null);}"}
}
};
db.save(designDoc);
@@ -81,19 +81,17 @@ couchTests.stats = function(debug) {
var pre_dbs = getStat("couchdb", "open_databases").current || 0;
var pre_files = getStat("couchdb", "open_os_files").current || 0;
- // We have to make sure that as we open the max'th database
- // that we've waited for more than 1 second since opening
- // the first database so that any delayed commits will be
- // flushed.
var triggered = false;
var db = null;
for(var i = 0; i < max*2; i++) {
- try {
- db = newDb("test_suite_db_" + i, true);
- } catch(e) {
- triggered = true;
- CouchDB.request("GET", "/_sleep?time=1500");
- db = newDb("test_suite_db_" + i, true);
+ while (true) {
+ try {
+ db = newDb("test_suite_db_" + i, true);
+ break;
+ } catch(e) {
+ // all_dbs_active error!
+ triggered = true;
+ }
}
// Trigger a delayed commit
@@ -162,12 +160,15 @@ couchTests.stats = function(debug) {
runTest("couchdb", "database_writes", {
run: function(db) {
- CouchDB.request("POST", "/test_suite_db", {body: '{"a": "1"}'})
+ CouchDB.request("POST", "/test_suite_db", {
+ headers: {"Content-Type": "application/json"},
+ body: '{"a": "1"}'
+ });
},
test: function(before, after) {
TEquals(before+1, after, "POST'ing new docs increments doc writes.");
}
- })
+ });
runTest("couchdb", "database_writes", {
setup: function(db) {db.save({"_id": "test"});},
@@ -246,7 +247,7 @@ couchTests.stats = function(debug) {
});
runTest("httpd", "temporary_view_reads", {
- run: function(db) {db.query(function(doc) {emit(doc._id)})},
+ run: function(db) { db.query(function(doc) { emit(doc._id); }); },
test: function(before, after) {
TEquals(before+1, after, "Temporary views have their own counter.");
}
@@ -260,7 +261,7 @@ couchTests.stats = function(debug) {
});
runTest("httpd", "view_reads", {
- run: function(db) {db.query(function(doc) {emit(doc._id)});},
+ run: function(db) { db.query(function(doc) { emit(doc._id); }); },
test: function(before, after) {
TEquals(before, after, "Temporary views don't affect permanent views.");
}
diff --git a/share/www/script/test/update_documents.js b/share/www/script/test/update_documents.js
index 87fc7352..da68d621 100644
--- a/share/www/script/test/update_documents.js
+++ b/share/www/script/test/update_documents.js
@@ -68,10 +68,13 @@ couchTests.update_documents = function(debug) {
"headers" : {
"Content-Type" : "application/xml"
},
- "body" : xml
+ "body" : xml.toXMLString()
};
return [doc, resp];
+ }),
+ "get-uuid" : stringFun(function(doc, req) {
+ return [null, req.uuid];
})
}
};
@@ -123,7 +126,7 @@ couchTests.update_documents = function(debug) {
// bump counter
xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/bump-counter/"+docid, {
- headers : {"X-Couch-Full-Commit":"false"}
+ headers : {"X-Couch-Full-Commit":"true"}
});
T(xhr.status == 201);
T(xhr.responseText == "<h1>bumped it!</h1>");
@@ -135,12 +138,16 @@ couchTests.update_documents = function(debug) {
headers : {"X-Couch-Full-Commit":"true"}
});
+ var NewRev = xhr.getResponseHeader("X-Couch-Update-NewRev");
doc = db.open(docid);
+ T(doc['_rev'] == NewRev);
+
+
T(doc.counter == 2);
// parse xml
xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/xml/"+docid, {
- headers : {"X-Couch-Full-Commit":"false"},
+ headers : {"X-Couch-Full-Commit":"true"},
"body" : '<xml><foo>bar</foo></xml>'
});
T(xhr.status == 201);
@@ -148,5 +155,10 @@ couchTests.update_documents = function(debug) {
doc = db.open(docid);
T(doc.via_xml == "bar");
+
+ // Server provides UUID when POSTing without an ID in the URL
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/update/_update/get-uuid/");
+ T(xhr.status == 200);
+ T(xhr.responseText.length == 32);
};
diff --git a/share/www/script/test/users_db.js b/share/www/script/test/users_db.js
index 2cf63fcf..1e13e5d7 100644
--- a/share/www/script/test/users_db.js
+++ b/share/www/script/test/users_db.js
@@ -24,44 +24,101 @@ couchTests.users_db = function(debug) {
// to determine the actual users db name.
function testFun() {
- usersDb.deleteDb();
-
// test that the validation function is installed
var ddoc = usersDb.open("_design/_auth");
T(ddoc.validate_doc_update);
// test that you can login as a user using basic auth
var jchrisUserDoc = CouchDB.prepareUserDoc({
- username: "jchris@apache.org"
+ name: "jchris@apache.org"
}, "funnybone");
T(usersDb.save(jchrisUserDoc).ok);
- T(CouchDB.session().name == null);
+ T(CouchDB.session().userCtx.name == null);
+
+ // test that you can use basic auth aginst the users db
var s = CouchDB.session({
headers : {
+ // base64_encode("jchris@apache.org:funnybone")
"Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l"
}
});
- T(s.name == "jchris@apache.org");
- T(s.user_doc._id == "org.couchdb.user:jchris@apache.org")
- T(s.info.authenticated == "{couch_httpd_auth, default_authentication_handler}");
- T(s.info.user_db == "test_suite_users");
- TEquals(["{couch_httpd_oauth, oauth_authentication_handler}",
- "{couch_httpd_auth, cookie_authentication_handler}",
- "{couch_httpd_auth, default_authentication_handler}"], s.info.handlers);
+ T(s.userCtx.name == "jchris@apache.org");
+ T(s.info.authenticated == "default");
+ T(s.info.authentication_db == "test_suite_users");
+ TEquals(["oauth", "cookie", "default"], s.info.authentication_handlers);
var s = CouchDB.session({
headers : {
- "Authorization" : "Basic Xzpf" // username and pass of _:_
+ "Authorization" : "Basic Xzpf" // name and pass of _:_
}
});
T(s.name == null);
- T(s.info.authenticated == "{couch_httpd_auth, default_authentication_handler}");
+ T(s.info.authenticated == "default");
+
+
+ // ok, now create a conflicting edit on the jchris doc, and make sure there's no login.
+ var jchrisUser2 = JSON.parse(JSON.stringify(jchrisUserDoc));
+ jchrisUser2.foo = "bar";
+ T(usersDb.save(jchrisUser2).ok);
+ try {
+ usersDb.save(jchrisUserDoc);
+ T(false && "should be an update conflict")
+ } catch(e) {
+ T(true);
+ }
+ // save as bulk with new_edits=false to force conflict save
+ var resp = usersDb.bulkSave([jchrisUserDoc],{all_or_nothing : true});
+
+ var jchrisWithConflict = usersDb.open(jchrisUserDoc._id, {conflicts : true});
+ T(jchrisWithConflict._conflicts.length == 1)
+
+ // no login with conflicted user doc
+ try {
+ var s = CouchDB.session({
+ headers : {
+ "Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l"
+ }
+ });
+ T(false && "this will throw")
+ } catch(e) {
+ T(e.error == "unauthorized")
+ T(/conflict/.test(e.reason))
+ }
+
+ // you can delete a user doc
+ s = CouchDB.session().userCtx;
+ T(s.name == null);
+ T(s.roles.indexOf("_admin") !== -1);
+ T(usersDb.deleteDoc(jchrisWithConflict).ok);
+
+ // you can't change doc from type "user"
+ jchrisUserDoc = usersDb.open(jchrisUserDoc._id);
+ jchrisUserDoc.type = "not user";
+ try {
+ usersDb.save(jchrisUserDoc);
+ T(false && "should only allow us to save doc when type == 'user'");
+ } catch(e) {
+ T(e.reason == "doc.type must be user");
+ }
+ jchrisUserDoc.type = "user";
+
+ // "roles" must be an array
+ jchrisUserDoc.roles = "not an array";
+ try {
+ usersDb.save(jchrisUserDoc);
+ T(false && "should only allow us to save doc when roles is an array");
+ } catch(e) {
+ T(e.reason == "doc.roles must be an array");
+ }
+ jchrisUserDoc.roles = [];
};
-
+
+ usersDb.deleteDb();
run_on_modified_server(
[{section: "couch_httpd_auth",
- key: "authentication_db", value: "test_suite_users"}],
+ key: "authentication_db", value: usersDb.name}],
testFun
);
+ usersDb.deleteDb(); // cleanup
-} \ No newline at end of file
+}
diff --git a/share/www/script/test/uuids.js b/share/www/script/test/uuids.js
index 4de7ce90..fc33a105 100644
--- a/share/www/script/test/uuids.js
+++ b/share/www/script/test/uuids.js
@@ -93,12 +93,20 @@ couchTests.uuids = function(debug) {
xhr = CouchDB.request("GET", "/_uuids?count=1000");
T(xhr.status == 200);
result = JSON.parse(xhr.responseText);
- for(var i = 1; i < result.uuids.length; i++) {
- T(result.uuids[i].length == 32);
- var u1 = result.uuids[i-1].substr(0, 13);
- var u2 = result.uuids[i].substr(0, 13);
- T(u1 < u2, "UTC uuids are roughly ordered.");
+ T(result.uuids[1].length == 32);
+
+ // no collisions
+ var seen = {};
+ for(var i in result.uuids) {
+ var id = result.uuids[i];
+ T(seen[id] === undefined);
+ seen[id] = 1;
}
+
+ // roughly ordered
+ var u1 = result.uuids[1].substr(0, 13);
+ var u2 = result.uuids[result.uuids.length-1].substr(0, 13);
+ T(u1 < u2, "UTC uuids are only roughly ordered, so this assertion may fail occasionally. Don't sweat it.");
};
run_on_modified_server([{
diff --git a/share/www/script/test/view_collation.js b/share/www/script/test/view_collation.js
index f4ae4a15..b01a5c50 100644
--- a/share/www/script/test/view_collation.js
+++ b/share/www/script/test/view_collation.js
@@ -90,27 +90,27 @@ couchTests.view_collation = function(debug) {
// the inclusive_end=true functionality is limited to endkey currently
// if you need inclusive_start=false for startkey, please do implement. ;)
var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:true}).rows;
- T(rows[rows.length-1].key == "b")
+ T(rows[rows.length-1].key == "b");
// descending=true
var rows = db.query(queryFun, null, {endkey : "b",
descending:true, inclusive_end:true}).rows;
- T(rows[rows.length-1].key == "b")
+ T(rows[rows.length-1].key == "b");
// test inclusive_end=false
var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "aa")
+ T(rows[rows.length-1].key == "aa");
// descending=true
var rows = db.query(queryFun, null, {endkey : "b",
descending:true, inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "B")
+ T(rows[rows.length-1].key == "B");
var rows = db.query(queryFun, null, {
endkey : "b", endkey_docid: "10",
inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "aa")
+ T(rows[rows.length-1].key == "aa");
var rows = db.query(queryFun, null, {
endkey : "b", endkey_docid: "11",
inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "b")
+ T(rows[rows.length-1].key == "b");
};
diff --git a/share/www/script/test/view_collation_raw.js b/share/www/script/test/view_collation_raw.js
index 08f37fae..31624cdb 100644
--- a/share/www/script/test/view_collation_raw.js
+++ b/share/www/script/test/view_collation_raw.js
@@ -97,27 +97,27 @@ couchTests.view_collation_raw = function(debug) {
// the inclusive_end=true functionality is limited to endkey currently
// if you need inclusive_start=false for startkey, please do implement. ;)
var rows = db.view("test/test", {endkey : "b", inclusive_end:true}).rows;
- T(rows[rows.length-1].key == "b")
+ T(rows[rows.length-1].key == "b");
// descending=true
var rows = db.view("test/test", {endkey : "b",
descending:true, inclusive_end:true}).rows;
- T(rows[rows.length-1].key == "b")
+ T(rows[rows.length-1].key == "b");
// test inclusive_end=false
var rows = db.view("test/test", {endkey : "b", inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "aa")
+ T(rows[rows.length-1].key == "aa");
// descending=true
var rows = db.view("test/test", {endkey : "b",
descending:true, inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "ba")
+ T(rows[rows.length-1].key == "ba");
var rows = db.view("test/test", {
endkey : "b", endkey_docid: "10",
inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "aa")
+ T(rows[rows.length-1].key == "aa");
var rows = db.view("test/test", {
endkey : "b", endkey_docid: "11",
inclusive_end:false}).rows;
- T(rows[rows.length-1].key == "aa")
+ T(rows[rows.length-1].key == "aa");
};
diff --git a/share/www/script/test/view_compaction.js b/share/www/script/test/view_compaction.js
new file mode 100644
index 00000000..a11fb7bd
--- /dev/null
+++ b/share/www/script/test/view_compaction.js
@@ -0,0 +1,104 @@
+// 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.view_compaction = function(debug) {
+
+ if (debug) debugger;
+
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit": "true"});
+
+ db.deleteDb();
+ db.createDb();
+
+ var ddoc = {
+ _id: "_design/foo",
+ language: "javascript",
+ views: {
+ view1: {
+ map: "function(doc) { emit(doc._id, doc.value) }"
+ },
+ view2: {
+ map: "function(doc) { emit(doc._id, doc.value); }",
+ reduce: "function(keys, values, rereduce) { return sum(values); }"
+ }
+ }
+ };
+ T(db.save(ddoc).ok);
+
+ var docs = makeDocs(0, 1000);
+ db.bulkSave(docs);
+
+ var resp = db.view('foo/view1', {});
+ T(resp.rows.length === 1000);
+
+ resp = db.view('foo/view2', {});
+ T(resp.rows.length === 1);
+
+ resp = db.designInfo("_design/foo");
+ T(resp.view_index.update_seq === 1001);
+
+
+ // update docs
+ for (var i = 0; i < docs.length; i++) {
+ docs[i].integer = docs[i].integer + 1;
+ }
+ db.bulkSave(docs);
+
+
+ resp = db.view('foo/view1', {});
+ T(resp.rows.length === 1000);
+
+ resp = db.view('foo/view2', {});
+ T(resp.rows.length === 1);
+
+ resp = db.designInfo("_design/foo");
+ T(resp.view_index.update_seq === 2001);
+
+
+ // update docs again...
+ for (var i = 0; i < docs.length; i++) {
+ docs[i].integer = docs[i].integer + 2;
+ }
+ db.bulkSave(docs);
+
+
+ resp = db.view('foo/view1', {});
+ T(resp.rows.length === 1000);
+
+ resp = db.view('foo/view2', {});
+ T(resp.rows.length === 1);
+
+ resp = db.designInfo("_design/foo");
+ T(resp.view_index.update_seq === 3001);
+
+ var disk_size_before_compact = resp.view_index.disk_size;
+
+ // compact view group
+ var xhr = CouchDB.request("POST", "/" + db.name + "/_compact" + "/foo");
+ T(JSON.parse(xhr.responseText).ok === true);
+
+ resp = db.designInfo("_design/foo");
+ while (resp.view_index.compact_running === true) {
+ resp = db.designInfo("_design/foo");
+ }
+
+
+ resp = db.view('foo/view1', {});
+ T(resp.rows.length === 1000);
+
+ resp = db.view('foo/view2', {});
+ T(resp.rows.length === 1);
+
+ resp = db.designInfo("_design/foo");
+ T(resp.view_index.update_seq === 3001);
+ T(resp.view_index.disk_size < disk_size_before_compact);
+}; \ No newline at end of file
diff --git a/share/www/script/test/view_errors.js b/share/www/script/test/view_errors.js
index 0f90c46f..c05000b7 100644
--- a/share/www/script/test/view_errors.js
+++ b/share/www/script/test/view_errors.js
@@ -16,8 +16,6 @@ couchTests.view_errors = function(debug) {
db.createDb();
if (debug) debugger;
-
-
run_on_modified_server(
[{section: "couchdb",
key: "os_process_timeout",
@@ -26,12 +24,13 @@ couchTests.view_errors = function(debug) {
var doc = {integer: 1, string: "1", array: [1, 2, 3]};
T(db.save(doc).ok);
- // emitting a key value that is undefined should result in that row not
- // being included in the view results
+ // emitting a key value that is undefined should result in that row
+ // being included in the view results as null
var results = db.query(function(doc) {
emit(doc.undef, null);
});
- T(results.total_rows == 0);
+ T(results.total_rows == 1);
+ T(results.rows[0].key == null);
// if a view function throws an exception, its results are not included in
// the view index, but the view does not itself raise an error
@@ -41,13 +40,13 @@ couchTests.view_errors = function(debug) {
T(results.total_rows == 0);
// if a view function includes an undefined value in the emitted key or
- // value, an error is logged and the result is not included in the view
- // index, and the view itself does not raise an error
+ // value, it is treated as null
var results = db.query(function(doc) {
emit([doc._id, doc.undef], null);
});
- T(results.total_rows == 0);
-
+ T(results.total_rows == 1);
+ T(results.rows[0].key[1] == null);
+
// querying a view with invalid params should give a resonable error message
var xhr = CouchDB.request("POST", "/test_suite_db/_temp_view?startkey=foo", {
headers: {"Content-Type": "application/json"},
@@ -57,14 +56,14 @@ couchTests.view_errors = function(debug) {
});
T(JSON.parse(xhr.responseText).error == "bad_request");
- // views should ignore Content-Type, like the rest of CouchDB
+ // content type must be json
var xhr = CouchDB.request("POST", "/test_suite_db/_temp_view", {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: JSON.stringify({language: "javascript",
map : "function(doc){}"
})
});
- T(xhr.status == 200);
+ T(xhr.status == 415);
var map = function (doc) {emit(doc.integer, doc.integer);};
@@ -75,9 +74,6 @@ couchTests.view_errors = function(debug) {
T(e.error == "query_parse_error");
}
- // reduce=false on map views doesn't work, so group=true will
- // never throw for temp reduce views.
-
var designDoc = {
_id:"_design/test",
language: "javascript",
@@ -105,6 +101,15 @@ couchTests.view_errors = function(debug) {
db.view("test/no_reduce", {group: true});
T(0 == 1);
} catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "query_parse_error");
+ }
+
+ try {
+ db.view("test/no_reduce", {group_level: 1});
+ T(0 == 1);
+ } catch(e) {
+ T(db.last_req.status == 400);
T(e.error == "query_parse_error");
}
@@ -116,10 +121,23 @@ couchTests.view_errors = function(debug) {
T(e.error == "query_parse_error");
}
+ db.view("test/no_reduce", {reduce: false});
+ TEquals(200, db.last_req.status, "reduce=false for map views (without"
+ + " group or group_level) is allowed");
+
try {
db.view("test/with_reduce", {group: true, reduce: false});
T(0 == 1);
} catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "query_parse_error");
+ }
+
+ try {
+ db.view("test/with_reduce", {group_level: 1, reduce: false});
+ T(0 == 1);
+ } catch(e) {
+ T(db.last_req.status == 400);
T(e.error == "query_parse_error");
}
diff --git a/share/www/script/test/view_multi_key_design.js b/share/www/script/test/view_multi_key_design.js
index 5a2f645d..c39e73d9 100644
--- a/share/www/script/test/view_multi_key_design.js
+++ b/share/www/script/test/view_multi_key_design.js
@@ -34,11 +34,11 @@ couchTests.view_multi_key_design = function(debug) {
reduce:"function (keys, values) { return sum(values); };"
}
}
- }
+ };
T(db.save(designDoc).ok);
// Test that missing keys work too
- var keys = [101,30,15,37,50]
+ var keys = [101,30,15,37,50];
var reduce = db.view("test/summate",{group:true},keys).rows;
T(reduce.length == keys.length-1); // 101 is missing
for(var i=0; i<reduce.length; i++) {
@@ -81,7 +81,7 @@ couchTests.view_multi_key_design = function(debug) {
}
// Test that a map & reduce containing func support keys when reduce=false
- resp = db.view("test/summate", {reduce: false}, keys);
+ var resp = db.view("test/summate", {reduce: false}, keys);
T(resp.rows.length == 5);
// Check that limiting by startkey_docid and endkey_docid get applied
diff --git a/share/www/script/test/view_sandboxing.js b/share/www/script/test/view_sandboxing.js
index 9f893b28..02951d9f 100644
--- a/share/www/script/test/view_sandboxing.js
+++ b/share/www/script/test/view_sandboxing.js
@@ -42,11 +42,99 @@ couchTests.view_sandboxing = function(debug) {
// make sure that a view cannot access the map_funs array defined used by
// the view server
- var results = db.query(function(doc) { map_funs.push(1); emit(null, doc) });
+ var results = db.query(function(doc) { map_funs.push(1); emit(null, doc); });
T(results.total_rows == 0);
// make sure that a view cannot access the map_results array defined used by
// the view server
- var results = db.query(function(doc) { map_results.push(1); emit(null, doc) });
+ var results = db.query(function(doc) { map_results.push(1); emit(null, doc); });
T(results.total_rows == 0);
+
+ // test for COUCHDB-925
+ // altering 'doc' variable in map function affects other map functions
+ var ddoc = {
+ _id: "_design/foobar",
+ language: "javascript",
+ views: {
+ view1: {
+ map:
+ (function(doc) {
+ if (doc.values) {
+ doc.values = [666];
+ }
+ if (doc.tags) {
+ doc.tags.push("qwerty");
+ }
+ if (doc.tokens) {
+ doc.tokens["c"] = 3;
+ }
+ }).toString()
+ },
+ view2: {
+ map:
+ (function(doc) {
+ if (doc.values) {
+ emit(doc._id, doc.values);
+ }
+ if (doc.tags) {
+ emit(doc._id, doc.tags);
+ }
+ if (doc.tokens) {
+ emit(doc._id, doc.tokens);
+ }
+ }).toString()
+ }
+ }
+ };
+ var doc1 = {
+ _id: "doc1",
+ values: [1, 2, 3]
+ };
+ var doc2 = {
+ _id: "doc2",
+ tags: ["foo", "bar"],
+ tokens: {a: 1, b: 2}
+ };
+
+ db.deleteDb();
+ db.createDb();
+ T(db.save(ddoc).ok);
+ T(db.save(doc1).ok);
+ T(db.save(doc2).ok);
+
+ var view1Results = db.view(
+ "foobar/view1", {bypass_cache: Math.round(Math.random() * 1000)});
+ var view2Results = db.view(
+ "foobar/view2", {bypass_cache: Math.round(Math.random() * 1000)});
+
+ TEquals(0, view1Results.rows.length, "view1 has 0 rows");
+ TEquals(3, view2Results.rows.length, "view2 has 3 rows");
+
+ TEquals(doc1._id, view2Results.rows[0].key);
+ TEquals(doc2._id, view2Results.rows[1].key);
+ TEquals(doc2._id, view2Results.rows[2].key);
+
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=449657
+ TEquals(3, view2Results.rows[0].value.length,
+ "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
+ if (view2Results.rows[0].value.length === 3) {
+ TEquals(1, view2Results.rows[0].value[0]);
+ TEquals(2, view2Results.rows[0].value[1]);
+ TEquals(3, view2Results.rows[0].value[2]);
+ }
+
+ TEquals(1, view2Results.rows[1].value["a"]);
+ TEquals(2, view2Results.rows[1].value["b"]);
+ TEquals('undefined', typeof view2Results.rows[1].value["c"],
+ "doc2.tokens object was not sealed");
+
+ TEquals(2, view2Results.rows[2].value.length,
+ "Warning: installed SpiderMonkey version doesn't allow sealing of arrays");
+ if (view2Results.rows[2].value.length === 2) {
+ TEquals("foo", view2Results.rows[2].value[0]);
+ TEquals("bar", view2Results.rows[2].value[1]);
+ }
+
+ // cleanup
+ db.deleteDb();
};
diff --git a/share/www/script/test/view_update_seq.js b/share/www/script/test/view_update_seq.js
new file mode 100644
index 00000000..9757caa1
--- /dev/null
+++ b/share/www/script/test/view_update_seq.js
@@ -0,0 +1,89 @@
+// 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.view_update_seq = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ T(db.info().update_seq == 0);
+
+ var resp = db.allDocs({update_seq:true});
+
+ T(resp.rows.length == 0);
+ T(resp.update_seq == 0);
+
+ var designDoc = {
+ _id:"_design/test",
+ language: "javascript",
+ views: {
+ all_docs: {
+ map: "function(doc) { emit(doc.integer, doc.string) }"
+ },
+ summate: {
+ map:"function (doc) {emit(doc.integer, doc.integer)};",
+ reduce:"function (keys, values) { return sum(values); };"
+ }
+ }
+ };
+ T(db.save(designDoc).ok);
+
+ T(db.info().update_seq == 1);
+
+ resp = db.allDocs({update_seq:true});
+
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 1);
+
+ var docs = makeDocs(0, 100);
+ db.bulkSave(docs);
+
+ resp = db.allDocs({limit: 1});
+ T(resp.rows.length == 1);
+ T(!resp.update_seq, "all docs");
+
+ resp = db.allDocs({limit: 1, update_seq:true});
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 101);
+
+ resp = db.view('test/all_docs', {limit: 1, update_seq:true});
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 101);
+
+ resp = db.view('test/all_docs', {limit: 1, update_seq:false});
+ T(resp.rows.length == 1);
+ T(!resp.update_seq, "view");
+
+ resp = db.view('test/summate', {update_seq:true});
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 101);
+
+ db.save({"id":"0"});
+ resp = db.view('test/all_docs', {limit: 1,stale: "ok", update_seq:true});
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 101);
+
+ resp = db.view('test/all_docs', {limit: 1, update_seq:true});
+ T(resp.rows.length == 1);
+ T(resp.update_seq == 102);
+
+ resp = db.view('test/all_docs',{update_seq:true},["0","1"]);
+ T(resp.update_seq == 102);
+
+ resp = db.view('test/all_docs',{update_seq:true},["0","1"]);
+ T(resp.update_seq == 102);
+
+ resp = db.view('test/summate',{group:true, update_seq:true},["0","1"]);
+ T(resp.update_seq == 102);
+
+};
diff --git a/share/www/script/test/view_xml.js b/share/www/script/test/view_xml.js
index 451fb6a8..3403b47c 100644
--- a/share/www/script/test/view_xml.js
+++ b/share/www/script/test/view_xml.js
@@ -22,7 +22,7 @@ couchTests.view_xml = function(debug) {
var results = db.query(
"function(doc) {\n" +
" var xml = new XML(doc.content);\n" +
- " emit(xml.title.text(), null);\n" +
+ " emit(xml.title.text().toXMLString(), null);\n" +
"}");
T(results.total_rows == 2);
T(results.rows[0].key == "Testing E4X");
@@ -31,7 +31,7 @@ couchTests.view_xml = function(debug) {
var results = db.query(
"function(doc) {\n" +
" var xml = new XML(doc.content);\n" +
- " emit(xml.title.@id, null);\n" +
+ " emit(xml.title.@id.toXMLString(), null);\n" +
"}");
T(results.total_rows == 2);
T(results.rows[0].key == "e4x");