You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by kx...@apache.org on 2014/12/10 12:08:16 UTC
[09/39] couchdb commit: updated refs/heads/master to 9950caa
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/cookie_auth.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/cookie_auth.js b/test/javascript/tests/cookie_auth.js
new file mode 100644
index 0000000..9b4bd64
--- /dev/null
+++ b/test/javascript/tests/cookie_auth.js
@@ -0,0 +1,288 @@
+// 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.cookie_auth = function(debug) {
+ // This tests cookie-based authentication.
+
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ var password = "3.141592653589";
+
+ var loginUser = function(username) {
+ var pws = {
+ jan: "apple",
+ "Jason Davies": password,
+ jchris: "funnybone"
+ };
+ var username1 = username.replace(/[0-9]$/, "");
+ var password = pws[username];
+ //console.log("Logging in '" + username1 + "' with password '" + password + "'");
+ T(CouchDB.login(username1, pws[username]).ok);
+ };
+
+ var open_as = function(db, docId, username) {
+ loginUser(username);
+ try {
+ return db.open(docId, {"anti-cache": Math.round(Math.random() * 100000)});
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ var save_as = function(db, doc, username)
+ {
+ loginUser(username);
+ try {
+ return db.save(doc);
+ } catch (ex) {
+ return ex;
+ } finally {
+ CouchDB.logout();
+ }
+ };
+
+ // 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;
+ }
+
+ // this function will be called on the modified server
+ var testFun = function () {
+ try {
+
+ // test that the users db is born with the auth ddoc
+ var ddoc = open_as(usersDb, "_design/_auth", "jan");
+ T(ddoc.validate_doc_update);
+
+ // TODO test that changing the config so an existing db becomes the users db installs the ddoc also
+
+ // Create a user
+ var jasonUserDoc = CouchDB.prepareUserDoc({
+ name: "Jason Davies"
+ }, password);
+ T(usersDb.save(jasonUserDoc).ok);
+
+ var checkDoc = open_as(usersDb, jasonUserDoc._id, "jan");
+ TEquals("Jason Davies", checkDoc.name);
+
+ var jchrisUserDoc = CouchDB.prepareUserDoc({
+ name: "jchris@apache.org"
+ }, "funnybone");
+ T(usersDb.save(jchrisUserDoc).ok);
+
+ // make sure we cant create duplicate users
+ var duplicateJchrisDoc = CouchDB.prepareUserDoc({
+ name: "jchris@apache.org"
+ }, "eh, Boo-Boo?");
+
+ try {
+ usersDb.save(duplicateJchrisDoc);
+ T(false && "Can't create duplicate user names. Should have thrown an error.");
+ } catch (e) {
+ TEquals("conflict", e.error);
+ TEquals(409, usersDb.last_req.status);
+ }
+
+ // we can't create _names
+ var underscoreUserDoc = CouchDB.prepareUserDoc({
+ name: "_why"
+ }, "copperfield");
+
+ try {
+ usersDb.save(underscoreUserDoc);
+ T(false && "Can't create underscore user names. Should have thrown an error.");
+ } catch (e) {
+ TEquals("forbidden", e.error);
+ TEquals(403, usersDb.last_req.status);
+ }
+
+ // we can't create docs with malformed ids
+ var badIdDoc = CouchDB.prepareUserDoc({
+ name: "w00x"
+ }, "bar");
+
+ badIdDoc._id = "org.apache.couchdb:w00x";
+
+ try {
+ usersDb.save(badIdDoc);
+ T(false && "Can't create malformed docids. Should have thrown an error.");
+ } catch (e) {
+ TEquals("forbidden", e.error);
+ TEquals(403, usersDb.last_req.status);
+ }
+
+ // login works
+ T(CouchDB.login('Jason Davies', password).ok);
+ TEquals('Jason Davies', CouchDB.session().userCtx.name);
+
+ // JSON login works
+ var xhr = CouchDB.request("POST", "/_session", {
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({
+ name: 'Jason Davies',
+ password: password
+ })
+ });
+
+ T(JSON.parse(xhr.responseText).ok);
+ TEquals('Jason Davies', CouchDB.session().userCtx.name);
+
+ // 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) {
+ TEquals("not_found", e.error);
+ TEquals(404, usersDb.last_req.status);
+ }
+
+ // 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().userCtx.name != 'Jason Davies');
+
+ // test redirect on success
+ xhr = CouchDB.request("POST", "/_session?next=/", {
+ headers: {"Content-Type": "application/x-www-form-urlencoded"},
+ body: "name=Jason%20Davies&password="+encodeURIComponent(password)
+ });
+ // the browser should transparently follow the redirect and GET the server root (/)
+ // see http://dev.w3.org/2006/webapi/XMLHttpRequest/#infrastructure-for-the-send-method
+ if (xhr.status == 200) {
+ T(/Welcome/.test(xhr.responseText))
+ }
+
+ // test redirect on fail
+ xhr = CouchDB.request("POST", "/_session?fail=/", {
+ headers: {"Content-Type": "application/x-www-form-urlencoded"},
+ body: "name=Jason%20Davies&password=foobar"
+ });
+ if (xhr.status == 200) {
+ T(/Welcome/.test(xhr.responseText));
+ }
+
+ // 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().userCtx.name == "jchris@apache.org");
+ T(CouchDB.session().userCtx.roles.length == 0);
+
+ jasonUserDoc.foo=3;
+
+ try {
+ usersDb.save(jasonUserDoc);
+ T(false && "Can't update someone else's user doc. Should have thrown an error.");
+ } catch (e) {
+ T(e.error == "not_found");
+ T(usersDb.last_req.status == 404);
+ }
+
+ // test that you can't edit roles unless you are admin
+ jchrisUserDoc.roles = ["foo"];
+
+ try {
+ usersDb.save(jchrisUserDoc);
+ T(false && "Can't set roles unless you are admin. Should have thrown an error.");
+ } catch (e) {
+ T(e.error == "forbidden");
+ T(usersDb.last_req.status == 403);
+ }
+
+ T(CouchDB.logout().ok);
+
+ jchrisUserDoc.foo = ["foo"];
+ T(save_as(usersDb, jchrisUserDoc, "jan"));
+
+ // test that you can't save system (underscore) roles even if you are admin
+ jchrisUserDoc.roles = ["_bar"];
+
+ var res = save_as(usersDb, jchrisUserDoc, "jan");
+ T(res.error == "forbidden");
+ T(usersDb.last_req.status == 403);
+
+ // make sure the foo role has been applied
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ 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);
+
+ // set the -hashed- password so the salt matches
+ // todo ask on the ML about this
+
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ run_on_modified_server([{section: "admins",
+ key: "jchris@apache.org", value: "funnybone"}], function() {
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ 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().userCtx.roles.indexOf("foo") != -1);
+
+ // should work even when user doc has no password
+ jchrisUserDoc = usersDb.open(jchrisUserDoc._id);
+ delete jchrisUserDoc.salt;
+ delete jchrisUserDoc.password_sha;
+ T(usersDb.save(jchrisUserDoc).ok);
+ T(CouchDB.logout().ok);
+ T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ var s = CouchDB.session();
+ T(s.userCtx.name == "jchris@apache.org");
+ T(s.userCtx.roles.indexOf("_admin") != -1);
+ // test session info
+ T(s.info.authenticated == "cookie");
+ T(s.info.authentication_db == "test_suite_users");
+ // test that jchris still has the foo role
+ T(CouchDB.session().userCtx.roles.indexOf("foo") != -1);
+ });
+
+ } finally {
+ // Make sure we erase any auth cookies so we don't affect other tests
+ T(CouchDB.logout().ok);
+ }
+ // log in one last time so run_on_modified_server can clean up the admin account
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+ };
+
+ var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"});
+ usersDb.deleteDb();
+
+ run_on_modified_server(
+ [
+ {section: "couch_httpd_auth",
+ key: "authentication_db", value: "test_suite_users"},
+ {section: "couch_httpd_auth",
+ key: "iterations", value: "1"},
+ {section: "admins",
+ key: "jan", value: "apple"}
+ ],
+ testFun
+ );
+
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/copy_doc.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/copy_doc.js b/test/javascript/tests/copy_doc.js
new file mode 100644
index 0000000..d595761
--- /dev/null
+++ b/test/javascript/tests/copy_doc.js
@@ -0,0 +1,65 @@
+// 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.copy_doc = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ // copy a doc
+ var ok = db.save({_id:"doc_to_be_copied",v:1}).ok;
+ TEquals(true, ok, "Should return ok:true");
+ var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied", {
+ headers: {"Destination":"doc_that_was_copied"}
+ });
+
+ TEquals(true, JSON.parse(xhr.responseText).ok, "Should return ok:true");
+
+ TEquals(201, xhr.status, "Should return 201 status");
+ TEquals(1, db.open("doc_that_was_copied").v, "Should have value 1");
+
+ // COPY with existing target
+ var ok = db.save({_id:"doc_to_be_copied2",v:1}).ok;
+ TEquals(true, ok, "Should return ok:true");
+ var doc = db.save({_id:"doc_to_be_overwritten",v:2});
+ TEquals(true, doc.ok, "Should return ok:true");
+
+ // error condition
+ var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
+ headers: {"Destination":"doc_to_be_overwritten"}
+ });
+ TEquals(409, xhr.status, "Should return 409 status"); // conflict
+
+ var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2");
+ TEquals(400, xhr.status, "Should return 400 status");
+ TEquals("Destination header is mandatory for COPY.", JSON.parse(xhr.responseText).reason,
+ "Should report missing destination header");
+
+ var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
+ headers: {
+ "Destination": "http://localhost:5984/test_suite_db/doc_to_be_written"
+ }});
+ TEquals(400, xhr.status, "Should return 400 status");
+ TEquals("Destination URL must be relative.", JSON.parse(xhr.responseText).reason,
+ "Should report invalid destination header");
+
+ var rev = db.open("doc_to_be_overwritten")._rev;
+ var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2", {
+ headers: {"Destination":"doc_to_be_overwritten?rev=" + rev}
+ });
+ TEquals(201, xhr.status, "Should return 201 status");
+
+ var over = db.open("doc_to_be_overwritten");
+ T(rev != over._rev);
+ TEquals(1, over.v, "Should be value 1");
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/delayed_commits.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/delayed_commits.js b/test/javascript/tests/delayed_commits.js
new file mode 100644
index 0000000..dbb072f
--- /dev/null
+++ b/test/javascript/tests/delayed_commits.js
@@ -0,0 +1,154 @@
+// 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.delayed_commits = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ run_on_modified_server(
+ [{section: "couchdb",
+ key: "delayed_commits",
+ value: "true"}],
+
+ function () {
+ // By default, couchdb doesn't fully commit documents to disk right away,
+ // it waits about a second to batch the full commit flush along with any
+ // other updates. If it crashes or is restarted you may lose the most
+ // recent commits.
+
+ T(db.save({_id:"1",a:2,b:4}).ok);
+ T(db.open("1") != null);
+
+ restartServer();
+
+ T(db.open("1") == null); // lost the update.
+ // note if we waited > 1 sec before the restart, the doc would likely
+ // commit.
+
+
+ // Retry the same thing but with full commits on.
+
+ var db2 = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
+
+ T(db2.save({_id:"1",a:2,b:4}).ok);
+ T(db2.open("1") != null);
+
+ restartServer();
+
+ T(db2.open("1") != null);
+
+ // You can update but without committing immediately, and then ensure
+ // everything is commited in the last step.
+
+ T(db.save({_id:"2",a:2,b:4}).ok);
+ T(db.open("2") != null);
+ T(db.ensureFullCommit().ok);
+ restartServer();
+
+ T(db.open("2") != null);
+
+ // However, it's possible even when flushed, that the server crashed between
+ // the update and the commit, and you don't want to check to make sure
+ // every doc you updated actually made it to disk. So record the instance
+ // start time of the database before the updates and then check it again
+ // after the flush (the instance start time is returned by the flush
+ // operation). if they are the same, we know everything was updated
+ // safely.
+
+ // First try it with a crash.
+
+ var instanceStartTime = db.info().instance_start_time;
+
+ T(db.save({_id:"3",a:2,b:4}).ok);
+ T(db.open("3") != null);
+
+ restartServer();
+
+ var commitResult = db.ensureFullCommit();
+ T(commitResult.ok && commitResult.instance_start_time != instanceStartTime);
+ // start times don't match, meaning the server lost our change
+
+ T(db.open("3") == null); // yup lost it
+
+ // retry with no server restart
+
+ var instanceStartTime = db.info().instance_start_time;
+
+ T(db.save({_id:"4",a:2,b:4}).ok);
+ T(db.open("4") != null);
+
+ var commitResult = db.ensureFullCommit();
+ T(commitResult.ok && commitResult.instance_start_time == instanceStartTime);
+ // Successful commit, start times match!
+
+ restartServer();
+
+ T(db.open("4") != null);
+ });
+
+ // Now test that when we exceed the max_dbs_open, pending commits are safely
+ // written.
+ T(db.save({_id:"5",foo:"bar"}).ok);
+ var max = 2;
+ run_on_modified_server(
+ [{section: "couchdb",
+ key: "delayed_commits",
+ value: "true"},
+ {section: "couchdb",
+ key: "max_dbs_open",
+ value: max.toString()}],
+
+ function () {
+ for(var i=0; i<max; i++) {
+ var dbi = new CouchDB("test_suite_db" + i);
+ dbi.deleteDb();
+ dbi.createDb();
+ }
+ T(db.open("5").foo=="bar");
+ for(var i=0; i<max+1; i++) {
+ var dbi = new CouchDB("test_suite_db" + i);
+ dbi.deleteDb();
+ }
+ });
+
+
+ // 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);
+ });
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_docs.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/design_docs.js b/test/javascript/tests/design_docs.js
new file mode 100644
index 0000000..dd38858
--- /dev/null
+++ b/test/javascript/tests/design_docs.js
@@ -0,0 +1,466 @@
+// 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.design_docs = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ var db2 = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"});
+
+ if (debug) debugger;
+
+ db.deleteDb();
+ db.createDb();
+ db2.deleteDb();
+ db2.createDb();
+
+ var server_config = [
+ {
+ section: "query_server_config",
+ key: "reduce_limit",
+ value: "false"
+ }
+ ];
+
+ var testFun = function() {
+ var numDocs = 500;
+
+ function makebigstring(power) {
+ var str = "a";
+ while(power-- > 0) {
+ str = str + str;
+ }
+ return str;
+ }
+
+ var designDoc = {
+ _id: "_design/test",
+ 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",
+ circular_one: "require('./circular_two'); exports.name = 'One';",
+ circular_two: "require('./circular_one'); exports.name = 'Two';"
+ },
+ // paths relative to parent
+ idtest1: {
+ a: {
+ b: {d: "module.exports = require('../c/e').id;"},
+ c: {e: "exports.id = module.id;"}
+ }
+ },
+ // multiple paths relative to parent
+ idtest2: {
+ a: {
+ b: {d: "module.exports = require('../../a/c/e').id;"},
+ c: {e: "exports.id = module.id;"}
+ }
+ },
+ // paths relative to module
+ idtest3: {
+ a: {
+ b: "module.exports = require('./c/d').id;",
+ c: {
+ d: "module.exports = require('./e');",
+ e: "exports.id = module.id;"
+ }
+ }
+ },
+ // paths relative to module and parent
+ idtest4: {
+ a: {
+ b: "module.exports = require('../a/./c/d').id;",
+ c: {
+ d: "module.exports = require('./e');",
+ e: "exports.id = module.id;"
+ }
+ }
+ },
+ // paths relative to root
+ idtest5: {
+ a: "module.exports = require('whatever/idtest5/b').id;",
+ b: "exports.id = module.id;"
+ }
+ },
+ views: {
+ all_docs_twice: {
+ map:
+ (function(doc) {
+ emit(doc.integer, null);
+ emit(doc.integer, null);
+ }).toString()
+ },
+ no_docs: {
+ map:
+ (function(doc) {
+ }).toString()
+ },
+ single_doc: {
+ map:
+ (function(doc) {
+ if (doc._id === "1") {
+ emit(1, null);
+ }
+ }).toString()
+ },
+ summate: {
+ map:
+ (function(doc) {
+ emit(doc.integer, doc.integer);
+ }).toString(),
+ reduce:
+ (function(keys, values) {
+ return sum(values);
+ }).toString()
+ },
+ summate2: {
+ map:
+ (function(doc) {
+ emit(doc.integer, doc.integer);
+ }).toString(),
+ reduce:
+ (function(keys, values) {
+ return sum(values);
+ }).toString()
+ },
+ huge_src_and_results: {
+ map:
+ (function(doc) {
+ if (doc._id === "1") {
+ emit(makebigstring(16), null);
+ }
+ }).toString(),
+ reduce:
+ (function(keys, values) {
+ return makebigstring(16);
+ }).toString()
+ },
+ lib : {
+ baz : "exports.baz = 'bam';",
+ foo : {
+ foo : "exports.foo = 'bar';",
+ boom : "exports.boom = 'ok';",
+ zoom : "exports.zoom = 'yeah';"
+ }
+ },
+ commonjs : {
+ map :
+ (function(doc) {
+ emit(null, require('views/lib/foo/boom').boom);
+ }).toString()
+ }
+ },
+ shows: {
+ simple:
+ (function() {
+ return 'ok';
+ }).toString(),
+ requirey:
+ (function() {
+ var lib = require('whatever/commonjs/upper');
+ return lib.testing;
+ }).toString(),
+ circular:
+ (function() {
+ var lib = require('whatever/commonjs/upper');
+ return JSON.stringify(this);
+ }).toString(),
+ circular_require:
+ (function() {
+ return require('whatever/commonjs/circular_one').name;
+ }).toString(),
+ idtest1: (function() {
+ return require('whatever/idtest1/a/b/d');
+ }).toString(),
+ idtest2: (function() {
+ return require('whatever/idtest2/a/b/d');
+ }).toString(),
+ idtest3: (function() {
+ return require('whatever/idtest3/a/b');
+ }).toString(),
+ idtest4: (function() {
+ return require('whatever/idtest4/a/b');
+ }).toString(),
+ idtest5: (function() {
+ return require('whatever/idtest5/a');
+ }).toString()
+ }
+ }; // designDoc
+
+ var xhr = CouchDB.request(
+ "PUT", "/test_suite_db_a/_design/test", {body: JSON.stringify(designDoc)}
+ );
+ var resp = JSON.parse(xhr.responseText);
+
+ TEquals(resp.rev, db.save(designDoc).rev);
+
+ // test that editing a show fun on the ddoc results in a change in output
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/simple");
+ T(xhr.status == 200);
+ TEquals(xhr.responseText, "ok");
+
+ designDoc.shows.simple = (function() {
+ return 'ko';
+ }).toString();
+ T(db.save(designDoc).ok);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/simple");
+ T(xhr.status == 200);
+ TEquals(xhr.responseText, "ko");
+
+ xhr = CouchDB.request(
+ "GET", "/test_suite_db_a/_design/test/_show/simple?cache=buster"
+ );
+ T(xhr.status == 200);
+ TEquals("ok", xhr.responseText, 'query server used wrong ddoc');
+
+ // test commonjs require
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/requirey");
+ T(xhr.status == 200);
+ TEquals("PLANKTONwhatever/commonjs/upperplankton", xhr.responseText);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/circular");
+ T(xhr.status == 200);
+ TEquals("javascript", JSON.parse(xhr.responseText).language);
+
+ // test circular commonjs dependencies
+ xhr = CouchDB.request(
+ "GET",
+ "/test_suite_db/_design/test/_show/circular_require"
+ );
+ TEquals(200, xhr.status);
+ TEquals("One", xhr.responseText);
+
+ // Test that changes to the design doc properly invalidate cached modules:
+
+ // update the designDoc and replace
+ designDoc.whatever.commonjs.circular_one = "exports.name = 'Updated';"
+ T(db.save(designDoc).ok);
+
+ // request circular_require show function again and check the response has
+ // changed
+ xhr = CouchDB.request(
+ "GET",
+ "/test_suite_db/_design/test/_show/circular_require"
+ );
+ TEquals(200, xhr.status);
+ TEquals("Updated", xhr.responseText);
+
+
+ // test module id values are as expected:
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest1");
+ TEquals(200, xhr.status);
+ TEquals("whatever/idtest1/a/c/e", xhr.responseText);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest2");
+ TEquals(200, xhr.status);
+ TEquals("whatever/idtest2/a/c/e", xhr.responseText);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest3");
+ TEquals(200, xhr.status);
+ TEquals("whatever/idtest3/a/c/e", xhr.responseText);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest4");
+ TEquals(200, xhr.status);
+ TEquals("whatever/idtest4/a/c/e", xhr.responseText);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/idtest5");
+ TEquals(200, xhr.status);
+ TEquals("whatever/idtest5/b", xhr.responseText);
+
+
+ var prev_view_sig = db.designInfo("_design/test").view_index.signature;
+ var prev_view_size = db.designInfo("_design/test").view_index.disk_size;
+
+ db.bulkSave(makeDocs(1, numDocs + 1));
+ T(db.ensureFullCommit().ok);
+
+ // test that we get correct design doc info back,
+ // and also that GET /db/_design/test/_info
+ // hasn't triggered an update of the views
+ db.view("test/summate", {stale: "ok"}); // make sure view group's open
+ for (var i = 0; i < 2; i++) {
+ var dinfo = db.designInfo("_design/test");
+ TEquals("test", dinfo.name);
+ var vinfo = dinfo.view_index;
+ TEquals(prev_view_size, vinfo.disk_size, "view group disk size didn't change");
+ TEquals(false, vinfo.compact_running);
+ TEquals(prev_view_sig, vinfo.signature, 'ddoc sig');
+ // wait some time (there were issues where an update
+ // of the views had been triggered in the background)
+ var start = new Date().getTime();
+ while (new Date().getTime() < start + 2000);
+ TEquals(0, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view info');
+ TEquals(0, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view info');
+ TEquals(0, db.view("test/summate", {stale: "ok"}).rows.length, 'view info');
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ };
+
+ db.bulkSave(makeDocs(numDocs + 1, numDocs * 2 + 1));
+ T(db.ensureFullCommit().ok);
+
+ // open view group
+ db.view("test/summate", {stale: "ok"});
+ // wait so the views can get initialized
+ var start = new Date().getTime();
+ while (new Date().getTime() < start + 2000);
+
+ // test that POST /db/_view_cleanup
+ // doesn't trigger an update of the views
+ var len1 = db.view("test/all_docs_twice", {stale: "ok"}).total_rows;
+ var len2 = db.view("test/single_doc", {stale: "ok"}).total_rows;
+ var len3 = db.view("test/summate", {stale: "ok"}).rows.length;
+ for (i = 0; i < 2; i++) {
+ T(db.viewCleanup().ok);
+ // wait some time (there were issues where an update
+ // of the views had been triggered in the background)
+ start = new Date().getTime();
+ while (new Date().getTime() < start + 2000);
+ TEquals(len1, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view cleanup');
+ TEquals(len2, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view cleanup');
+ TEquals(len3, db.view("test/summate", {stale: "ok"}).rows.length, 'view cleanup');
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ // we'll test whether the view group stays closed
+ // and the views stay uninitialized (they should!)
+ len1 = len2 = len3 = 0;
+ };
+
+ // test commonjs in map functions
+ resp = db.view("test/commonjs", {limit:1});
+ T(resp.rows[0].value == 'ok');
+
+ // test that the _all_docs view returns correctly with keys
+ var results = db.allDocs({startkey:"_design", endkey:"_design0"});
+ T(results.rows.length == 1);
+
+ for (i = 0; i < 2; i++) {
+ var rows = db.view("test/all_docs_twice").rows;
+ for (var j = 0; j < numDocs; j++) {
+ T(rows[2 * j].key == (j + 1));
+ T(rows[(2 * j) + 1].key == (j + 1));
+ };
+ T(db.view("test/no_docs").total_rows == 0);
+ T(db.view("test/single_doc").total_rows == 1);
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ };
+
+ // test when language not specified, Javascript is implied
+ var designDoc2 = {
+ _id: "_design/test2",
+ // language: "javascript",
+ views: {
+ single_doc: {
+ map:
+ (function(doc) {
+ if (doc._id === "1") {
+ emit(1, null);
+ }
+ }).toString()
+ }
+ }
+ };
+
+ T(db.save(designDoc2).ok);
+ T(db.view("test2/single_doc").total_rows == 1);
+
+ var summate = function(N) {
+ return (N + 1) * (N / 2);
+ };
+ var result = db.view("test/summate");
+ T(result.rows[0].value == summate(numDocs * 2));
+
+ result = db.view("test/summate", {startkey: 4, endkey: 4});
+ T(result.rows[0].value == 4);
+
+ result = db.view("test/summate", {startkey: 4, endkey: 5});
+ T(result.rows[0].value == 9);
+
+ result = db.view("test/summate", {startkey: 4, endkey: 6});
+ T(result.rows[0].value == 15);
+
+ // test start_key and end_key aliases
+ result = db.view("test/summate", {start_key: 4, end_key: 6});
+ T(result.rows[0].value == 15);
+
+ // Verify that a shared index (view def is an exact copy of "summate")
+ // does not confuse the reduce stage
+ result = db.view("test/summate2", {startkey: 4, endkey: 6});
+ T(result.rows[0].value == 15);
+
+ for(i = 1; i < (numDocs / 2); i += 30) {
+ result = db.view("test/summate", {startkey: i, endkey: (numDocs - i)});
+ T(result.rows[0].value == summate(numDocs - i) - summate(i - 1));
+ }
+
+ T(db.deleteDoc(designDoc).ok);
+ T(db.open(designDoc._id) == null);
+ T(db.view("test/no_docs") == null);
+
+ T(db.ensureFullCommit().ok);
+ restartServer();
+ T(db.open(designDoc._id) == null);
+ T(db.view("test/no_docs") == null);
+
+ // trigger ddoc cleanup
+ T(db.viewCleanup().ok);
+ }; // enf of testFun
+
+ run_on_modified_server(server_config, testFun);
+
+ // COUCHDB-1227 - if a design document is deleted, by adding a "_deleted"
+ // field with the boolean value true, its validate_doc_update functions
+ // should no longer have effect.
+ db.deleteDb();
+ db.createDb();
+ var ddoc = {
+ _id: "_design/test",
+ language: "javascript",
+ validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
+ if (newDoc.value % 2 == 0) {
+ throw({forbidden: "dont like even numbers"});
+ }
+ return true;
+ }).toString()
+ };
+
+ TEquals(true, db.save(ddoc).ok);
+ try {
+ db.save({_id: "doc1", value: 4});
+ T(false, "doc insertion should have failed");
+ } catch (x) {
+ TEquals("forbidden", x.error);
+ }
+
+ var doc = db.open("doc1");
+ TEquals(null, doc);
+ ddoc._deleted = true;
+ TEquals(true, db.save(ddoc).ok);
+
+ try {
+ TEquals(true, db.save({_id: "doc1", value: 4}).ok);
+ } catch (x) {
+ T(false, "doc insertion should have succeeded");
+ }
+
+ doc = db.open("doc1");
+ TEquals(true, doc !== null, "doc was not persisted");
+ TEquals(4, doc.value);
+
+ // cleanup
+ db.deleteDb();
+ db2.deleteDb();
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_options.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/design_options.js b/test/javascript/tests/design_options.js
new file mode 100644
index 0000000..05764e2
--- /dev/null
+++ b/test/javascript/tests/design_options.js
@@ -0,0 +1,74 @@
+// 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.design_options = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ //// test the includes_design option
+ var map = "function (doc) {emit(null, doc._id);}";
+ var withseq = "function(doc) {emit(doc._local_seq, null)}"
+
+ // we need a design doc even to test temp views with it
+ var designDoc = {
+ _id:"_design/fu",
+ language: "javascript",
+ options: {
+ include_design: true,
+ local_seq: true
+ },
+ views: {
+ data: {"map": map},
+ with_seq : {"map" : withseq}
+ }
+ };
+ T(db.save(designDoc).ok);
+
+ // should work for temp views
+ var rows = db.query(map, null, {options:{include_design: true}}).rows;
+ T(rows.length == 1);
+ T(rows[0].value == "_design/fu");
+
+ rows = db.query(map).rows;
+ T(rows.length == 0);
+
+ // when true, should include design docs in views
+ rows = db.view("fu/data").rows;
+ T(rows.length == 1);
+ T(rows[0].value == "_design/fu");
+
+ // when false, should not
+ designDoc.options.include_design = false;
+ delete designDoc._rev;
+ designDoc._id = "_design/bingo";
+ T(db.save(designDoc).ok);
+ rows = db.view("bingo/data").rows;
+ T(rows.length == 0);
+
+ // should default to false
+ delete designDoc.options;
+ delete designDoc._rev;
+ designDoc._id = "_design/bango";
+ T(db.save(designDoc).ok);
+ rows = db.view("bango/data").rows;
+ T(rows.length == 0);
+
+ // should also have local_seq in the view
+ var resp = db.save({});
+ rows = db.view("fu/with_seq").rows;
+ T(rows[0].key == 1)
+ T(rows[1].key == 2)
+ var doc = db.open(resp.id);
+ db.deleteDoc(doc);
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/design_paths.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/design_paths.js b/test/javascript/tests/design_paths.js
new file mode 100644
index 0000000..426a252
--- /dev/null
+++ b/test/javascript/tests/design_paths.js
@@ -0,0 +1,72 @@
+// 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.design_paths = function(debug) {
+ if (debug) debugger;
+ var dbNames = ["test_suite_db", "test_suite_db/with_slashes"];
+ for (var i=0; i < dbNames.length; i++) {
+ var db = new CouchDB(dbNames[i]);
+ var dbName = encodeURIComponent(dbNames[i]);
+ db.deleteDb();
+ db.createDb();
+
+ // create a ddoc w bulk_docs
+ db.bulkSave([{
+ _id : "_design/test",
+ views : {
+ "testing" : {
+ "map" : "function(){emit(1,1)}"
+ }
+ }
+ }]);
+
+ // ddoc is getable
+ var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test");
+
+ // it's at 2 urls...
+ var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test");
+
+ // ensure that views are addressable
+ resp = db.view("test/testing")
+ T(resp.total_rows == 0)
+
+ // create a ddoc by putting to url with raw slash
+ var xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test2",{
+ body : JSON.stringify({
+ _id : "_design/test2",
+ views : {
+ "testing" : {
+ "map" : "function(){emit(1,1)}"
+ }
+ }
+ })
+ });
+
+ // ddoc is getable
+ var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test2");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test2");
+
+ // it's at 2 urls...
+ var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest2");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test2");
+
+ // ensure that views are addressable
+ resp = db.view("test2/testing");
+ T(resp.total_rows == 0);
+ };
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/erlang_views.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/erlang_views.js b/test/javascript/tests/erlang_views.js
new file mode 100644
index 0000000..c6bc5d7
--- /dev/null
+++ b/test/javascript/tests/erlang_views.js
@@ -0,0 +1,136 @@
+// 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.erlang_views = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+
+
+ run_on_modified_server(
+ [{section: "native_query_servers",
+ key: "erlang",
+ value: "{couch_native_process, start_link, []}"}],
+ function() {
+ // Note we just do some basic 'smoke tests' here - the
+ // test/query_server_spec.rb tests have more comprehensive tests
+ var doc = {_id: "1", integer: 1, string: "str1", array: [1, 2, 3]};
+ T(db.save(doc).ok);
+
+ var mfun = 'fun({Doc}) -> ' +
+ ' K = couch_util:get_value(<<"integer">>, Doc, null), ' +
+ ' V = couch_util:get_value(<<"string">>, Doc, null), ' +
+ ' Emit(K, V) ' +
+ 'end.';
+
+ // emitting a key value that is undefined should result in that row not
+ // being included in the view results
+ var results = db.query(mfun, null, null, null, "erlang");
+ T(results.total_rows == 1);
+ T(results.rows[0].key == 1);
+ T(results.rows[0].value == "str1");
+
+ // check simple reduction - another doc with same key.
+ var doc = {_id: "2", integer: 1, string: "str2"};
+ T(db.save(doc).ok);
+ rfun = 'fun' +
+ ' (_, Values, false) -> length(Values); ' +
+ ' (_, Values, true) -> lists:sum(Values) ' +
+ ' end.';
+ results = db.query(mfun, rfun, null, null, "erlang");
+ T(results.rows[0].value == 2);
+
+ // simple 'list' tests
+ var designDoc = {
+ _id:"_design/erlview",
+ language: "erlang",
+ shows: {
+ simple:
+ 'fun(Doc, {Req}) -> ' +
+ ' {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.'
+ },
+ lists: {
+ simple_list :
+ 'fun(Head, {Req}) -> ' +
+ ' Send(<<"head">>), ' +
+ ' Fun = fun({Row}, _) -> ' +
+ ' Val = couch_util:get_value(<<"value">>, Row, -1), ' +
+ ' Send(list_to_binary(integer_to_list(Val))), ' +
+ ' {ok, nil} ' +
+ ' end, ' +
+ ' {ok, _} = FoldRows(Fun, nil), ' +
+ ' <<"tail">> ' +
+ 'end. '
+ },
+ views: {
+ simple_view : {
+ map: mfun,
+ reduce: rfun
+ }
+ }
+ };
+ T(db.save(designDoc).ok);
+
+ var url = "/test_suite_db/_design/erlview/_show/simple/1";
+ var xhr = CouchDB.request("GET", url);
+ T(xhr.status == 200, "standard get should be 200");
+ T(xhr.responseText == "0 - GET");
+
+ var url = "/test_suite_db/_design/erlview/_list/simple_list/simple_view";
+ var xhr = CouchDB.request("GET", url);
+ T(xhr.status == 200, "standard get should be 200");
+ T(xhr.responseText == "head2tail");
+
+ // Larger dataset
+
+ db.deleteDb();
+ db.createDb();
+ var words = "foo bar abc def baz xxyz".split(/\s+/);
+
+ var docs = [];
+ for(var i = 0; i < 250; i++) {
+ var body = [];
+ for(var j = 0; j < 100; j++) {
+ body.push({
+ word: words[j%words.length],
+ count: j
+ });
+ }
+ docs.push({
+ "_id": "test-" + i,
+ "words": body
+ });
+ }
+ T(db.bulkSave(docs).length, 250, "Saved big doc set.");
+
+ var mfun = 'fun({Doc}) -> ' +
+ 'Words = couch_util:get_value(<<"words">>, Doc), ' +
+ 'lists:foreach(fun({Word}) -> ' +
+ 'WordString = couch_util:get_value(<<"word">>, Word), ' +
+ 'Count = couch_util:get_value(<<"count">>, Word), ' +
+ 'Emit(WordString , Count) ' +
+ 'end, Words) ' +
+ 'end.';
+
+ var rfun = 'fun(Keys, Values, RR) -> length(Values) end.';
+ var results = db.query(mfun, rfun, null, null, "erlang");
+ T(results.rows[0].key === null, "Returned a reduced value.");
+ T(results.rows[0].value > 0, "Reduce value exists.");
+ });
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/etags_head.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/etags_head.js b/test/javascript/tests/etags_head.js
new file mode 100644
index 0000000..63e2999
--- /dev/null
+++ b/test/javascript/tests/etags_head.js
@@ -0,0 +1,78 @@
+// 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.etags_head = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ var xhr;
+
+ // create a new doc
+ xhr = CouchDB.request("PUT", "/test_suite_db/1", {
+ body: "{}"
+ });
+ T(xhr.status == 201);
+
+ // extract the ETag header values
+ var etag = xhr.getResponseHeader("etag");
+
+ // get the doc and verify the headers match
+ xhr = CouchDB.request("GET", "/test_suite_db/1");
+ T(etag == xhr.getResponseHeader("etag"));
+
+ // 'head' the doc and verify the headers match
+ xhr = CouchDB.request("HEAD", "/test_suite_db/1", {
+ headers: {"if-none-match": "s"}
+ });
+ T(etag == xhr.getResponseHeader("etag"));
+
+ // replace a doc
+ xhr = CouchDB.request("PUT", "/test_suite_db/1", {
+ body: "{}",
+ headers: {"if-match": etag}
+ });
+ T(xhr.status == 201);
+
+ // extract the new ETag value
+ var etagOld= etag;
+ etag = xhr.getResponseHeader("etag");
+
+ // fail to replace a doc
+ xhr = CouchDB.request("PUT", "/test_suite_db/1", {
+ body: "{}"
+ });
+ T(xhr.status == 409);
+
+ // verify get w/Etag
+ xhr = CouchDB.request("GET", "/test_suite_db/1", {
+ headers: {"if-none-match": etagOld}
+ });
+ T(xhr.status == 200);
+ xhr = CouchDB.request("GET", "/test_suite_db/1", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // fail to delete a doc
+ xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
+ headers: {"if-match": etagOld}
+ });
+ T(xhr.status == 409);
+
+ //now do it for real
+ xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
+ headers: {"if-match": etag}
+ });
+ T(xhr.status == 200);
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/etags_views.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/etags_views.js b/test/javascript/tests/etags_views.js
new file mode 100644
index 0000000..6d8e97b
--- /dev/null
+++ b/test/javascript/tests/etags_views.js
@@ -0,0 +1,220 @@
+// 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.etags_views = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ var designDoc = {
+ _id: "_design/etags",
+ language: "javascript",
+ views : {
+ fooView: {
+ map: stringFun(function(doc) {
+ if (doc.foo) {
+ emit("bar", 1);
+ }
+ }),
+ },
+ basicView : {
+ map : stringFun(function(doc) {
+ if(doc.integer && doc.string) {
+ emit(doc.integer, doc.string);
+ }
+ })
+ },
+ withReduce : {
+ map : stringFun(function(doc) {
+ if(doc.integer && doc.string) {
+ emit(doc.integer, doc.string);
+ }
+ }),
+ reduce : stringFun(function(keys, values, rereduce) {
+ if (rereduce) {
+ return sum(values);
+ } else {
+ return values.length;
+ }
+ })
+ }
+ }
+ };
+ T(db.save(designDoc).ok);
+ db.bulkSave(makeDocs(0, 10));
+
+ var xhr;
+
+ // verify get w/Etag on map view
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ T(xhr.status == 200);
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // verify ETag doesn't change when an update
+ // doesn't change the view group's index
+ T(db.save({"_id":"doc1", "foo":"bar"}).ok);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 == etag);
+
+ // verify ETag always changes for include_docs=true on update
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView?include_docs=true");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(db.save({"_id":"doc2", "foo":"bar"}).ok);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView?include_docs=true");
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2);
+
+ // Verify that purges affect etags
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
+ var foo_etag = xhr.getResponseHeader("etag");
+ var doc1 = db.open("doc1");
+ xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
+ body: JSON.stringify({"doc1":[doc1._rev]})
+ });
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 != foo_etag);
+
+ // Test that _purge didn't affect the other view etags.
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 == etag);
+
+ // verify different views in the same view group may have different ETags
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
+ var etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2);
+
+ // verify ETag changes when an update changes the view group's index.
+ db.bulkSave(makeDocs(10, 20));
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 != etag);
+
+ // verify ETag is the same after a restart
+ restartServer();
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView");
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 == etag2);
+
+ // reduce view
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ T(xhr.status == 200);
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce",{
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // verify ETag doesn't change when an update
+ // doesn't change the view group's index
+ T(db.save({"_id":"doc3", "foo":"bar"}).ok);
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 == etag);
+ // purge
+ var doc3 = db.open("doc3");
+ xhr = CouchDB.request("POST", "/test_suite_db/_purge", {
+ body: JSON.stringify({"doc3":[doc3._rev]})
+ });
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 == etag);
+
+ // verify different views in the same view group may have different ETags
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/fooView");
+ var etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2);
+
+ // verify ETag changes when an update changes the view group's index
+ db.bulkSave(makeDocs(20, 30));
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ var etag1 = xhr.getResponseHeader("etag");
+ T(etag1 != etag);
+
+ // verify ETag is the same after a restart
+ restartServer();
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/withReduce");
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 == etag2);
+
+ // confirm ETag changes with different POST bodies
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/etags/_view/basicView",
+ {body: JSON.stringify({keys:[1]})}
+ );
+ var etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/etags/_view/basicView",
+ {body: JSON.stringify({keys:[2]})}
+ );
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2, "POST to map view generates key-depdendent ETags");
+
+ xhr = CouchDB.request("POST",
+ "/test_suite_db/_design/etags/_view/withReduce?group=true",
+ {body: JSON.stringify({keys:[1]})}
+ );
+ etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("POST",
+ "/test_suite_db/_design/etags/_view/withReduce?group=true",
+ {body: JSON.stringify({keys:[2]})}
+ );
+ etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2, "POST to reduce view generates key-depdendent ETags");
+
+ // all docs
+ xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
+ T(xhr.status == 200);
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_all_docs", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // _changes
+ xhr = CouchDB.request("GET", "/test_suite_db/_changes");
+ T(xhr.status == 200);
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_changes", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // list etag
+ // in the list test for now
+
+ // A new database should have unique _all_docs etags.
+ db.deleteDb();
+ db.createDb();
+ db.save({a: 1});
+ xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
+ var etag = xhr.getResponseHeader("etag");
+ db.deleteDb();
+ db.createDb();
+ db.save({a: 2});
+ xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
+ var new_etag = xhr.getResponseHeader("etag");
+ T(etag != new_etag);
+ // but still be cacheable
+ xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
+ T(new_etag == xhr.getResponseHeader("etag"));
+
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/form_submit.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/form_submit.js b/test/javascript/tests/form_submit.js
new file mode 100644
index 0000000..710bf47
--- /dev/null
+++ b/test/javascript/tests/form_submit.js
@@ -0,0 +1,25 @@
+// 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.
+
+// Do some basic tests.
+couchTests.form_submit = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+
+ var json = "{}";
+ var xhr = CouchDB.request("POST", "/test_suite_db/baz", {body: json});
+ T(xhr.status == 415);
+ result = JSON.parse(xhr.responseText);
+ T(result.error, "bad_content_type");
+ T(result.reason, "Invalid Content-Type header for form upload");
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/http.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/http.js b/test/javascript/tests/http.js
new file mode 100644
index 0000000..21c42a0
--- /dev/null
+++ b/test/javascript/tests/http.js
@@ -0,0 +1,79 @@
+// 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.http = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+
+ // bug COUCHDB-100: DELETE on non-existent DB returns 500 instead of 404
+ db.deleteDb();
+
+ db.createDb();
+
+ // PUT on existing DB should return 412 instead of 500
+ if (debug) debugger;
+
+ var xhr = CouchDB.request("PUT", "/test_suite_db/test", {body: "{}"});
+ var host = CouchDB.host;
+
+ TEquals(CouchDB.protocol + host + "/test_suite_db/test",
+ xhr.getResponseHeader("Location"),
+ "should include ip address");
+
+ xhr = CouchDB.request("PUT", "/test_suite_db/test2", {
+ body: "{}",
+ headers: {"X-Forwarded-Host": "mysite.com"}
+ });
+
+ TEquals(CouchDB.protocol + "mysite.com/test_suite_db/test2",
+ xhr.getResponseHeader("Location"),
+ "should include X-Forwarded-Host");
+
+ run_on_modified_server([{
+ section:"httpd",
+ key:"x_forwarded_host",
+ value:"X-Host"}],
+ function() {
+ xhr = CouchDB.request("PUT", "/test_suite_db/test3", {
+ body: "{}",
+ headers: {"X-Host": "mysite2.com"}
+ });
+ TEquals(CouchDB.protocol + "mysite2.com/test_suite_db/test3",
+ xhr.getResponseHeader("Location"),
+ "should include X-Host");
+ });
+
+ // COUCHDB-708: newlines document names
+ xhr = CouchDB.request("PUT", "/test_suite_db/docid%0A/attachment.txt", {
+ headers: {"Content-Type": "text/plain;charset=utf-8"},
+ body: ""
+ });
+ TEquals(CouchDB.protocol + host + "/test_suite_db/docid%0A/attachment.txt",
+ xhr.getResponseHeader("Location"),
+ "should work with newlines in document names for attachments");
+
+ xhr = CouchDB.request("PUT", "/test_suite_db/docidtest%0A", {
+ body: JSON.stringify({"foo": "bar"}),
+ headers: {"Content-Type": "application/json"}
+ });
+ TEquals(CouchDB.protocol + host + "/test_suite_db/docidtest%0A",
+ xhr.getResponseHeader("Location"),
+ "should work with newlines in document names");
+
+ xhr = CouchDB.request("POST", "/test_suite_db/", {
+ body: JSON.stringify({"_id": "docidtestpost%0A"}),
+ headers: {"Content-Type": "application/json"}
+ });
+ TEquals(CouchDB.protocol + host + "/test_suite_db/docidtestpost%250A",
+ xhr.getResponseHeader("Location"),
+ "should work with newlines in document names");
+}
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/invalid_docids.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/invalid_docids.js b/test/javascript/tests/invalid_docids.js
new file mode 100644
index 0000000..d0195b0
--- /dev/null
+++ b/test/javascript/tests/invalid_docids.js
@@ -0,0 +1,77 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.invalid_docids = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ // Test _local explicitly first.
+ T(db.save({"_id": "_local/foo"}).ok);
+ T(db.open("_local/foo")._id == "_local/foo");
+
+ var urls = [
+ "/test_suite_db/_local",
+ "/test_suite_db/_local/",
+ "/test_suite_db/_local%2F",
+ "/test_suite_db/_local/foo/bar",
+ ];
+
+ urls.forEach(function(u) {
+ var res = db.request("PUT", u, {"body": "{}"});
+ T(res.status == 400);
+ T(JSON.parse(res.responseText).error == "bad_request");
+ });
+
+ //Test non-string
+ try {
+ db.save({"_id": 1});
+ T(1 == 0, "doc id must be string");
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "bad_request");
+ }
+
+ // Via PUT with _id not in body.
+ var res = res = db.request("PUT", "/test_suite_db/_other", {"body": "{}"});
+ T(res.status == 400);
+ T(JSON.parse(res.responseText).error == "bad_request");
+
+ // Accidental POST to form handling code.
+ res = db.request("POST", "/test_suite_db/_tmp_view", {"body": "{}"});
+ T(res.status == 400);
+ T(JSON.parse(res.responseText).error == "bad_request");
+
+ // Test invalid _prefix
+ try {
+ db.save({"_id": "_invalid"});
+ T(1 == 0, "doc id may not start with underscore");
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "bad_request");
+ }
+
+ // Test _bulk_docs explicitly.
+ var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}];
+ db.bulkSave(docs);
+ docs.forEach(function(d) {T(db.open(d._id)._id == d._id);});
+
+ docs = [{"_id": "_invalid"}];
+ try {
+ db.bulkSave(docs);
+ T(1 == 0, "doc id may not start with underscore, even in bulk docs");
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "bad_request");
+ }
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/jsonp.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/jsonp.js b/test/javascript/tests/jsonp.js
new file mode 100644
index 0000000..e4f4490
--- /dev/null
+++ b/test/javascript/tests/jsonp.js
@@ -0,0 +1,84 @@
+// 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.
+
+// Verify callbacks ran
+var jsonp_flag = 0;
+
+// Callbacks
+function jsonp_no_chunk(doc) {
+ T(jsonp_flag == 0);
+ T(doc._id == "0");
+ jsonp_flag = 1;
+}
+
+function jsonp_chunk(doc) {
+ T(jsonp_flag == 0);
+ T(doc.total_rows == 1);
+ jsonp_flag = 1;
+}
+
+// Do some jsonp tests.
+couchTests.jsonp = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ 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);
+
+ run_on_modified_server(
+ [{section: "httpd",
+ key: "allow_jsonp",
+ value: "true"}],
+ function() {
+
+ // Test unchunked callbacks.
+ var xhr = CouchDB.request("GET", "/test_suite_db/0?callback=jsonp_no_chunk");
+ TEquals("application/javascript", xhr.getResponseHeader("Content-Type"));
+ 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);}"}
+ }
+ };
+ T(db.save(designDoc).ok);
+
+ var url = "/test_suite_db/_design/test/_view/all_docs?callback=jsonp_chunk";
+ xhr = CouchDB.request("GET", url);
+ TEquals("application/javascript", xhr.getResponseHeader("Content-Type"));
+ T(xhr.status == 200);
+ jsonp_flag = 0;
+ eval(xhr.responseText);
+ T(jsonp_flag == 1);
+ xhr = CouchDB.request("GET", url + "\'");
+ T(xhr.status == 400);
+ });
+
+
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/large_docs.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/large_docs.js b/test/javascript/tests/large_docs.js
new file mode 100644
index 0000000..b84648b
--- /dev/null
+++ b/test/javascript/tests/large_docs.js
@@ -0,0 +1,33 @@
+// 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.large_docs = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ var longtext = "0123456789\n";
+
+ for (var i=0; i<10; i++) {
+ longtext = longtext + longtext
+ }
+ T(db.save({"longtest":longtext}).ok);
+ T(db.save({"longtest":longtext}).ok);
+ T(db.save({"longtest":longtext}).ok);
+ T(db.save({"longtest":longtext}).ok);
+
+ // query all documents, and return the doc.foo member as a key.
+ results = db.query(function(doc){
+ emit(null, doc.longtest);
+ });
+};
http://git-wip-us.apache.org/repos/asf/couchdb/blob/6a4893aa/test/javascript/tests/list_views.js
----------------------------------------------------------------------
diff --git a/test/javascript/tests/list_views.js b/test/javascript/tests/list_views.js
new file mode 100644
index 0000000..ece81ea
--- /dev/null
+++ b/test/javascript/tests/list_views.js
@@ -0,0 +1,492 @@
+// 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.list_views = function(debug) {
+ var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ var designDoc = {
+ _id:"_design/lists",
+ language: "javascript",
+ views : {
+ basicView : {
+ map : stringFun(function(doc) {
+ emit(doc.integer, doc.string);
+ })
+ },
+ withReduce : {
+ map : stringFun(function(doc) {
+ emit(doc.integer, doc.string);
+ }),
+ reduce : stringFun(function(keys, values, rereduce) {
+ if (rereduce) {
+ return sum(values);
+ } else {
+ return values.length;
+ }
+ })
+ }
+ },
+ lists: {
+ basicBasic : stringFun(function(head, req) {
+ send("head");
+ var row;
+ while(row = getRow()) {
+ log("row: "+toJSON(row));
+ send(row.key);
+ };
+ return "tail";
+ }),
+ basicJSON : stringFun(function(head, req) {
+ start({"headers":{"Content-Type" : "application/json"}});
+ send('{"head":'+toJSON(head)+', ');
+ send('"req":'+toJSON(req)+', ');
+ send('"rows":[');
+ var row, sep = '';
+ while (row = getRow()) {
+ send(sep + toJSON(row));
+ sep = ', ';
+ }
+ return "]}";
+ }),
+ 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>';
+ }),
+ acceptSwitch: stringFun(function(head, req) {
+ // respondWith takes care of setting the proper headers
+ provides("html", function() {
+ send("HTML <ul>");
+
+ var row, num = 0;
+ while (row = getRow()) {
+ num ++;
+ send('\n<li>Key: '
+ +row.key+' Value: '+row.value
+ +' LineNo: '+num+'</li>');
+ }
+
+ // tail
+ return '</ul>';
+ });
+ }),
+ qsParams: stringFun(function(head, req) {
+ return toJSON(req.query) + "\n";
+ }),
+ stopIter: stringFun(function(req) {
+ send("head");
+ var row, row_number = 0;
+ while(row = getRow()) {
+ if(row_number > 2) break;
+ send(" " + row_number);
+ row_number += 1;
+ };
+ return " tail";
+ }),
+ stopIter2: stringFun(function(head, req) {
+ provides("html", function() {
+ send("head");
+ var row, row_number = 0;
+ while(row = getRow()) {
+ if(row_number > 2) break;
+ send(" " + row_number);
+ row_number += 1;
+ };
+ return " tail";
+ });
+ }),
+ tooManyGetRows : stringFun(function() {
+ send("head");
+ var row;
+ while(row = getRow()) {
+ send(row.key);
+ };
+ getRow();
+ getRow();
+ getRow();
+ row = getRow();
+ return "after row: "+toJSON(row);
+ }),
+ emptyList: stringFun(function() {
+ return " ";
+ }),
+ rowError : stringFun(function(head, req) {
+ send("head");
+ var row = getRow();
+ send(fooBarBam); // intentional error
+ return "tail";
+ }),
+ docReference : stringFun(function(head, req) {
+ send("head");
+ var row = getRow();
+ send(row.doc.integer);
+ return "tail";
+ }),
+ secObj: stringFun(function(head, req) {
+ return toJSON(req.secObj);
+ }),
+ setHeaderAfterGotRow: stringFun(function(head, req) {
+ getRow();
+ start({
+ code: 400,
+ headers: {
+ "X-My-Header": "MyHeader"
+ }
+ });
+ send("bad request");
+ }),
+ allDocs: stringFun(function(head, req){
+ start({'headers': {'Content-Type': 'application/json'}});
+ var resp = head;
+ var rows = [];
+ while(row=getRow()){
+ rows.push(row);
+ }
+ resp.rows = rows;
+ return toJSON(resp);
+ })
+ }
+ };
+ var viewOnlyDesignDoc = {
+ _id:"_design/views",
+ language: "javascript",
+ views : {
+ basicView : {
+ map : stringFun(function(doc) {
+ emit(-doc.integer, doc.string);
+ })
+ }
+ }
+ };
+ var erlListDoc = {
+ _id: "_design/erlang",
+ language: "erlang",
+ lists: {
+ simple:
+ 'fun(Head, {Req}) -> ' +
+ ' Send(<<"[">>), ' +
+ ' Fun = fun({Row}, Sep) -> ' +
+ ' Val = couch_util:get_value(<<"key">>, Row, 23), ' +
+ ' Send(list_to_binary(Sep ++ integer_to_list(Val))), ' +
+ ' {ok, ","} ' +
+ ' end, ' +
+ ' {ok, _} = FoldRows(Fun, ""), ' +
+ ' Send(<<"]">>) ' +
+ 'end.'
+ }
+ };
+
+ T(db.save(designDoc).ok);
+
+ var docs = makeDocs(0, 10);
+ db.bulkSave(docs);
+
+ var view = db.view('lists/basicView');
+ T(view.total_rows == 10);
+
+ // standard get
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
+ T(xhr.status == 200, "standard get should be 200");
+ T(/head0123456789tail/.test(xhr.responseText));
+
+ // standard options
+ var xhr = CouchDB.request("OPTIONS", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
+ T(xhr.status == 200, "standard get should be 200");
+ T(/head0123456789tail/.test(xhr.responseText));
+
+ // test that etags are available
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // confirm ETag changes with different POST bodies
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
+ {body: JSON.stringify({keys:[1]})}
+ );
+ var etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
+ {body: JSON.stringify({keys:[2]})}
+ );
+ var etag2 = xhr.getResponseHeader("etag");
+ 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?update_seq=true");
+ T(xhr.status == 200, "standard get should be 200");
+ var resp = JSON.parse(xhr.responseText);
+ 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"});
+
+ TEquals(resp.req.info.db_name, "test_suite_db");
+ TEquals(resp.req.method, "GET");
+ TEquals(resp.req.path, [
+ "test_suite_db",
+ "_design",
+ "lists",
+ "_list",
+ "basicJSON",
+ "basicView"
+ ]);
+ T(resp.req.headers.Accept);
+ T(resp.req.headers.Host);
+ T(resp.req.headers["User-Agent"]);
+ T(resp.req.cookie);
+ TEquals("/test_suite_db/_design/lists/_list/basicJSON/basicView?update_seq=true",
+ resp.req.raw_path, "should include raw path");
+
+ // get with query params
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/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));
+
+ // with 0 rows
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
+ T(xhr.status == 200, "0 rows");
+ T(/<\/ul>/.test(xhr.responseText));
+
+ //too many Get Rows
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/tooManyGetRows/basicView");
+ T(xhr.status == 200, "tooManyGetRows");
+ T(/9after row: null/.test(xhr.responseText));
+
+
+ // reduce with 0 rows
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30");
+ T(xhr.status == 200, "reduce 0 rows");
+ T(/LastKey: undefined/.test(xhr.responseText));
+
+ // when there is a reduce present, but not used
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");
+ T(xhr.status == 200, "reduce false");
+ T(/Key: 1/.test(xhr.responseText));
+
+
+ // when there is a reduce present, and used
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true");
+ T(xhr.status == 200, "group reduce");
+ T(/Key: 1/.test(xhr.responseText));
+
+ // there should be etags on reduce as well
+ var etag = xhr.getResponseHeader("etag");
+ T(etag, "Etags should be served with reduce lists");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
+
+ // confirm ETag changes with different POST bodies
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
+ {body: JSON.stringify({keys:[1]})}
+ );
+ var etag1 = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
+ {body: JSON.stringify({keys:[2]})}
+ );
+ var etag2 = xhr.getResponseHeader("etag");
+ T(etag1 != etag2, "POST to reduce _list generates key-depdendent ETags");
+
+ // verify the etags expire correctly
+ var docs = makeDocs(11, 12);
+ db.bulkSave(docs);
+
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 200, "reduce etag");
+
+ // empty list
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/basicView");
+ T(xhr.responseText.match(/^ $/));
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/withReduce?group=true");
+ T(xhr.responseText.match(/^ $/));
+
+ // multi-key fetch
+ var xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/basicView", {
+ body: '{"keys":[2,4,5,7]}'
+ });
+ T(xhr.status == 200, "multi key");
+ T(!(/Key: 1 /.test(xhr.responseText)));
+ T(/Key: 2/.test(xhr.responseText));
+ T(/FirstKey: 2/.test(xhr.responseText));
+ T(/LastKey: 7/.test(xhr.responseText));
+
+ // multi-key fetch with GET
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView" +
+ "?keys=[2,4,5,7]");
+
+ T(xhr.status == 200, "multi key");
+ T(!(/Key: 1 /.test(xhr.responseText)));
+ T(/Key: 2/.test(xhr.responseText));
+ T(/FirstKey: 2/.test(xhr.responseText));
+ T(/LastKey: 7/.test(xhr.responseText));
+
+ // no multi-key fetch allowed when group=false
+ xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=false", {
+ body: '{"keys":[2,4,5,7]}'
+ });
+ T(xhr.status == 400);
+ T(/query_parse_error/.test(xhr.responseText));
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/rowError/basicView");
+ T(/ReferenceError/.test(xhr.responseText));
+
+
+ // with include_docs and a reference to the doc.
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/docReference/basicView?include_docs=true");
+ T(xhr.responseText.match(/head0tail/));
+
+ // now with extra qs params
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam");
+ T(xhr.responseText.match(/blam/));
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
+ // 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", {
+ 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", {
+ headers : {
+ "Accept" : "text/html"
+ }
+ });
+ T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop 2");
+
+ // with accept headers for HTML
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
+ headers: {
+ "Accept": 'text/html'
+ }
+ });
+ T(xhr.getResponseHeader("Content-Type") == "text/html; charset=utf-8");
+ T(xhr.responseText.match(/HTML/));
+ T(xhr.responseText.match(/Value/));
+
+ // Test we can run lists and views from separate docs.
+ T(db.save(viewOnlyDesignDoc).ok);
+ var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView" +
+ "?startkey=-3";
+ xhr = CouchDB.request("GET", url);
+ T(xhr.status == 200, "multiple design docs.");
+ T(!(/Key: -4/.test(xhr.responseText)));
+ T(/FirstKey: -3/.test(xhr.responseText));
+ 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";
+ xhr = CouchDB.request("POST", url, {
+ body: '{"keys":[-2,-4,-5,-7]}'
+ });
+
+ T(xhr.status == 200, "multi key separate docs");
+ T(!(/Key: -3/.test(xhr.responseText)));
+ T(/Key: -7/.test(xhr.responseText));
+ T(/FirstKey: -2/.test(xhr.responseText));
+ T(/LastKey: -7/.test(xhr.responseText));
+
+ // Test if secObj is available
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/secObj/basicView");
+ T(xhr.status == 200, "standard get should be 200");
+ var resp = JSON.parse(xhr.responseText);
+ T(typeof(resp) == "object");
+
+ var erlViewTest = function() {
+ T(db.save(erlListDoc).ok);
+ var url = "/test_suite_db/_design/erlang/_list/simple/views/basicView" +
+ "?startkey=-3";
+ xhr = CouchDB.request("GET", url);
+ T(xhr.status == 200, "multiple languages in design docs.");
+ var list = JSON.parse(xhr.responseText);
+ T(list.length == 4);
+ for(var i = 0; i < list.length; i++)
+ {
+ T(list[i] + 3 == i);
+ }
+ };
+
+ run_on_modified_server([{
+ section: "native_query_servers",
+ key: "erlang",
+ value: "{couch_native_process, start_link, []}"
+ }], erlViewTest);
+
+ // COUCHDB-1113
+ var ddoc = {
+ _id: "_design/test",
+ views: {
+ me: {
+ map: (function(doc) { emit(null,null)}).toString()
+ }
+ },
+ lists: {
+ you: (function(head, req) {
+ var row;
+ while(row = getRow()) {
+ send(row);
+ }
+ }).toString()
+ }
+ };
+ db.save(ddoc);
+
+ var resp = CouchDB.request("GET", "/" + db.name + "/_design/test/_list/you/me", {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ }
+ });
+ TEquals(200, resp.status, "should return a 200 response");
+
+ // TEST HTTP header response set after getRow() called in _list function.
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/setHeaderAfterGotRow/basicView");
+ T(xhr.status == 400);
+ T(xhr.getResponseHeader("X-My-Header") == "MyHeader");
+ T(xhr.responseText.match(/^bad request$/));
+
+ // test handling _all_docs by _list functions. the result should be equal
+ var xhr_lAllDocs = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/allDocs/_all_docs");
+ T(xhr_lAllDocs.status == 200, "standard get should be 200");
+ var xhr_allDocs = CouchDB.request("GET", "/test_suite_db/_all_docs");
+ var allDocs = JSON.parse(xhr_allDocs.responseText);
+ var lAllDocs = JSON.parse(xhr_lAllDocs.responseText);
+ TEquals(allDocs.total_rows, lAllDocs.total_rows, "total_rows mismatch");
+ TEquals(allDocs.offset, lAllDocs.offset, "offset mismatch");
+ TEquals(allDocs.rows.length, lAllDocs.rows.length, "amount of rows mismatch");
+ TEquals(allDocs.rows, lAllDocs.rows, "rows mismatch");
+};