Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 62352C809 for ; Wed, 10 Dec 2014 11:08:10 +0000 (UTC) Received: (qmail 40763 invoked by uid 500); 10 Dec 2014 11:08:09 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 40594 invoked by uid 500); 10 Dec 2014 11:08:09 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 40078 invoked by uid 99); 10 Dec 2014 11:08:09 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Dec 2014 11:08:09 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 3E447A23216; Wed, 10 Dec 2014 11:08:09 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: kxepal@apache.org To: commits@couchdb.apache.org Date: Wed, 10 Dec 2014 11:08:16 -0000 Message-Id: <1af6c98db000482c8089bda1c4e942eb@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [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 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 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('
    '); + var row, row_number = 0, prevKey, firstKey = null; + while (row = getRow()) { + row_number += 1; + if (!firstKey) firstKey = row.key; + prevKey = row.key; + send('\n
  • Key: '+row.key + +' Value: '+row.value + +' LineNo: '+row_number+'
  • '); + } + return '

FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'

'; + }), + acceptSwitch: stringFun(function(head, req) { + // respondWith takes care of setting the proper headers + provides("html", function() { + send("HTML
    "); + + var row, num = 0; + while (row = getRow()) { + num ++; + send('\n
  • Key: ' + +row.key+' Value: '+row.value + +' LineNo: '+num+'
  • '); + } + + // tail + return '
'; + }); + }), + 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"); +};