diff options
Diffstat (limited to 'rel/overlay/share/www/script')
38 files changed, 1861 insertions, 383 deletions
diff --git a/rel/overlay/share/www/script/couch.js b/rel/overlay/share/www/script/couch.js index ca860bd5..6a997cff 100644 --- a/rel/overlay/share/www/script/couch.js +++ b/rel/overlay/share/www/script/couch.js @@ -272,7 +272,7 @@ function CouchDB(name, httpHeaders) { for (var name in options) { if (!options.hasOwnProperty(name)) { continue; }; var value = options[name]; - if (name == "key" || name == "startkey" || name == "endkey") { + if (name == "key" || name == "keys" || name == "startkey" || name == "endkey") { value = toJSON(value); } buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); @@ -402,7 +402,7 @@ CouchDB.request = function(method, uri, options) { options.headers["Content-Type"] = options.headers["Content-Type"] || options.headers["content-type"] || "application/json"; options.headers["Accept"] = options.headers["Accept"] || options.headers["accept"] || "application/json"; var req = CouchDB.newXhr(); - if(uri.substr(0, "http://".length) != "http://") { + if(uri.substr(0, CouchDB.protocol.length) != CouchDB.protocol) { uri = CouchDB.urlPrefix + uri; } req.open(method, uri, false); diff --git a/rel/overlay/share/www/script/couch_tests.js b/rel/overlay/share/www/script/couch_tests.js index 896b3538..eb573526 100644 --- a/rel/overlay/share/www/script/couch_tests.js +++ b/rel/overlay/share/www/script/couch_tests.js @@ -13,10 +13,12 @@ // Used by replication test if (typeof window == 'undefined' || !window) { CouchDB.host = "127.0.0.1:5984"; + CouchDB.protocol = "http://"; CouchDB.inBrowser = false; } else { CouchDB.host = window.location.host; CouchDB.inBrowser = true; + CouchDB.protocol = window.location.protocol + "//"; } CouchDB.urlPrefix = ".."; @@ -35,6 +37,7 @@ loadTest("attachments_multipart.js"); loadTest("attachment_conflicts.js"); loadTest("attachment_names.js"); loadTest("attachment_paths.js"); +loadTest("attachment_ranges.js"); loadTest("attachment_views.js"); loadTest("auth_cache.js"); loadTest("batch_save.js"); @@ -74,6 +77,7 @@ loadTest("reduce_builtin.js"); loadTest("reduce_false.js"); loadTest("reduce_false_temp.js"); loadTest("replication.js"); +loadTest("replicator_db.js"); loadTest("rev_stemming.js"); loadTest("rewrite.js"); loadTest("security_validation.js"); diff --git a/rel/overlay/share/www/script/futon.browse.js b/rel/overlay/share/www/script/futon.browse.js index a3f6e8cb..b981feec 100644 --- a/rel/overlay/share/www/script/futon.browse.js +++ b/rel/overlay/share/www/script/futon.browse.js @@ -682,10 +682,12 @@ key = $.futon.formatJSON(row.key, {indent: 0, linesep: ""}); } if (row.id) { - $("<td class='key'><a href='document.html?" + encodeURIComponent(db.name) + - "/" + $.couch.encodeDocId(row.id) + "'><strong></strong><br>" + - "<span class='docid'>ID: " + $.futon.escape(row.id) + "</span></a></td>") - .find("strong").text(key).end() + key = key.replace(/\\"/, '"'); + var rowlink = encodeURIComponent(db.name) + + "/" + $.couch.encodeDocId(row.id); + $("<td class='key'><a href=\"document.html?" + rowlink + "\"><strong>" + + $.futon.escape(key) + "</strong><br>" + + "<span class='docid'>ID: " + $.futon.escape(row.id) + "</span></a></td>") .appendTo(tr); } else { $("<td class='key'><strong></strong></td>") diff --git a/rel/overlay/share/www/script/futon.format.js b/rel/overlay/share/www/script/futon.format.js index 8d9b7f5c..0eb9b104 100644 --- a/rel/overlay/share/www/script/futon.format.js +++ b/rel/overlay/share/www/script/futon.format.js @@ -17,8 +17,8 @@ return string.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") - .replace(/"/, """) - .replace(/'/, "'") + .replace(/"/g, """) + .replace(/'/g, "'") ; }, diff --git a/rel/overlay/share/www/script/futon.js b/rel/overlay/share/www/script/futon.js index c4647ed1..fb73e3c9 100644 --- a/rel/overlay/share/www/script/futon.js +++ b/rel/overlay/share/www/script/futon.js @@ -68,6 +68,10 @@ function $$(node) { callback({name: "Please enter a name."}); return false; }; + return validatePassword(data, callback); + }; + + function validatePassword(data, callback) { if (!data.password || data.password.length == 0) { callback({password: "Please enter a password."}); return false; @@ -129,11 +133,61 @@ function $$(node) { return false; }; + function changePassword () { + $.showDialog("dialog/_change_password.html", { + submit: function(data, callback) { + if (validatePassword(data, callback)) { + if (data.password != data.verify_password) { + callback({verify_password: "Passwords don't match."}); + return false; + } + } else { + return false; + } + $.couch.session({success: function (resp) { + if (resp.userCtx.roles.indexOf("_admin") > -1) { + $.couch.config({ + success : function () { + doLogin(resp.userCtx.name, data.password, function(errors) { + if(!$.isEmptyObject(errors)) { + callback(errors); + return; + } else { + location.reload(); + } + }); + } + }, "admins", resp.userCtx.name, data.password); + } else { + $.couch.db(resp.info.authentication_db).openDoc("org.couchdb.user:"+resp.userCtx.name, { + success: function (user) { + $.couch.db(resp.info.authentication_db).saveDoc($.couch.prepareUserDoc(user, data.password), { + success: function() { + doLogin(user.name, data.password, function(errors) { + if(!$.isEmptyObject(errors)) { + callback(errors); + return; + } else { + location.reload(); + } + }); + } + }); + } + }); + } + }}); + } + }); + return false; + }; + this.setupSidebar = function() { $("#userCtx .login").click(login); $("#userCtx .logout").click(logout); $("#userCtx .signup").click(signup); $("#userCtx .createadmin").click(createAdmin); + $("#userCtx .changepass").click(changePassword); }; this.sidebar = function() { @@ -146,6 +200,7 @@ function $$(node) { if (userCtx.name) { $("#userCtx .name").text(userCtx.name).attr({href : $.couch.urlPrefix + "/_utils/document.html?"+encodeURIComponent(r.info.authentication_db)+"/org.couchdb.user%3A"+encodeURIComponent(userCtx.name)}); if (userCtx.roles.indexOf("_admin") != -1) { + $("#userCtx .loggedin").show(); $("#userCtx .loggedinadmin").show(); } else { $("#userCtx .loggedin").show(); diff --git a/rel/overlay/share/www/script/jquery.couch.js b/rel/overlay/share/www/script/jquery.couch.js index 114e5801..634a03fe 100644 --- a/rel/overlay/share/www/script/jquery.couch.js +++ b/rel/overlay/share/www/script/jquery.couch.js @@ -22,25 +22,6 @@ return encodeURIComponent(docID); }; - function prepareUserDoc(user_doc, new_password) { - if (typeof hex_sha1 == "undefined") { - alert("creating a user doc requires sha1.js to be loaded in the page"); - return; - } - var user_prefix = "org.couchdb.user:"; - user_doc._id = user_doc._id || user_prefix + user_doc.name; - if (new_password) { - // handle the password crypto - user_doc.salt = $.couch.newUUID(); - user_doc.password_sha = hex_sha1(new_password + user_doc.salt); - } - user_doc.type = "user"; - if (!user_doc.roles) { - user_doc.roles = []; - } - return user_doc; - }; - var uuidCache = []; $.extend($.couch, { @@ -87,8 +68,11 @@ options = options || {}; $.ajax({ type: "GET", url: this.urlPrefix + "/_session", + beforeSend: function(xhr) { + xhr.setRequestHeader('Accept', 'application/json'); + }, complete: function(req) { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { @@ -112,19 +96,41 @@ signup: function(user_doc, password, options) { options = options || {}; // prepare user doc based on name and password - user_doc = prepareUserDoc(user_doc, password); + user_doc = this.prepareUserDoc(user_doc, password); $.couch.userDb(function(db) { db.saveDoc(user_doc, options); }); }, - + + prepareUserDoc: function(user_doc, new_password) { + if (typeof hex_sha1 == "undefined") { + alert("creating a user doc requires sha1.js to be loaded in the page"); + return; + } + var user_prefix = "org.couchdb.user:"; + user_doc._id = user_doc._id || user_prefix + user_doc.name; + if (new_password) { + // handle the password crypto + user_doc.salt = $.couch.newUUID(); + user_doc.password_sha = hex_sha1(new_password + user_doc.salt); + } + user_doc.type = "user"; + if (!user_doc.roles) { + user_doc.roles = []; + } + return user_doc; + }, + login: function(options) { options = options || {}; $.ajax({ type: "POST", url: this.urlPrefix + "/_session", dataType: "json", data: {name: options.name, password: options.password}, + beforeSend: function(xhr) { + xhr.setRequestHeader('Accept', 'application/json'); + }, complete: function(req) { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { @@ -140,8 +146,11 @@ $.ajax({ type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json", username : "_", password : "_", + beforeSend: function(xhr) { + xhr.setRequestHeader('Accept', 'application/json'); + }, complete: function(req) { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); if (req.status == 200) { if (options.success) options.success(resp); } else if (options.error) { @@ -385,7 +394,7 @@ dataType: "json", data: toJSON(doc), beforeSend : beforeSend, complete: function(req) { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); if (req.status == 200 || req.status == 201 || req.status == 202) { doc._id = resp.id; doc._rev = resp.rev; @@ -450,7 +459,7 @@ copyDoc: function(docId, options, ajaxOptions) { ajaxOptions = $.extend(ajaxOptions, { complete: function(req) { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); if (req.status == 201) { if (options.success) options.success(resp); } else if (options.error) { @@ -563,7 +572,7 @@ replicate: function(source, target, ajaxOptions, repOpts) { repOpts = $.extend({source: source, target: target}, repOpts); - if (repOpts.continuous) { + if (repOpts.continuous && !repOpts.cancel) { ajaxOptions.successStatus = 202; } ajax({ @@ -593,9 +602,36 @@ } }); + var httpData = $.httpData || function( xhr, type, s ) { // lifted from jq1.4.4 + var ct = xhr.getResponseHeader("content-type") || "", + xml = type === "xml" || !type && ct.indexOf("xml") >= 0, + data = xml ? xhr.responseXML : xhr.responseText; + + if ( xml && data.documentElement.nodeName === "parsererror" ) { + $.error( "parsererror" ); + } + if ( s && s.dataFilter ) { + data = s.dataFilter( data, type ); + } + if ( typeof data === "string" ) { + if ( type === "json" || !type && ct.indexOf("json") >= 0 ) { + data = $.parseJSON( data ); + } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) { + $.globalEval( data ); + } + } + return data; + }; + function ajax(obj, options, errorMessage, ajaxOptions) { + + var defaultAjaxOpts = { + contentType: "application/json", + headers:{"Accept": "application/json"} + }; + options = $.extend({successStatus: 200}, options); - ajaxOptions = $.extend({contentType: "application/json"}, ajaxOptions); + ajaxOptions = $.extend(defaultAjaxOpts, ajaxOptions); errorMessage = errorMessage || "Unknown error"; $.ajax($.extend($.extend({ type: "GET", dataType: "json", cache : !$.browser.msie, @@ -608,7 +644,7 @@ }, complete: function(req) { try { - var resp = $.httpData(req, "json"); + var resp = httpData(req, "json"); } catch(e) { if (options.error) { options.error(req.status, req, e); @@ -638,6 +674,7 @@ var commit = options.ensure_full_commit; delete options.ensure_full_commit; return function(xhr) { + xhr.setRequestHeader('Accept', 'application/json'); xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString()); }; } diff --git a/rel/overlay/share/www/script/json2.js b/rel/overlay/share/www/script/json2.js index 39d8f370..a1a3b170 100644 --- a/rel/overlay/share/www/script/json2.js +++ b/rel/overlay/share/www/script/json2.js @@ -1,6 +1,6 @@ /* http://www.JSON.org/json2.js - 2009-09-29 + 2010-03-20 Public Domain. @@ -433,6 +433,7 @@ if (!this.JSON) { // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. + text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { diff --git a/rel/overlay/share/www/script/jspec/jspec.js b/rel/overlay/share/www/script/jspec/jspec.js index d6daf5ef..b2ea4768 100644 --- a/rel/overlay/share/www/script/jspec/jspec.js +++ b/rel/overlay/share/www/script/jspec/jspec.js @@ -87,7 +87,7 @@ */ Server : function(results, options) { - var uri = options.uri || 'http://' + window.location.host + '/results' + var uri = options.uri || window.location.protocol + "//" + window.location.host + '/results' JSpec.post(uri, { stats: JSpec.stats, options: options, diff --git a/rel/overlay/share/www/script/test/all_docs.js b/rel/overlay/share/www/script/test/all_docs.js index ab443605..1d83aa95 100644 --- a/rel/overlay/share/www/script/test/all_docs.js +++ b/rel/overlay/share/www/script/test/all_docs.js @@ -86,10 +86,51 @@ couchTests.all_docs = function(debug) { T(changes.results[2].doc); T(changes.results[2].doc._deleted); + rows = db.allDocs({include_docs: true}, ["1"]).rows; + TEquals(1, rows.length); + TEquals("1", rows[0].key); + TEquals("1", rows[0].id); + TEquals(true, rows[0].value.deleted); + TEquals(null, rows[0].doc); + + // add conflicts + var conflictDoc1 = { + _id: "3", _rev: "2-aa01552213fafa022e6167113ed01087", value: "X" + }; + var conflictDoc2 = { + _id: "3", _rev: "2-ff01552213fafa022e6167113ed01087", value: "Z" + }; + T(db.save(conflictDoc1, {new_edits: false})); + T(db.save(conflictDoc2, {new_edits: false})); + + var winRev = db.open("3"); + + changes = db.changes({include_docs: true, conflicts: true, style: "all_docs"}); + TEquals("3", changes.results[3].id); + TEquals(3, changes.results[3].changes.length); + TEquals(winRev._rev, changes.results[3].changes[0].rev); + TEquals("3", changes.results[3].doc._id); + TEquals(winRev._rev, changes.results[3].doc._rev); + TEquals(true, changes.results[3].doc._conflicts instanceof Array); + TEquals(2, changes.results[3].doc._conflicts.length); + + rows = db.allDocs({include_docs: true, conflicts: true}).rows; + TEquals(3, rows.length); + TEquals("3", rows[2].key); + TEquals("3", rows[2].id); + TEquals(winRev._rev, rows[2].value.rev); + TEquals(winRev._rev, rows[2].doc._rev); + TEquals("3", rows[2].doc._id); + TEquals(true, rows[2].doc._conflicts instanceof Array); + TEquals(2, rows[2].doc._conflicts.length); + // test the all docs collates sanely db.save({_id: "Z", foo: "Z"}); db.save({_id: "a", foo: "a"}); var rows = db.allDocs({startkey: "Z", endkey: "Z"}).rows; T(rows.length == 1); + + // cleanup + db.deleteDb(); }; diff --git a/rel/overlay/share/www/script/test/attachment_names.js b/rel/overlay/share/www/script/test/attachment_names.js index 988dd2d2..777b5ece 100644 --- a/rel/overlay/share/www/script/test/attachment_names.js +++ b/rel/overlay/share/www/script/test/attachment_names.js @@ -16,6 +16,24 @@ couchTests.attachment_names = function(debug) { db.createDb(); if (debug) debugger; + var goodDoc = { + _id: "good_doc", + _attachments: { + "Колян.txt": { + content_type:"text/plain", + data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" + } + } + }; + + var save_response = db.save(goodDoc); + T(save_response.ok); + + var xhr = CouchDB.request("GET", "/test_suite_db/good_doc/Колян.txt"); + T(xhr.responseText == "This is a base64 encoded text"); + T(xhr.getResponseHeader("Content-Type") == "text/plain"); + T(xhr.getResponseHeader("Etag") == '"' + save_response.rev + '"'); + var binAttDoc = { _id: "bin_doc", _attachments:{ @@ -27,28 +45,23 @@ couchTests.attachment_names = function(debug) { }; // inline attachments - try { - db.save(binAttDoc); - TEquals(1, 2, "Attachment name with non UTF-8 encoding saved. Should never show!"); - } catch (e) { - TEquals("bad_request", e.error, "attachment_name: inline attachments"); - TEquals("Attachment name is not UTF-8 encoded", e.reason, "attachment_name: inline attachments"); - } + resp = db.save(binAttDoc); + TEquals(true, resp.ok, "attachment_name: inline attachment"); // standalone docs var bin_data = "JHAPDO*AU£PN ){(3u[d 93DQ9¡€])} ææøo'∂ƒæ≤çæππ•¥∫¶®#†π¶®¥π€ª®˙π8np"; + var xhr = (CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment\x80txt", { headers:{"Content-Type":"text/plain;charset=utf-8"}, body:bin_data })); var resp = JSON.parse(xhr.responseText); - TEquals(400, xhr.status, "attachment_name: standalone API"); - TEquals("bad_request", resp.error, "attachment_name: standalone API"); - TEquals("Attachment name is not UTF-8 encoded", resp.reason, "attachment_name: standalone API"); - + TEquals(201, xhr.status, "attachment_name: standalone API"); + TEquals("Created", xhr.statusText, "attachment_name: standalone API"); + TEquals(true, resp.ok, "attachment_name: standalone API"); // bulk docs var docs = { docs: [binAttDoc] }; @@ -57,10 +70,8 @@ couchTests.attachment_names = function(debug) { body: JSON.stringify(docs) }); - var resp = JSON.parse(xhr.responseText); - TEquals(400, xhr.status, "attachment_name: bulk docs"); - TEquals("bad_request", resp.error, "attachment_name: bulk docs"); - TEquals("Attachment name is not UTF-8 encoded", resp.reason, "attachment_name: bulk docs"); + TEquals(201, xhr.status, "attachment_name: bulk docs"); + TEquals("Created", xhr.statusText, "attachment_name: bulk docs"); // leading underscores diff --git a/rel/overlay/share/www/script/test/attachments.js b/rel/overlay/share/www/script/test/attachments.js index e16c384f..826373dc 100644 --- a/rel/overlay/share/www/script/test/attachments.js +++ b/rel/overlay/share/www/script/test/attachments.js @@ -93,6 +93,7 @@ couchTests.attachments= function(debug) { }); T(xhr.status == 201); var rev = JSON.parse(xhr.responseText).rev; + TEquals('"' + rev + '"', xhr.getResponseHeader("Etag")); var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); T(xhr.responseText == bin_data); @@ -110,6 +111,7 @@ couchTests.attachments= function(debug) { }); T(xhr.status == 201); var rev = JSON.parse(xhr.responseText).rev; + TEquals('"' + rev + '"', xhr.getResponseHeader("Etag")); var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); T(xhr.responseText == bin_data); diff --git a/rel/overlay/share/www/script/test/attachments_multipart.js b/rel/overlay/share/www/script/test/attachments_multipart.js index f173d2bb..2fc8e3bd 100644 --- a/rel/overlay/share/www/script/test/attachments_multipart.js +++ b/rel/overlay/share/www/script/test/attachments_multipart.js @@ -78,15 +78,19 @@ couchTests.attachments_multipart= function(debug) { // now edit an attachment - var doc = db.open("multipart"); + var doc = db.open("multipart", {att_encoding_info: true}); var firstrev = doc._rev; T(doc._attachments["foo.txt"].stub == true); T(doc._attachments["bar.txt"].stub == true); T(doc._attachments["baz.txt"].stub == true); + TEquals("undefined", typeof doc._attachments["foo.txt"].encoding); + TEquals("undefined", typeof doc._attachments["bar.txt"].encoding); + TEquals("gzip", doc._attachments["baz.txt"].encoding); //lets change attachment bar delete doc._attachments["bar.txt"].stub; // remove stub member (or could set to false) + delete doc._attachments["bar.txt"].digest; // remove the digest (it's for the gzip form) doc._attachments["bar.txt"].length = 18; doc._attachments["bar.txt"].follows = true; //lets delete attachment baz: @@ -104,6 +108,7 @@ couchTests.attachments_multipart= function(debug) { "this is 18 chars l" + "\r\n--abc123--" }); + TEquals(201, xhr.status); xhr = CouchDB.request("GET", "/test_suite_db/multipart/bar.txt"); @@ -115,8 +120,11 @@ couchTests.attachments_multipart= function(debug) { // now test receiving multipart docs function getBoundary(xhr) { + if (xhr instanceof XMLHttpRequest) { var ctype = xhr.getResponseHeader("Content-Type"); - + } else { + var ctype = xhr.headers['Content-Type']; + } var ctypeArgs = ctype.split("; ").slice(1); var boundary = null; for(var i=0; i<ctypeArgs.length; i++) { @@ -134,7 +142,11 @@ couchTests.attachments_multipart= function(debug) { function parseMultipart(xhr) { var boundary = getBoundary(xhr); + if (xhr instanceof XMLHttpRequest) { var mimetext = xhr.responseText; + } else { + var mimetext = xhr.body; + } // strip off leading boundary var leading = "--" + boundary + "\r\n"; var last = "\r\n--" + boundary + "--"; @@ -208,6 +220,33 @@ couchTests.attachments_multipart= function(debug) { T(sections[1].body == "this is 18 chars l"); + // try the atts_since parameter together with the open_revs parameter + xhr = CouchDB.request( + "GET", + '/test_suite_db/multipart?open_revs=["' + + doc._rev + '"]&atts_since=["' + firstrev + '"]', + {headers: {"accept": "multipart/mixed"}} + ); + + T(xhr.status === 200); + + sections = parseMultipart(xhr); + // 1 section, with a multipart/related Content-Type + T(sections.length === 1); + T(sections[0].headers['Content-Type'].indexOf('multipart/related;') === 0); + + var innerSections = parseMultipart(sections[0]); + // 2 inner sections: a document body section plus an attachment data section + T(innerSections.length === 2); + T(innerSections[0].headers['content-type'] === 'application/json'); + + doc = JSON.parse(innerSections[0].body); + + T(doc._attachments['foo.txt'].stub === true); + T(doc._attachments['bar.txt'].follows === true); + + T(innerSections[1].body === "this is 18 chars l"); + // try it with a rev that doesn't exist (should get all attachments) xhr = CouchDB.request("GET", "/test_suite_db/multipart?atts_since=[\"1-2897589\"]", @@ -245,4 +284,130 @@ couchTests.attachments_multipart= function(debug) { T(sections[1].body == "this is 18 chars l"); + + // check that with the document multipart/mixed API it's possible to receive + // attachments in compressed form (if they're stored in compressed form) + + var server_config = [ + { + section: "attachments", + key: "compression_level", + value: "8" + }, + { + section: "attachments", + key: "compressible_types", + value: "text/plain" + } + ]; + + function testMultipartAttCompression() { + var doc = { _id: "foobar" }; + var lorem = + CouchDB.request("GET", "/_utils/script/test/lorem.txt").responseText; + var helloData = "hello world"; + + TEquals(true, db.save(doc).ok); + + var firstRev = doc._rev; + var xhr = CouchDB.request( + "PUT", + "/" + db.name + "/" + doc._id + "/data.bin?rev=" + firstRev, + { + body: helloData, + headers: {"Content-Type": "application/binary"} + } + ); + TEquals(201, xhr.status); + + var secondRev = db.open(doc._id)._rev; + xhr = CouchDB.request( + "PUT", + "/" + db.name + "/" + doc._id + "/lorem.txt?rev=" + secondRev, + { + body: lorem, + headers: {"Content-Type": "text/plain"} + } + ); + TEquals(201, xhr.status); + + var thirdRev = db.open(doc._id)._rev; + + xhr = CouchDB.request( + "GET", + '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]', + { + headers: { + "Accept": "multipart/mixed", + "X-CouchDB-Send-Encoded-Atts": "true" + } + } + ); + TEquals(200, xhr.status); + + var sections = parseMultipart(xhr); + // 1 section, with a multipart/related Content-Type + TEquals(1, sections.length); + TEquals(0, + sections[0].headers['Content-Type'].indexOf('multipart/related;')); + + var innerSections = parseMultipart(sections[0]); + // 3 inner sections: a document body section plus 2 attachment data sections + TEquals(3, innerSections.length); + TEquals('application/json', innerSections[0].headers['content-type']); + + doc = JSON.parse(innerSections[0].body); + + TEquals(true, doc._attachments['lorem.txt'].follows); + TEquals("gzip", doc._attachments['lorem.txt'].encoding); + TEquals(true, doc._attachments['data.bin'].follows); + T(doc._attachments['data.bin'] !== "gzip"); + + if (innerSections[1].body === helloData) { + T(innerSections[2].body !== lorem); + } else if (innerSections[2].body === helloData) { + T(innerSections[1].body !== lorem); + } else { + T(false, "Could not found data.bin attachment data"); + } + + // now test that it works together with the atts_since parameter + + xhr = CouchDB.request( + "GET", + '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]' + + '&atts_since=["' + secondRev + '"]', + { + headers: { + "Accept": "multipart/mixed", + "X-CouchDB-Send-Encoded-Atts": "true" + } + } + ); + TEquals(200, xhr.status); + + sections = parseMultipart(xhr); + // 1 section, with a multipart/related Content-Type + TEquals(1, sections.length); + TEquals(0, + sections[0].headers['Content-Type'].indexOf('multipart/related;')); + + innerSections = parseMultipart(sections[0]); + // 2 inner sections: a document body section plus 1 attachment data section + TEquals(2, innerSections.length); + TEquals('application/json', innerSections[0].headers['content-type']); + + doc = JSON.parse(innerSections[0].body); + + TEquals(true, doc._attachments['lorem.txt'].follows); + TEquals("gzip", doc._attachments['lorem.txt'].encoding); + TEquals("undefined", typeof doc._attachments['data.bin'].follows); + TEquals(true, doc._attachments['data.bin'].stub); + T(innerSections[1].body !== lorem); + } + + run_on_modified_server(server_config, testMultipartAttCompression); + + // cleanup + db.deleteDb(); }; diff --git a/rel/overlay/share/www/script/test/basics.js b/rel/overlay/share/www/script/test/basics.js index 8885ba6e..30c27c11 100644 --- a/rel/overlay/share/www/script/test/basics.js +++ b/rel/overlay/share/www/script/test/basics.js @@ -37,9 +37,8 @@ couchTests.basics = function(debug) { TEquals(dbname, xhr.getResponseHeader("Location").substr(-dbname.length), "should return Location header to newly created document"); - - TEquals("http://", - xhr.getResponseHeader("Location").substr(0, 7), + TEquals(CouchDB.protocol, + xhr.getResponseHeader("Location").substr(0, CouchDB.protocol.length), "should return absolute Location header to newly created document"); }); @@ -160,8 +159,8 @@ couchTests.basics = function(debug) { var loc = xhr.getResponseHeader("Location"); T(loc, "should have a Location header"); var locs = loc.split('/'); - T(locs[4] == resp.id); - T(locs[3] == "test_suite_db"); + T(locs[locs.length-1] == resp.id); + T(locs[locs.length-2] == "test_suite_db"); // test that that POST's with an _id aren't overriden with a UUID. var xhr = CouchDB.request("POST", "/test_suite_db", { @@ -181,9 +180,8 @@ couchTests.basics = function(debug) { TEquals("/test_suite_db/newdoc", xhr.getResponseHeader("Location").substr(-21), "should return Location header to newly created document"); - - TEquals("http://", - xhr.getResponseHeader("Location").substr(0, 7), + TEquals(CouchDB.protocol, + xhr.getResponseHeader("Location").substr(0, CouchDB.protocol.length), "should return absolute Location header to newly created document"); // deleting a non-existent doc should be 404 diff --git a/rel/overlay/share/www/script/test/changes.js b/rel/overlay/share/www/script/test/changes.js index 50649508..ea22bfb3 100644 --- a/rel/overlay/share/www/script/test/changes.js +++ b/rel/overlay/share/www/script/test/changes.js @@ -12,7 +12,7 @@ function jsonp(obj) { T(jsonp_flag == 0); - T(obj.results.length == 1 && obj.last_seq==1, "jsonp") + T(obj.results.length == 1 && obj.last_seq == 1, "jsonp"); jsonp_flag = 1; } @@ -25,7 +25,7 @@ couchTests.changes = function(debug) { var req = CouchDB.request("GET", "/test_suite_db/_changes"); var resp = JSON.parse(req.responseText); - T(resp.results.length == 0 && resp.last_seq==0, "empty db") + T(resp.results.length == 0 && resp.last_seq == 0, "empty db"); var docFoo = {_id:"foo", bar:1}; T(db.save(docFoo).ok); T(db.ensureFullCommit().ok); @@ -35,8 +35,8 @@ couchTests.changes = function(debug) { var resp = JSON.parse(req.responseText); T(resp.last_seq == 1); - T(resp.results.length == 1, "one doc db") - T(resp.results[0].changes[0].rev == docFoo._rev) + T(resp.results.length == 1, "one doc db"); + T(resp.results[0].changes[0].rev == docFoo._rev); // test with callback @@ -90,16 +90,16 @@ couchTests.changes = function(debug) { change1 = JSON.parse(lines[0]); change2 = JSON.parse(lines[1]); if (change2.seq != 2) { - throw "bad seq, try again" + throw "bad seq, try again"; } }, "bar-only"); - T(change1.seq == 1) - T(change1.id == "foo") + T(change1.seq == 1); + T(change1.id == "foo"); - T(change2.seq == 2) - T(change2.id == "bar") - T(change2.changes[0].rev == docBar._rev) + T(change2.seq == 2); + T(change2.id == "bar"); + T(change2.changes[0].rev == docBar._rev); var docBaz = {_id:"baz", baz:1}; @@ -110,7 +110,7 @@ couchTests.changes = function(debug) { lines = xhr.responseText.split("\n"); change3 = JSON.parse(lines[2]); if (change3.seq != 3) { - throw "bad seq, try again" + throw "bad seq, try again"; } }); @@ -133,8 +133,8 @@ couchTests.changes = function(debug) { } }, "heartbeat"); - T(str.charAt(str.length - 1) == "\n") - T(str.charAt(str.length - 2) == "\n") + T(str.charAt(str.length - 1) == "\n"); + T(str.charAt(str.length - 2) == "\n"); // otherwise we'll continue to receive heartbeats forever xhr.abort(); @@ -164,10 +164,10 @@ couchTests.changes = function(debug) { if (line.charAt(line.length-1) == ",") { var linetrimmed = line.substring(0, line.length-1); } else { - var linetrimmed = line + var linetrimmed = line; } return JSON.parse(linetrimmed); - } + }; waitForSuccess(function() { lines = xhr.responseText.split("\n"); @@ -181,6 +181,8 @@ couchTests.changes = function(debug) { T(change.id == "barz"); T(change.changes[0].rev == docBarz._rev); T(lines[3]=='"last_seq":4}'); + + } // test the filtered changes @@ -195,7 +197,7 @@ couchTests.changes = function(debug) { "userCtx" : stringFun(function(doc, req) { return doc.user && (doc.user == req.userCtx.name); }), - "conflicted" : "function(doc, req) { return (doc._conflicts);}", + "conflicted" : "function(doc, req) { return (doc._conflicts);}" }, options : { local_seq : true @@ -205,7 +207,7 @@ couchTests.changes = function(debug) { map : "function(doc) {emit(doc._local_seq, null)}" } } - } + }; db.save(ddoc); @@ -251,8 +253,8 @@ couchTests.changes = function(debug) { T(resp.results && resp.results.length > 0 && resp.results[0]["id"] == "bingo", "filter the correct update"); xhr.abort(); - timeout = 500; - last_seq = 10 + var timeout = 500; + var last_seq = 10; while (true) { // filter with continuous @@ -267,15 +269,15 @@ couchTests.changes = function(debug) { waitForSuccess(function() { // throws an error after 5 seconds if (xhr.readyState != 4) { - throw("still waiting") + throw("still waiting"); } }, "continuous-rusty"); lines = xhr.responseText.split("\n"); + var good = false; try { - JSON.parse(lines[3]) + JSON.parse(lines[3]); good = true; } catch(e) { - good = false; } if (good) { T(JSON.parse(lines[1]).id == "bingo", lines[1]); @@ -350,7 +352,7 @@ couchTests.changes = function(debug) { req = CouchDB.request("GET", "/test_suite_db/_changes?limit=1"); resp = JSON.parse(req.responseText); - TEquals(1, resp.results.length) + TEquals(1, resp.results.length); //filter includes _conflicts var id = db.save({'food' : 'pizza'}).id; @@ -396,7 +398,116 @@ couchTests.changes = function(debug) { T(resp.results.length === 2); T(resp.results[0].id === "doc2"); T(resp.results[1].id === "doc4"); + + // test filtering on docids + // + + var options = { + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]}) + }; + + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 0); + + T(db.save({"_id":"something", "bop" : "plankton"}).ok); + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 1); + T(resp.results[0].id === "something"); + + T(db.save({"_id":"anotherthing", "bop" : "plankton"}).ok); + var req = CouchDB.request("POST", "/test_suite_db/_changes?filter=_doc_ids", options); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 2); + T(resp.results[0].id === "something"); + T(resp.results[1].id === "anotherthing"); + + var docids = JSON.stringify(["something", "anotherthing", "andmore"]), + req = CouchDB.request("GET", "/test_suite_db/_changes?filter=_doc_ids&doc_ids="+docids, options); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 2); + T(resp.results[0].id === "something"); + T(resp.results[1].id === "anotherthing"); + + var req = CouchDB.request("GET", "/test_suite_db/_changes?filter=_design"); + var resp = JSON.parse(req.responseText); + T(resp.results.length === 1); + T(resp.results[0].id === "_design/erlang"); + + + if (!is_safari && xhr) { + // filter docids with continuous + xhr = CouchDB.newXhr(); + xhr.open("POST", "/test_suite_db/_changes?feed=continuous&timeout=500&since=7&filter=_doc_ids", true); + xhr.setRequestHeader("Content-Type", "application/json"); + + xhr.send(options.body); + + T(db.save({"_id":"andmore", "bop" : "plankton"}).ok); + + + waitForSuccess(function() { + if (xhr.readyState != 4) { + throw("still waiting"); + } + }, "andmore-only"); + + var line = JSON.parse(xhr.responseText.split("\n")[0]); + T(line.seq == 8); + T(line.id == "andmore"); + } + }); + // COUCHDB-1037 - empty result for ?limit=1&filter=foo/bar in some cases + T(db.deleteDb()); + T(db.createDb()); + + ddoc = { + _id: "_design/testdocs", + filters: { + testdocsonly: (function(doc, req) { + return (typeof doc.integer === "number"); + }).toString() + } + }; + T(db.save(ddoc)); + + ddoc = { + _id: "_design/foobar", + foo: "bar" + }; + T(db.save(ddoc)); + + db.bulkSave(makeDocs(0, 5)); + + req = CouchDB.request("GET", "/" + db.name + "/_changes"); + resp = JSON.parse(req.responseText); + TEquals(7, resp.last_seq); + TEquals(7, resp.results.length); + + req = CouchDB.request( + "GET", "/"+ db.name + "/_changes?limit=1&filter=testdocs/testdocsonly"); + resp = JSON.parse(req.responseText); + TEquals(3, resp.last_seq); + TEquals(1, resp.results.length); + TEquals("0", resp.results[0].id); + + req = CouchDB.request( + "GET", "/" + db.name + "/_changes?limit=2&filter=testdocs/testdocsonly"); + resp = JSON.parse(req.responseText); + TEquals(4, resp.last_seq); + TEquals(2, resp.results.length); + TEquals("0", resp.results[0].id); + TEquals("1", resp.results[1].id); + + TEquals(0, CouchDB.requestStats('httpd', 'clients_requesting_changes').current); + CouchDB.request("GET", "/" + db.name + "/_changes"); + TEquals(0, CouchDB.requestStats('httpd', 'clients_requesting_changes').current); + + // cleanup + db.deleteDb(); }; diff --git a/rel/overlay/share/www/script/test/config.js b/rel/overlay/share/www/script/test/config.js index ef74934b..e83ecfd9 100644 --- a/rel/overlay/share/www/script/test/config.js +++ b/rel/overlay/share/www/script/test/config.js @@ -29,19 +29,25 @@ couchTests.config = function(debug) { */ var server_port = CouchDB.host.split(':'); if(server_port.length == 1 && CouchDB.inBrowser) { - var proto = window.location.protocol; - if(proto == "http:") { + if(CouchDB.protocol == "http://") { port = 80; } - if(proto == "https:") { + if(CouchDB.protocol == "https://") { port = 443; } } else { port = server_port.pop(); } + if(CouchDB.protocol == "http://") { + config_port = config.httpd.port; + } + if(CouchDB.protocol == "https://") { + config_port = config.ssl.port; + } + if(port) { - T(config.httpd.port == port); + TEquals(config_port, port, "ports should match"); } T(config.couchdb.database_dir); @@ -50,7 +56,8 @@ couchTests.config = function(debug) { T(config.log.level); T(config.query_servers.javascript); - // test that settings can be altered + // test that settings can be altered, and that an undefined whitelist allows any change + TEquals(undefined, config.httpd.config_whitelist, "Default whitelist is empty"); xhr = CouchDB.request("PUT", "/_config/test/foo",{ body : JSON.stringify("bar"), headers: {"X-Couch-Persist": "false"} @@ -64,4 +71,93 @@ couchTests.config = function(debug) { xhr = CouchDB.request("GET", "/_config/test/foo"); config = JSON.parse(xhr.responseText); T(config == "bar"); + + // Non-term whitelist values allow further modification of the whitelist. + xhr = CouchDB.request("PUT", "/_config/httpd/config_whitelist",{ + body : JSON.stringify("!This is an invalid Erlang term!"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Set config whitelist to an invalid Erlang term"); + xhr = CouchDB.request("DELETE", "/_config/httpd/config_whitelist",{ + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Modify whitelist despite it being invalid syntax"); + + // Non-list whitelist values allow further modification of the whitelist. + xhr = CouchDB.request("PUT", "/_config/httpd/config_whitelist",{ + body : JSON.stringify("{[yes, a_valid_erlang_term, but_unfortunately, not_a_list]}"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Set config whitelist to an non-list term"); + xhr = CouchDB.request("DELETE", "/_config/httpd/config_whitelist",{ + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Modify whitelist despite it not being a list"); + + // Keys not in the whitelist may not be modified. + xhr = CouchDB.request("PUT", "/_config/httpd/config_whitelist",{ + body : JSON.stringify("[{httpd,config_whitelist}, {test,foo}]"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Set config whitelist to something valid"); + + ["PUT", "DELETE"].forEach(function(method) { + ["test/not_foo", "not_test/foo", "neither_test/nor_foo"].forEach(function(pair) { + var path = "/_config/" + pair; + var test_name = method + " to " + path + " disallowed: not whitelisted"; + + xhr = CouchDB.request(method, path, { + body : JSON.stringify("Bummer! " + test_name), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(400, xhr.status, test_name); + }); + }); + + // Keys in the whitelist may be modified. + ["PUT", "DELETE"].forEach(function(method) { + xhr = CouchDB.request(method, "/_config/test/foo",{ + body : JSON.stringify(method + " to whitelisted config variable"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Keys in the whitelist may be modified"); + }); + + // Non-2-tuples in the whitelist are ignored + xhr = CouchDB.request("PUT", "/_config/httpd/config_whitelist",{ + body : JSON.stringify("[{httpd,config_whitelist}, these, {are}, {nOt, 2, tuples}," + + " [so], [they, will], [all, become, noops], {test,foo}]"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Set config whitelist with some inert values"); + ["PUT", "DELETE"].forEach(function(method) { + xhr = CouchDB.request(method, "/_config/test/foo",{ + body : JSON.stringify(method + " to whitelisted config variable"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Update whitelisted variable despite invalid entries"); + }); + + // Atoms, binaries, and strings suffice as whitelist sections and keys. + ["{test,foo}", '{"test","foo"}', '{<<"test">>,<<"foo">>}'].forEach(function(pair) { + xhr = CouchDB.request("PUT", "/_config/httpd/config_whitelist",{ + body : JSON.stringify("[{httpd,config_whitelist}, " + pair + "]"), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Set config whitelist to include " + pair); + + var pair_format = {"t":"tuple", '"':"string", "<":"binary"}[pair[1]]; + ["PUT", "DELETE"].forEach(function(method) { + xhr = CouchDB.request(method, "/_config/test/foo",{ + body : JSON.stringify(method + " with " + pair_format), + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Whitelist works with " + pair_format); + }); + }); + + xhr = CouchDB.request("DELETE", "/_config/httpd/config_whitelist",{ + headers: {"X-Couch-Persist": "false"} + }); + TEquals(200, xhr.status, "Reset config whitelist to undefined"); }; diff --git a/rel/overlay/share/www/script/test/conflicts.js b/rel/overlay/share/www/script/test/conflicts.js index 7258bc31..65122581 100644 --- a/rel/overlay/share/www/script/test/conflicts.js +++ b/rel/overlay/share/www/script/test/conflicts.js @@ -61,4 +61,29 @@ couchTests.conflicts = function(debug) { T(db.save(doc2).ok); // we can save a new document over a deletion without // knowing the deletion rev. + + // Verify COUCHDB-1178 + var r1 = {"_id":"doc","foo":"bar"}; + var r2 = {"_id":"doc","foo":"baz","_rev":"1-4c6114c65e295552ab1019e2b046b10e"}; + var r3 = {"_id":"doc","foo":"bam","_rev":"2-cfcd6781f13994bde69a1c3320bfdadb"}; + var r4 = {"_id":"doc","foo":"bat","_rev":"3-cc2f3210d779aef595cd4738be0ef8ff"}; + + T(db.save({"_id":"_design/couchdb-1178","validate_doc_update":"function(){}"}).ok); + T(db.save(r1).ok); + T(db.save(r2).ok); + T(db.save(r3).ok); + + T(db.compact().ok); + while (db.info().compact_running) {}; + + TEquals({"_id":"doc", + "_rev":"3-cc2f3210d779aef595cd4738be0ef8ff", + "foo":"bam", + "_revisions":{"start":3, + "ids":["cc2f3210d779aef595cd4738be0ef8ff", + "cfcd6781f13994bde69a1c3320bfdadb", + "4c6114c65e295552ab1019e2b046b10e"]}}, + db.open("doc", {"revs": true})); + TEquals([], db.bulkSave([r4, r3, r2], {"new_edits":false}), "no failures"); + }; diff --git a/rel/overlay/share/www/script/test/cookie_auth.js b/rel/overlay/share/www/script/test/cookie_auth.js index ef915602..e3548640 100644 --- a/rel/overlay/share/www/script/test/cookie_auth.js +++ b/rel/overlay/share/www/script/test/cookie_auth.js @@ -104,6 +104,18 @@ couchTests.cookie_auth = function(debug) { T(CouchDB.login('Jason Davies', password).ok); T(CouchDB.session().userCtx.name == 'Jason Davies'); + // 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); + T(CouchDB.session().userCtx.name == 'Jason Davies'); + // update one's own credentials document jasonUserDoc.foo=2; T(usersDb.save(jasonUserDoc).ok); diff --git a/rel/overlay/share/www/script/test/copy_doc.js b/rel/overlay/share/www/script/test/copy_doc.js index a6de1892..99e3c7fe 100644 --- a/rel/overlay/share/www/script/test/copy_doc.js +++ b/rel/overlay/share/www/script/test/copy_doc.js @@ -36,6 +36,9 @@ couchTests.copy_doc = function(debug) { }); T(xhr.status == 409); // conflict + var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied2"); + T(xhr.status == 400); // bad request (no 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} diff --git a/rel/overlay/share/www/script/test/design_docs.js b/rel/overlay/share/www/script/test/design_docs.js index a24167b2..702f0441 100644 --- a/rel/overlay/share/www/script/test/design_docs.js +++ b/rel/overlay/share/www/script/test/design_docs.js @@ -13,180 +13,415 @@ 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(); - if (debug) debugger; - run_on_modified_server( - [{section: "query_server_config", + var server_config = [ + { + section: "query_server_config", key: "reduce_limit", - value: "false"}], -function() { + value: "false" + } + ]; - var numDocs = 500; + 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", // turn off couch.js id escaping? - language: "javascript", - whatever : { - stringzone : "exports.string = 'plankton';", - commonjs : { - whynot : "exports.test = require('../stringzone'); exports.foo = require('whatever/stringzone');", - upper : "exports.testing = require('./whynot').test.string.toUpperCase()+module.id+require('./whynot').foo.string" + function makebigstring(power) { + var str = "a"; + while(power-- > 0) { + str = str + str; } - }, - views: { - all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"}, - no_docs: {map: "function(doc) {}"}, - single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}, - summate: {map:"function (doc) {emit(doc.integer, doc.integer)};", - reduce:"function (keys, values) { return sum(values); };"}, - summate2: {map:"function (doc) {emit(doc.integer, doc.integer)};", - reduce:"function (keys, values) { return sum(values); };"}, - huge_src_and_results: {map: "function(doc) { if (doc._id == \"1\") { emit(\"" + makebigstring(16) + "\", null) }}", - reduce:"function (keys, values) { return \"" + makebigstring(16) + "\"; };"} - }, - shows: { - simple: "function() {return 'ok'};", - requirey : "function() { var lib = require('whatever/commonjs/upper'); return lib.testing; };", - circular : "function() { var lib = require('whatever/commonjs/upper'); return JSON.stringify(this); };" + return str; } - }; - - 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 - var 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'};" - T(db.save(designDoc).ok); - - var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/simple"); - T(xhr.status == 200); - TEquals(xhr.responseText, "ko"); - - var 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 - var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/requirey"); - T(xhr.status == 200); - TEquals("PLANKTONwhatever/commonjs/upperplankton", xhr.responseText); - - var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/circular"); - T(xhr.status == 200); - TEquals("javascript", JSON.parse(xhr.responseText).language); - - var prev_view_sig = db.designInfo("_design/test").view_index.signature; - - db.bulkSave(makeDocs(1, numDocs + 1)); - - // test that we get design doc info back - var dinfo = db.designInfo("_design/test"); - TEquals("test", dinfo.name); - var vinfo = dinfo.view_index; - TEquals(51, vinfo.disk_size); - TEquals(false, vinfo.compact_running); - // test that GET /db/_design/test/_info - // hasn't triggered an update of the views - TEquals(prev_view_sig, vinfo.signature, 'ddoc sig'); - for (var loop = 0; loop < 2; loop++) { - T(db.view("test/all_docs_twice", {stale: "ok"}).total_rows === 0); - T(db.view("test/single_doc", {stale: "ok"}).total_rows === 0); - T(db.view("test/summate", {stale: "ok"}).rows.length === 0); + + 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); - restartServer(); - }; - - // test that POST /db/_view_cleanup - // doesn't trigger an update of the views - T(db.viewCleanup().ok); - for (var loop = 0; loop < 2; loop++) { - T(db.view("test/all_docs_twice", {stale: "ok"}).total_rows == 0); - T(db.view("test/single_doc", {stale: "ok"}).total_rows == 0); - T(db.view("test/summate", {stale: "ok"}).rows.length == 0); + + // 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); - restartServer(); - }; - // test that the _all_docs view returns correctly with keys - var results = db.allDocs({startkey:"_design", endkey:"_design0"}); - T(results.rows.length == 1); + // 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); - for (var loop = 0; loop < 2; loop++) { - var rows = db.view("test/all_docs_twice").rows; - for (var i = 0; i < numDocs; i++) { - T(rows[2*i].key == i+1); - T(rows[(2*i)+1].key == i+1); + // 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; }; - 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) }}"} - } - }; - T(db.save(designDoc2).ok); - T(db.view("test2/single_doc").total_rows == 1); + // 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(); + }; - var summate = function(N) {return (N+1)*N/2;}; - var result = db.view("test/summate"); - T(result.rows[0].value == summate(numDocs)); + // 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() + } + } + }; - result = db.view("test/summate", {startkey:4,endkey:4}); - T(result.rows[0].value == 4); + T(db.save(designDoc2).ok); + T(db.view("test2/single_doc").total_rows == 1); - result = db.view("test/summate", {startkey:4,endkey:5}); - T(result.rows[0].value == 9); + 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:6}); - T(result.rows[0].value == 15); + result = db.view("test/summate", {startkey: 4, endkey: 4}); + T(result.rows[0].value == 4); - // 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); + result = db.view("test/summate", {startkey: 4, endkey: 5}); + T(result.rows[0].value == 9); - for(var 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)); - } + result = db.view("test/summate", {startkey: 4, endkey: 6}); + T(result.rows[0].value == 15); - T(db.deleteDoc(designDoc).ok); - T(db.open(designDoc._id) == null); - T(db.view("test/no_docs") == null); + // test start_key and end_key aliases + result = db.view("test/summate", {start_key: 4, end_key: 6}); + T(result.rows[0].value == 15); - T(db.ensureFullCommit().ok); - restartServer(); - T(db.open(designDoc._id) == null); - T(db.view("test/no_docs") == null); + // 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); - // trigger ddoc cleanup - T(db.viewCleanup().ok); + 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); + + // cleanup + db.deleteDb(); + db2.deleteDb(); }; diff --git a/rel/overlay/share/www/script/test/etags_views.js b/rel/overlay/share/www/script/test/etags_views.js index 7e1537bd..34116f71 100644 --- a/rel/overlay/share/www/script/test/etags_views.js +++ b/rel/overlay/share/www/script/test/etags_views.js @@ -11,23 +11,34 @@ // the License. couchTests.etags_views = function(debug) { - var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"}); + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"}); db.deleteDb(); db.createDb(); if (debug) debugger; var designDoc = { - _id:"_design/etags", + _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) { @@ -40,9 +51,9 @@ couchTests.etags_views = function(debug) { } }; T(db.save(designDoc).ok); + db.bulkSave(makeDocs(0, 10)); + var xhr; - var docs = makeDocs(0, 10); - db.bulkSave(docs); // verify get w/Etag on map view xhr = CouchDB.request("GET", "/test_suite_db/_design/etags/_view/basicView"); @@ -52,17 +63,92 @@ couchTests.etags_views = function(debug) { headers: {"if-none-match": etag} }); T(xhr.status == 304); - // TODO GET with keys (when that is available) + + // 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 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", { + 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]})} diff --git a/rel/overlay/share/www/script/test/http.js b/rel/overlay/share/www/script/test/http.js index 8a2e09b8..5f46af52 100644 --- a/rel/overlay/share/www/script/test/http.js +++ b/rel/overlay/share/www/script/test/http.js @@ -25,7 +25,7 @@ couchTests.http = function(debug) { var xhr = CouchDB.request("PUT", "/test_suite_db/test", {body: "{}"}); var host = CouchDB.host; - TEquals("http://" + host + "/test_suite_db/test", + TEquals(CouchDB.protocol + host + "/test_suite_db/test", xhr.getResponseHeader("Location"), "should include ip address"); @@ -34,7 +34,7 @@ couchTests.http = function(debug) { headers: {"X-Forwarded-Host": "mysite.com"} }); - TEquals("http://mysite.com/test_suite_db/test2", + TEquals(CouchDB.protocol + "mysite.com/test_suite_db/test2", xhr.getResponseHeader("Location"), "should include X-Forwarded-Host"); @@ -47,7 +47,7 @@ couchTests.http = function(debug) { body: "{}", headers: {"X-Host": "mysite2.com"} }); - TEquals("http://mysite2.com/test_suite_db/test3", + TEquals(CouchDB.protocol + "mysite2.com/test_suite_db/test3", xhr.getResponseHeader("Location"), "should include X-Host"); }); diff --git a/rel/overlay/share/www/script/test/jsonp.js b/rel/overlay/share/www/script/test/jsonp.js index 6bec63ab..9aba7189 100644 --- a/rel/overlay/share/www/script/test/jsonp.js +++ b/rel/overlay/share/www/script/test/jsonp.js @@ -65,7 +65,7 @@ couchTests.jsonp = function(debug) { 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"; diff --git a/rel/overlay/share/www/script/test/list_views.js b/rel/overlay/share/www/script/test/list_views.js index 44afa899..2c1ac321 100644 --- a/rel/overlay/share/www/script/test/list_views.js +++ b/rel/overlay/share/www/script/test/list_views.js @@ -156,6 +156,9 @@ couchTests.list_views = function(debug) { var row = getRow(); send(row.doc.integer); return "tail"; + }), + secObj: stringFun(function(head, req) { + return toJSON(req.secObj); }) } }; @@ -201,6 +204,7 @@ couchTests.list_views = function(debug) { 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", { @@ -323,6 +327,16 @@ couchTests.list_views = function(debug) { 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]}' @@ -405,6 +419,12 @@ couchTests.list_views = function(debug) { 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" + @@ -419,10 +439,37 @@ couchTests.list_views = function(debug) { } }; + + 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"); }; diff --git a/rel/overlay/share/www/script/test/oauth.js b/rel/overlay/share/www/script/test/oauth.js index b439b4db..82ebe8a4 100644 --- a/rel/overlay/share/www/script/test/oauth.js +++ b/rel/overlay/share/www/script/test/oauth.js @@ -71,7 +71,7 @@ couchTests.oauth = function(debug) { var host = CouchDB.host; var dbPair = { source: { - url: "http://" + host + "/test_suite_db_a", + url: CouchDB.protocol + host + "/test_suite_db_a", auth: { oauth: { consumer_key: "key", @@ -82,7 +82,7 @@ couchTests.oauth = function(debug) { } }, target: { - url: "http://" + host + "/test_suite_db_b", + url: CouchDB.protocol + host + "/test_suite_db_b", headers: {"Authorization": adminBasicAuthHeaderValue()} } }; @@ -90,7 +90,7 @@ couchTests.oauth = function(debug) { // this function will be called on the modified server var testFun = function () { try { - CouchDB.request("PUT", "http://" + host + "/_config/admins/testadmin", { + CouchDB.request("PUT", CouchDB.protocol + host + "/_config/admins/testadmin", { headers: {"X-Couch-Persist": "false"}, body: JSON.stringify(testadminPassword) }); @@ -98,7 +98,7 @@ couchTests.oauth = function(debug) { waitForSuccess(function() { //loop until the couch server has processed the password i += 1; - var xhr = CouchDB.request("GET", "http://" + host + "/_config/admins/testadmin?foo="+i,{ + var xhr = CouchDB.request("GET", CouchDB.protocol + host + "/_config/admins/testadmin?foo="+i,{ headers: { "Authorization": adminBasicAuthHeaderValue() }}); @@ -109,7 +109,7 @@ couchTests.oauth = function(debug) { CouchDB.newUuids(2); // so we have one to make the salt - CouchDB.request("PUT", "http://" + host + "/_config/couch_httpd_auth/require_valid_user", { + CouchDB.request("PUT", CouchDB.protocol + host + "/_config/couch_httpd_auth/require_valid_user", { headers: { "X-Couch-Persist": "false", "Authorization": adminBasicAuthHeaderValue() @@ -157,11 +157,11 @@ couchTests.oauth = function(debug) { }; // Get request token via Authorization header - xhr = oauthRequest("GET", "http://" + host + "/_oauth/request_token", message, accessor); + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token", message, accessor); T(xhr.status == expectedCode); // GET request token via query parameters - xhr = oauthRequest("GET", "http://" + host + "/_oauth/request_token", message, accessor); + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token", message, accessor); T(xhr.status == expectedCode); responseMessage = OAuth.decodeForm(xhr.responseText); @@ -171,7 +171,7 @@ couchTests.oauth = function(debug) { //xhr = CouchDB.request("GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token); //T(xhr.status == expectedCode); - xhr = oauthRequest("GET", "http://" + host + "/_session", message, accessor); + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session", message, accessor); T(xhr.status == expectedCode); if (xhr.status == expectedCode == 200) { data = JSON.parse(xhr.responseText); @@ -179,11 +179,11 @@ couchTests.oauth = function(debug) { T(data.roles[0] == "test"); } - xhr = oauthRequest("GET", "http://" + host + "/_session?foo=bar", message, accessor); + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar", message, accessor); T(xhr.status == expectedCode); // Test HEAD method - xhr = oauthRequest("HEAD", "http://" + host + "/_session?foo=bar", message, accessor); + xhr = oauthRequest("HEAD", CouchDB.protocol + host + "/_session?foo=bar", message, accessor); T(xhr.status == expectedCode); // Replication @@ -207,7 +207,7 @@ couchTests.oauth = function(debug) { oauth_version: "1.0" } }; - xhr = oauthRequest("GET", "http://" + host + "/_session?foo=bar", message, adminAccessor); + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar", message, adminAccessor); if (xhr.status == expectedCode == 200) { data = JSON.parse(xhr.responseText); T(data.name == "testadmin"); @@ -216,13 +216,13 @@ couchTests.oauth = function(debug) { // Test when the user's token doesn't exist. message.parameters.oauth_token = "not a token!"; - xhr = oauthRequest("GET", "http://" + host + "/_session?foo=bar", + xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session?foo=bar", message, adminAccessor); T(xhr.status == 400, "Request should be invalid."); } } } finally { - var xhr = CouchDB.request("PUT", "http://" + host + "/_config/couch_httpd_auth/require_valid_user", { + var xhr = CouchDB.request("PUT", CouchDB.protocol + host + "/_config/couch_httpd_auth/require_valid_user", { headers: { "Authorization": adminBasicAuthHeaderValue(), "X-Couch-Persist": "false" @@ -231,7 +231,7 @@ couchTests.oauth = function(debug) { }); T(xhr.status == 200); - var xhr = CouchDB.request("DELETE", "http://" + host + "/_config/admins/testadmin", { + var xhr = CouchDB.request("DELETE", CouchDB.protocol + host + "/_config/admins/testadmin", { headers: { "Authorization": adminBasicAuthHeaderValue(), "X-Couch-Persist": "false" diff --git a/rel/overlay/share/www/script/test/proxyauth.js b/rel/overlay/share/www/script/test/proxyauth.js index 91e2f221..dff39415 100644 --- a/rel/overlay/share/www/script/test/proxyauth.js +++ b/rel/overlay/share/www/script/test/proxyauth.js @@ -127,4 +127,4 @@ couchTests.proxyauth = function(debug) { TestFun ); -}
\ No newline at end of file +}; diff --git a/rel/overlay/share/www/script/test/purge.js b/rel/overlay/share/www/script/test/purge.js index f8f45138..29689137 100644 --- a/rel/overlay/share/www/script/test/purge.js +++ b/rel/overlay/share/www/script/test/purge.js @@ -110,4 +110,36 @@ couchTests.purge = function(debug) { T(rows[(2*(i-4))+1].key == i+1); } T(db.view("test/single_doc").total_rows == 0); + + // COUCHDB-1065 + var dbA = new CouchDB("test_suite_db_a"); + var dbB = new CouchDB("test_suite_db_b"); + dbA.deleteDb(); + dbA.createDb(); + dbB.deleteDb(); + dbB.createDb(); + var docA = {_id:"test", a:1}; + var docB = {_id:"test", a:2}; + dbA.save(docA); + dbB.save(docB); + CouchDB.replicate(dbA.name, dbB.name); + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", { + body: JSON.stringify({"test":[docA._rev]}) + }); + TEquals(200, xhr.status, "single rev purge after replication succeeds"); + + var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docA._rev); + TEquals(404, xhr.status, "single rev purge removes revision"); + + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", { + body: JSON.stringify({"test":[docB._rev]}) + }); + TEquals(200, xhr.status, "single rev purge after replication succeeds"); + var xhr = CouchDB.request("GET", "/" + dbB.name + "/test?rev=" + docB._rev); + TEquals(404, xhr.status, "single rev purge removes revision"); + + var xhr = CouchDB.request("POST", "/" + dbB.name + "/_purge", { + body: JSON.stringify({"test":[docA._rev, docB._rev]}) + }); + TEquals(200, xhr.status, "all rev purge after replication succeeds"); }; diff --git a/rel/overlay/share/www/script/test/reduce_builtin.js b/rel/overlay/share/www/script/test/reduce_builtin.js index c9d41fa4..b3cc3cc7 100644 --- a/rel/overlay/share/www/script/test/reduce_builtin.js +++ b/rel/overlay/share/www/script/test/reduce_builtin.js @@ -150,5 +150,30 @@ couchTests.reduce_builtin = function(debug) { T(equals(results.rows[5], {key:["d","b"],value:10*i})); T(equals(results.rows[6], {key:["d","c"],value:10*i})); }; + + map = function (doc) { emit(doc.keys, [1, 1]); }; + + var results = db.query(map, "_sum", {group:true}); + T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]})); + T(equals(results.rows[1], {key:["a","b"],value:[20*i,20*i]})); + T(equals(results.rows[2], {key:["a", "b", "c"],value:[10*i,10*i]})); + T(equals(results.rows[3], {key:["a", "b", "d"],value:[10*i,10*i]})); + + var results = db.query(map, "_sum", {group: true, limit: 2}); + T(equals(results.rows[0], {key: ["a"], value: [20*i,20*i]})); + T(equals(results.rows.length, 2)); + + var results = db.query(map, "_sum", {group_level:1}); + T(equals(results.rows[0], {key:["a"],value:[70*i,70*i]})); + T(equals(results.rows[1], {key:["d"],value:[40*i,40*i]})); + + var results = db.query(map, "_sum", {group_level:2}); + T(equals(results.rows[0], {key:["a"],value:[20*i,20*i]})); + T(equals(results.rows[1], {key:["a","b"],value:[40*i,40*i]})); + T(equals(results.rows[2], {key:["a","c"],value:[10*i,10*i]})); + T(equals(results.rows[3], {key:["d"],value:[10*i,10*i]})); + T(equals(results.rows[4], {key:["d","a"],value:[10*i,10*i]})); + T(equals(results.rows[5], {key:["d","b"],value:[10*i,10*i]})); + T(equals(results.rows[6], {key:["d","c"],value:[10*i,10*i]})); } } diff --git a/rel/overlay/share/www/script/test/replication.js b/rel/overlay/share/www/script/test/replication.js index 7cc1f823..25746625 100644 --- a/rel/overlay/share/www/script/test/replication.js +++ b/rel/overlay/share/www/script/test/replication.js @@ -12,16 +12,30 @@ couchTests.replication = function(debug) { if (debug) debugger; + + function waitForSeq(sourceDb, targetDb) { + var targetSeq, + sourceSeq = sourceDb.info().update_seq, + t0 = new Date(), + t1, + ms = 3000; + + do { + targetSeq = targetDb.info().update_seq; + t1 = new Date(); + } while (((t1 - t0) <= ms) && targetSeq < sourceSeq); + } + var host = CouchDB.host; var dbPairs = [ {source:"test_suite_db_a", target:"test_suite_db_b"}, {source:"test_suite_db_a", - target:"http://" + host + "/test_suite_db_b"}, - {source:"http://" + host + "/test_suite_db_a", + target:CouchDB.protocol + host + "/test_suite_db_b"}, + {source:CouchDB.protocol + host + "/test_suite_db_a", target:"test_suite_db_b"}, - {source:"http://" + host + "/test_suite_db_a", - target:"http://" + host + "/test_suite_db_b"} + {source:CouchDB.protocol + host + "/test_suite_db_a", + target:CouchDB.protocol + host + "/test_suite_db_b"} ]; var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"}); var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"}); @@ -296,7 +310,7 @@ couchTests.replication = function(debug) { // remote dbB.deleteDb(); - CouchDB.replicate(dbA.name, "http://" + CouchDB.host + "/test_suite_db_b", { + CouchDB.replicate(dbA.name, CouchDB.protocol + CouchDB.host + "/test_suite_db_b", { body: {"create_target": true} }); TEquals("test_suite_db_b", dbB.info().db_name, @@ -310,14 +324,14 @@ couchTests.replication = function(debug) { T(continuousResult._local_id); var cancelResult = CouchDB.replicate(dbA.name, "test_suite_db_b", { - body: {"cancel": true} + body: {"continuous":true, "cancel": true} }); T(cancelResult.ok); T(continuousResult._local_id == cancelResult._local_id); try { var cancelResult2 = CouchDB.replicate(dbA.name, "test_suite_db_b", { - body: {"cancel": true} + body: {"continuous":true, "cancel": true} }); } catch (e) { T(e.error == "not_found"); @@ -372,11 +386,11 @@ couchTests.replication = function(debug) { {source:"test_suite_rep_docs_db_a", target:"test_suite_rep_docs_db_b"}, {source:"test_suite_rep_docs_db_a", - target:"http://" + host + "/test_suite_rep_docs_db_b"}, - {source:"http://" + host + "/test_suite_rep_docs_db_a", + target:CouchDB.protocol + host + "/test_suite_rep_docs_db_b"}, + {source:CouchDB.protocol + host + "/test_suite_rep_docs_db_a", target:"test_suite_rep_docs_db_b"}, - {source:"http://" + host + "/test_suite_rep_docs_db_a", - target:"http://" + host + "/test_suite_rep_docs_db_b"} + {source:CouchDB.protocol + host + "/test_suite_rep_docs_db_a", + target:CouchDB.protocol + host + "/test_suite_rep_docs_db_b"} ]; var target_doc_ids = [ @@ -399,24 +413,25 @@ couchTests.replication = function(debug) { var valid_doc_ids = []; var invalid_doc_ids = []; - $.each(doc_ids, function(index, id) { - var found = false; + for (var p = 0; p < doc_ids.length; p++) { + var id = doc_ids[p]; + var found = false; - for (var k = 0; k < all_docs.length; k++) { - var doc = all_docs[k]; + for (var k = 0; k < all_docs.length; k++) { + var doc = all_docs[k]; - if (id === doc._id) { - found = true; - break; - } - } + if (id === doc._id) { + found = true; + break; + } + } - if (found) { - valid_doc_ids.push(id); - } else { - invalid_doc_ids.push(id); - } - }); + if (found) { + valid_doc_ids.push(id); + } else { + invalid_doc_ids.push(id); + } + }; dbB.deleteDb(); dbB.createDb(); @@ -434,7 +449,7 @@ couchTests.replication = function(debug) { var doc = all_docs[k]; var tgt_doc = dbB.open(doc._id); - if ($.inArray(doc._id, doc_ids) >= 0) { + if (doc_ids.indexOf(doc._id) >= 0) { T(tgt_doc !== null); T(tgt_doc.value === doc.value); } else { @@ -451,45 +466,50 @@ couchTests.replication = function(debug) { } // test filtered replication - - var sourceDb = new CouchDB( - "test_suite_filtered_rep_db_a", {"X-Couch-Full-Commit":"false"} - ); - - sourceDb.deleteDb(); - sourceDb.createDb(); - - T(sourceDb.save({_id:"foo1",value:1}).ok); - T(sourceDb.save({_id:"foo2",value:2}).ok); - T(sourceDb.save({_id:"foo3",value:3}).ok); - T(sourceDb.save({_id:"foo4",value:4}).ok); - T(sourceDb.save({ - "_id": "_design/mydesign", - "language" : "javascript", - "filters" : { - "myfilter" : (function(doc, req) { - if (doc.value < Number(req.query.maxvalue)) { - return true; - } else { - return false; - } - }).toString() + var filterFun1 = (function(doc, req) { + if (doc.value < Number(req.query.maxvalue)) { + return true; + } else { + return false; } - }).ok); + }).toString(); + + var filterFun2 = (function(doc, req) { + return true; + }).toString(); var dbPairs = [ {source:"test_suite_filtered_rep_db_a", target:"test_suite_filtered_rep_db_b"}, {source:"test_suite_filtered_rep_db_a", - target:"http://" + host + "/test_suite_filtered_rep_db_b"}, - {source:"http://" + host + "/test_suite_filtered_rep_db_a", + target:CouchDB.protocol + host + "/test_suite_filtered_rep_db_b"}, + {source:CouchDB.protocol + host + "/test_suite_filtered_rep_db_a", target:"test_suite_filtered_rep_db_b"}, - {source:"http://" + host + "/test_suite_filtered_rep_db_a", - target:"http://" + host + "/test_suite_filtered_rep_db_b"} + {source:CouchDB.protocol + host + "/test_suite_filtered_rep_db_a", + target:CouchDB.protocol + host + "/test_suite_filtered_rep_db_b"} ]; + var sourceDb = new CouchDB("test_suite_filtered_rep_db_a"); + var targetDb = new CouchDB("test_suite_filtered_rep_db_b"); for (var i = 0; i < dbPairs.length; i++) { - var targetDb = new CouchDB("test_suite_filtered_rep_db_b"); + sourceDb.deleteDb(); + sourceDb.createDb(); + + T(sourceDb.save({_id: "foo1", value: 1}).ok); + T(sourceDb.save({_id: "foo2", value: 2}).ok); + T(sourceDb.save({_id: "foo3", value: 3}).ok); + T(sourceDb.save({_id: "foo4", value: 4}).ok); + + var ddoc = { + "_id": "_design/mydesign", + "language": "javascript", + "filters": { + "myfilter": filterFun1 + } + }; + + T(sourceDb.save(ddoc).ok); + targetDb.deleteDb(); targetDb.createDb(); @@ -506,7 +526,7 @@ couchTests.replication = function(debug) { }); T(repResult.ok); - T($.isArray(repResult.history)); + T(repResult.history instanceof Array); T(repResult.history.length === 1); T(repResult.history[0].docs_written === 2); T(repResult.history[0].docs_read === 2); @@ -525,6 +545,45 @@ couchTests.replication = function(debug) { var docFoo4 = targetDb.open("foo4"); T(docFoo4 === null); + + // replication should start from scratch after the filter's code changed + + ddoc.filters.myfilter = filterFun2; + T(sourceDb.save(ddoc).ok); + + repResult = CouchDB.replicate(dbA, dbB, { + body: { + "filter" : "mydesign/myfilter", + "query_params" : { + "maxvalue": "3" + } + } + }); + + T(repResult.ok); + T(repResult.history instanceof Array); + T(repResult.history.length === 1); + T(repResult.history[0].docs_written === 3); + T(repResult.history[0].docs_read === 3); + T(repResult.history[0].doc_write_failures === 0); + + docFoo1 = targetDb.open("foo1"); + T(docFoo1 !== null); + T(docFoo1.value === 1); + + docFoo2 = targetDb.open("foo2"); + T(docFoo2 !== null); + T(docFoo2.value === 2); + + docFoo3 = targetDb.open("foo3"); + T(docFoo3 !== null); + T(docFoo3.value === 3); + + docFoo4 = targetDb.open("foo4"); + T(docFoo4 !== null); + T(docFoo4.value === 4); + + T(targetDb.open("_design/mydesign") !== null); } // test for COUCHDB-868 - design docs' attachments not getting replicated @@ -610,7 +669,7 @@ couchTests.replication = function(debug) { T(CouchDB.session().userCtx.roles.indexOf("_admin") === -1); var repResult = CouchDB.replicate( - "http://fdmanana:qwerty@" + host + "/" + dbA.name, + CouchDB.protocol + "fdmanana:qwerty@" + host + "/" + dbA.name, dbB.name ); T(repResult.ok === true); @@ -662,7 +721,7 @@ couchTests.replication = function(debug) { T(CouchDB.session().userCtx.roles.indexOf("_admin") === -1); try { repResult = CouchDB.replicate( - "http://fdmanana:qwerty@" + host + "/" + dbA.name, + CouchDB.protocol + "fdmanana:qwerty@" + host + "/" + dbA.name, dbB.name ); T(false, "replication should have failed"); @@ -679,6 +738,132 @@ couchTests.replication = function(debug) { run_on_modified_server(server_config, test_fun); + // COUCHDB-1093 - filtered and continuous _changes feed dies when the + // database is compacted + dbA = new CouchDB("test_suite_db_a"); + dbB = new CouchDB("test_suite_db_b"); + + dbA.deleteDb(); + dbA.createDb(); + dbB.deleteDb(); + dbB.createDb(); + + var docs = makeDocs(1, 10); + docs.push({ + _id: "_design/foo", + language: "javascript", + filters: { + myfilter: (function(doc, req) { return true; }).toString() + } + }); + dbA.bulkSave(docs).ok; + + var repResult = CouchDB.replicate( + CouchDB.protocol + host + "/" + dbA.name, + dbB.name, + { + body: { + continuous: true, + filter: "foo/myfilter" + } + } + ); + TEquals(true, repResult.ok); + TEquals('string', typeof repResult._local_id); + + var xhr = CouchDB.request("GET", "/_active_tasks"); + var tasks = JSON.parse(xhr.responseText); + + TEquals(true, dbA.compact().ok); + while (dbA.info().compact_running) {}; + + TEquals(true, dbA.save(makeDocs(30, 31)[0]).ok); + xhr = CouchDB.request("GET", "/_active_tasks"); + + var tasksAfter = JSON.parse(xhr.responseText); + TEquals(tasks.length, tasksAfter.length); + waitForSeq(dbA, dbB); + T(dbB.open("30") !== null); + + repResult = CouchDB.replicate( + CouchDB.protocol + host + "/" + dbA.name, + dbB.name, + { + body: { + continuous: true, + filter: "foo/myfilter", + cancel: true + } + } + ); + TEquals(true, repResult.ok); + TEquals('string', typeof repResult._local_id); + + + // COUCHDB-885 - push replication of a doc with attachment causes a + // conflict in the target. + dbA = new CouchDB("test_suite_db_a"); + dbB = new CouchDB("test_suite_db_b"); + + dbA.deleteDb(); + dbA.createDb(); + dbB.deleteDb(); + dbB.createDb(); + + var doc = { + _id: "doc1" + }; + TEquals(true, dbA.save(doc).ok); + + repResult = CouchDB.replicate( + dbA.name, + CouchDB.protocol + host + "/" + dbB.name + ); + TEquals(true, repResult.ok); + TEquals(true, repResult.history instanceof Array); + TEquals(1, repResult.history.length); + TEquals(1, repResult.history[0].docs_written); + TEquals(1, repResult.history[0].docs_read); + TEquals(0, repResult.history[0].doc_write_failures); + + doc["_attachments"] = { + "hello.txt": { + "content_type": "text/plain", + "data": "aGVsbG8gd29ybGQ=" // base64:encode("hello world") + }, + "foo.dat": { + "content_type": "not/compressible", + "data": "aSBhbSBub3QgZ3ppcGVk" // base64:encode("i am not gziped") + } + }; + + TEquals(true, dbA.save(doc).ok); + repResult = CouchDB.replicate( + dbA.name, + CouchDB.protocol + host + "/" + dbB.name + ); + TEquals(true, repResult.ok); + TEquals(true, repResult.history instanceof Array); + TEquals(2, repResult.history.length); + TEquals(1, repResult.history[0].docs_written); + TEquals(1, repResult.history[0].docs_read); + TEquals(0, repResult.history[0].doc_write_failures); + + var copy = dbB.open(doc._id, { + conflicts: true, deleted_conflicts: true, attachments: true, + att_encoding_info: true}); + T(copy !== null); + TEquals("undefined", typeof copy._conflicts); + TEquals("undefined", typeof copy._deleted_conflicts); + TEquals("text/plain", copy._attachments["hello.txt"]["content_type"]); + TEquals("aGVsbG8gd29ybGQ=", copy._attachments["hello.txt"]["data"]); + TEquals("gzip", copy._attachments["hello.txt"]["encoding"]); + TEquals("not/compressible", copy._attachments["foo.dat"]["content_type"]); + TEquals("aSBhbSBub3QgZ3ppcGVk", copy._attachments["foo.dat"]["data"]); + TEquals("undefined", typeof copy._attachments["foo.dat"]["encoding"]); + // end of test for COUCHDB-885 + + // cleanup dbA.deleteDb(); dbB.deleteDb(); diff --git a/rel/overlay/share/www/script/test/rewrite.js b/rel/overlay/share/www/script/test/rewrite.js index ff2d3822..ac5257da 100644 --- a/rel/overlay/share/www/script/test/rewrite.js +++ b/rel/overlay/share/www/script/test/rewrite.js @@ -84,6 +84,24 @@ couchTests.rewrite = function(debug) { "method": "GET" }, { + "from": "/welcome4/*", + "to" : "_show/welcome3", + "query": { + "name": "*" + } + }, + { + "from": "/welcome5/*", + "to" : "_show/*", + "query": { + "name": "*" + } + }, + { + "from": "basicView", + "to": "_view/basicView", + }, + { "from": "simpleForm/basicView", "to": "_list/simpleForm/basicView", }, @@ -101,6 +119,10 @@ couchTests.rewrite = function(debug) { "query": { "startkey": ":start", "endkey": ":end" + }, + "formats": { + "start": "int", + "end": "int" } }, { @@ -144,6 +166,22 @@ couchTests.rewrite = function(debug) { "query": { "key": [":a", ":b"] } + }, + { + "from": "simpleForm/complexView7/:a/:b", + "to": "_view/complexView3", + "query": { + "key": [":a", ":b"], + "include_docs": ":doc" + }, + "format": { + "doc": "bool" + } + + }, + { + "from": "/", + "to": "_view/basicView", } ], lists: { @@ -169,6 +207,9 @@ couchTests.rewrite = function(debug) { "welcome2": stringFun(function(doc, req) { return "Welcome " + doc.name; }), + "welcome3": stringFun(function(doc,req) { + return "Welcome " + req.query["name"]; + }) }, updates: { "hello" : stringFun(function(doc, req) { @@ -289,6 +330,20 @@ couchTests.rewrite = function(debug) { xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome3/test"); T(xhr.responseText == "Welcome test"); + req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome4/user"); + T(req.responseText == "Welcome user"); + + req = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/welcome5/welcome3"); + T(req.responseText == "Welcome welcome3"); + + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/basicView"); + T(xhr.status == 200, "view call"); + T(/{"total_rows":9/.test(xhr.responseText)); + + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/"); + T(xhr.status == 200, "view call"); + T(/{"total_rows":9/.test(xhr.responseText)); + // get with query params xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8"); @@ -342,6 +397,11 @@ couchTests.rewrite = function(debug) { T(xhr.status == 200, "with query params"); T(/Value: doc 4/.test(xhr.responseText)); + xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView7/test/essai?doc=true"); + T(xhr.status == 200, "with query params"); + var result = JSON.parse(xhr.responseText); + T(typeof(result.rows[0].doc) === "object"); + // test path relative to server designDoc.rewrites.push({ "from": "uuids", diff --git a/rel/overlay/share/www/script/test/security_validation.js b/rel/overlay/share/www/script/test/security_validation.js index dd3b202e..42aa11c9 100644 --- a/rel/overlay/share/www/script/test/security_validation.js +++ b/rel/overlay/share/www/script/test/security_validation.js @@ -250,16 +250,16 @@ couchTests.security_validation = function(debug) { target:"test_suite_db_b"}, {source:"test_suite_db_a", - target:{url: "http://" + host + "/test_suite_db_b", + target:{url: CouchDB.protocol + host + "/test_suite_db_b", headers: AuthHeaders}}, - {source:{url:"http://" + host + "/test_suite_db_a", + {source:{url:CouchDB.protocol + host + "/test_suite_db_a", headers: AuthHeaders}, target:"test_suite_db_b"}, - {source:{url:"http://" + host + "/test_suite_db_a", + {source:{url:CouchDB.protocol + host + "/test_suite_db_a", headers: AuthHeaders}, - target:{url:"http://" + host + "/test_suite_db_b", + target:{url:CouchDB.protocol + host + "/test_suite_db_b", headers: AuthHeaders}}, ] var adminDbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"}); diff --git a/rel/overlay/share/www/script/test/show_documents.js b/rel/overlay/share/www/script/test/show_documents.js index e06bcadc..55ed9698 100644 --- a/rel/overlay/share/www/script/test/show_documents.js +++ b/rel/overlay/share/www/script/test/show_documents.js @@ -157,6 +157,9 @@ couchTests.show_documents = function(debug) { }), "withSlash": stringFun(function(doc, req) { return { json: doc } + }), + "secObj": stringFun(function(doc, req) { + return { json: req.secObj }; }) } }; @@ -410,5 +413,24 @@ couchTests.show_documents = function(debug) { db.deleteDoc(doc); var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc"); TEquals("No doc testdoc", xhr.responseText, "should return 'no doc testdoc'"); + + + run_on_modified_server( + [{section: "httpd", + key: "authentication_handlers", + value: "{couch_httpd_auth, special_test_authentication_handler}"}, + {section:"httpd", + key: "WWW-Authenticate", + value: "X-Couch-Test-Auth"}], + + function() { + T(db.setDbProperty("_security", {foo: true}).ok); + T(db.save(doc).ok); + + xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/secObj"); + var resp = JSON.parse(xhr.responseText); + T(resp.foo == true); + } + ); }; diff --git a/rel/overlay/share/www/script/test/update_documents.js b/rel/overlay/share/www/script/test/update_documents.js index da68d621..49d3b68a 100644 --- a/rel/overlay/share/www/script/test/update_documents.js +++ b/rel/overlay/share/www/script/test/update_documents.js @@ -113,9 +113,13 @@ couchTests.update_documents = function(debug) { T(JSON.parse(xhr.responseText).error == "method_not_allowed"); // // hello update world (non-existing docid) + xhr = CouchDB.request("GET", "/test_suite_db/nonExistingDoc"); + T(xhr.status == 404); xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/hello/nonExistingDoc"); T(xhr.status == 201); T(xhr.responseText == "<p>New World</p>"); + xhr = CouchDB.request("GET", "/test_suite_db/nonExistingDoc"); + T(xhr.status == 200); // in place update xhr = CouchDB.request("PUT", "/test_suite_db/_design/update/_update/in-place/"+docid+'?field=title&value=test'); diff --git a/rel/overlay/share/www/script/test/view_errors.js b/rel/overlay/share/www/script/test/view_errors.js index c05000b7..e8bd08e4 100644 --- a/rel/overlay/share/www/script/test/view_errors.js +++ b/rel/overlay/share/www/script/test/view_errors.js @@ -177,5 +177,13 @@ couchTests.view_errors = function(debug) { T(xhr.status == 500); result = JSON.parse(xhr.responseText); T(result.error == "reduce_overflow_error"); + + try { + db.query(function() {emit(null, null)}, null, {startkey: 2, endkey:1}); + T(0 == 1); + } catch(e) { + T(e.error == "query_parse_error"); + T(e.reason.match(/no rows can match/i)); + } }); }; diff --git a/rel/overlay/share/www/script/test/view_include_docs.js b/rel/overlay/share/www/script/test/view_include_docs.js index 06aafc56..944c9103 100644 --- a/rel/overlay/share/www/script/test/view_include_docs.js +++ b/rel/overlay/share/www/script/test/view_include_docs.js @@ -135,4 +135,58 @@ couchTests.view_include_docs = function(debug) { T(!resp.rows[0].doc); T(resp.rows[0].doc == null); T(resp.rows[1].doc.integer == 23); + + // COUCHDB-549 - include_docs=true with conflicts=true + + var dbA = new CouchDB("test_suite_db_a", {"X-Couch-Full-Commit":"false"}); + var dbB = new CouchDB("test_suite_db_b", {"X-Couch-Full-Commit":"false"}); + + dbA.deleteDb(); + dbA.createDb(); + dbB.deleteDb(); + dbB.createDb(); + + var ddoc = { + _id: "_design/mydesign", + language : "javascript", + views : { + myview : { + map: (function(doc) { + emit(doc.value, 1); + }).toString() + } + } + }; + TEquals(true, dbA.save(ddoc).ok); + + var doc1a = {_id: "foo", value: 1, str: "1"}; + TEquals(true, dbA.save(doc1a).ok); + + var doc1b = {_id: "foo", value: 1, str: "666"}; + TEquals(true, dbB.save(doc1b).ok); + + var doc2 = {_id: "bar", value: 2, str: "2"}; + TEquals(true, dbA.save(doc2).ok); + + TEquals(true, CouchDB.replicate(dbA.name, dbB.name).ok); + + doc1b = dbB.open("foo", {conflicts: true}); + TEquals(true, doc1b._conflicts instanceof Array); + TEquals(1, doc1b._conflicts.length); + var conflictRev = doc1b._conflicts[0]; + + doc2 = dbB.open("bar", {conflicts: true}); + TEquals("undefined", typeof doc2._conflicts); + + resp = dbB.view("mydesign/myview", {include_docs: true, conflicts: true}); + + TEquals(2, resp.rows.length); + TEquals(true, resp.rows[0].doc._conflicts instanceof Array); + TEquals(1, resp.rows[0].doc._conflicts.length); + TEquals(conflictRev, resp.rows[0].doc._conflicts[0]); + TEquals("undefined", typeof resp.rows[1].doc._conflicts); + + // cleanup + dbA.deleteDb(); + dbB.deleteDb(); }; diff --git a/rel/overlay/share/www/script/test/view_multi_key_all_docs.js b/rel/overlay/share/www/script/test/view_multi_key_all_docs.js index 62e49665..1113be4d 100644 --- a/rel/overlay/share/www/script/test/view_multi_key_all_docs.js +++ b/rel/overlay/share/www/script/test/view_multi_key_all_docs.js @@ -25,24 +25,52 @@ couchTests.view_multi_key_all_docs = function(debug) { for(var i=0; i<rows.length; i++) T(rows[i].id == keys[i]); + // keys in GET parameters + rows = db.allDocs({keys:keys}, null).rows; + T(rows.length == keys.length); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[i]); + rows = db.allDocs({limit: 1}, keys).rows; T(rows.length == 1); T(rows[0].id == keys[0]); + // keys in GET parameters + rows = db.allDocs({limit: 1, keys: keys}, null).rows; + T(rows.length == 1); + T(rows[0].id == keys[0]); + rows = db.allDocs({skip: 2}, keys).rows; T(rows.length == 3); for(var i=0; i<rows.length; i++) T(rows[i].id == keys[i+2]); + // keys in GET parameters + rows = db.allDocs({skip: 2, keys: keys}, null).rows; + T(rows.length == 3); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[i+2]); + rows = db.allDocs({descending: "true"}, keys).rows; T(rows.length == keys.length); for(var i=0; i<rows.length; i++) T(rows[i].id == keys[keys.length-i-1]); + // keys in GET parameters + rows = db.allDocs({descending: "true", keys: keys}, null).rows; + T(rows.length == keys.length); + for(var i=0; i<rows.length; i++) + T(rows[i].id == keys[keys.length-i-1]); + rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).rows; T(rows.length == 1); T(rows[0].id == keys[1]); + // keys in GET parameters + rows = db.allDocs({descending: "true", skip: 3, limit:1, keys: keys}, null).rows; + T(rows.length == 1); + T(rows[0].id == keys[1]); + // Check we get invalid rows when the key doesn't exist rows = db.allDocs({}, [1, "i_dont_exist", "0"]).rows; T(rows.length == 3); @@ -51,4 +79,13 @@ couchTests.view_multi_key_all_docs = function(debug) { T(rows[1].error == "not_found"); T(!rows[1].id); T(rows[2].id == rows[2].key && rows[2].key == "0"); + + // keys in GET parameters + rows = db.allDocs({keys: [1, "i_dont_exist", "0"]}, null).rows; + T(rows.length == 3); + T(rows[0].error == "not_found"); + T(!rows[0].id); + T(rows[1].error == "not_found"); + T(!rows[1].id); + T(rows[2].id == rows[2].key && rows[2].key == "0"); }; diff --git a/rel/overlay/share/www/script/test/view_multi_key_design.js b/rel/overlay/share/www/script/test/view_multi_key_design.js index c39e73d9..38396955 100644 --- a/rel/overlay/share/www/script/test/view_multi_key_design.js +++ b/rel/overlay/share/www/script/test/view_multi_key_design.js @@ -54,6 +54,13 @@ couchTests.view_multi_key_design = function(debug) { T(rows[i].key == rows[i].value); } + // with GET keys + rows = db.view("test/all_docs",{keys:keys},null).rows; + for(var i=0;i<rows.length; i++) { + T(keys.indexOf(rows[i].key) != -1); + T(rows[i].key == rows[i].value); + } + var reduce = db.view("test/summate",{group:true},keys).rows; T(reduce.length == keys.length); for(var i=0; i<reduce.length; i++) { @@ -61,8 +68,18 @@ couchTests.view_multi_key_design = function(debug) { T(reduce[i].key == reduce[i].value); } + // with GET keys + reduce = db.view("test/summate",{group:true,keys:keys},null).rows; + T(reduce.length == keys.length); + for(var i=0; i<reduce.length; i++) { + T(keys.indexOf(reduce[i].key) != -1); + T(reduce[i].key == reduce[i].value); + } + // Test that invalid parameter combinations get rejected var badargs = [{startkey:0}, {endkey:0}, {key: 0}, {group_level: 2}]; + var getbadargs = [{startkey:0, keys:keys}, {endkey:0, keys:keys}, + {key:0, keys:keys}, {group_level: 2, keys:keys}]; for(var i in badargs) { try { @@ -71,6 +88,13 @@ couchTests.view_multi_key_design = function(debug) { } catch (e) { T(e.error == "query_parse_error"); } + + try { + db.view("test/all_docs",getbadargs[i],null); + T(0==1); + } catch (e) { + T(e.error = "query_parse_error"); + } } try { @@ -80,10 +104,20 @@ couchTests.view_multi_key_design = function(debug) { T(e.error == "query_parse_error"); } + try { + db.view("test/summate",{keys:keys},null); + T(0==1); + } catch (e) { + T(e.error == "query_parse_error"); + } + // Test that a map & reduce containing func support keys when reduce=false var resp = db.view("test/summate", {reduce: false}, keys); T(resp.rows.length == 5); + resp = db.view("test/summate", {reduce: false, keys: keys}, null); + T(resp.rows.length == 5); + // Check that limiting by startkey_docid and endkey_docid get applied // as expected. var curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23}, [0, 2]).rows; @@ -96,34 +130,66 @@ couchTests.view_multi_key_design = function(debug) { T(curr[i].value == exp_val[i]); } + curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23, keys: [0, 2]}, null).rows; + T(curr.length == 6); + for( var i = 0 ; i < 6 ; i++) + { + T(curr[i].key == exp_key[i]); + T(curr[i].value == exp_val[i]); + } + // Check limit works curr = db.view("test/all_docs", {limit: 1}, keys).rows; T(curr.length == 1); T(curr[0].key == 10); + curr = db.view("test/all_docs", {limit: 1, keys: keys}, null).rows; + T(curr.length == 1); + T(curr[0].key == 10); + // Check offset works curr = db.view("test/multi_emit", {skip: 1}, [0]).rows; T(curr.length == 99); T(curr[0].value == 1); + curr = db.view("test/multi_emit", {skip: 1, keys: [0]}, null).rows; + T(curr.length == 99); + T(curr[0].value == 1); + // Check that dir works curr = db.view("test/multi_emit", {descending: "true"}, [1]).rows; T(curr.length == 100); T(curr[0].value == 99); T(curr[99].value == 0); + curr = db.view("test/multi_emit", {descending: "true", keys: [1]}, null).rows; + T(curr.length == 100); + T(curr[0].value == 99); + T(curr[99].value == 0); + // Check a couple combinations curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2}, [2]).rows; T(curr.length, 2); T(curr[0].value == 96); T(curr[1].value == 95); + curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2, keys: [2]}, null).rows; + T(curr.length, 2); + T(curr[0].value == 96); + T(curr[1].value == 95); + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13"}, [0]).rows; T(curr.length == 3); T(curr[0].value == 15); T(curr[1].value == 16); T(curr[2].value == 17); + curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13", keys: [0]}, null).rows; + T(curr.length == 3); + T(curr[0].value == 15); + T(curr[1].value == 16); + T(curr[2].value == 17); + curr = db.view("test/multi_emit", {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).rows; T(curr.length == 2); @@ -131,8 +197,20 @@ couchTests.view_multi_key_design = function(debug) { T(curr[1].value == 27); curr = db.view("test/multi_emit", + {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27", keys: [1]}, null).rows; + T(curr.length == 2); + T(curr[0].value == 26); + T(curr[1].value == 27); + + curr = db.view("test/multi_emit", {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true"}, [1]).rows; T(curr.length == 2); T(curr[0].value == 27); T(curr[1].value == 26); + + curr = db.view("test/multi_emit", + {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true", keys: [1]}, null).rows; + T(curr.length == 2); + T(curr[0].value == 27); + T(curr[1].value == 26); }; diff --git a/rel/overlay/share/www/script/test/view_pagination.js b/rel/overlay/share/www/script/test/view_pagination.js index 1af2df35..ed3a7ee1 100644 --- a/rel/overlay/share/www/script/test/view_pagination.js +++ b/rel/overlay/share/www/script/test/view_pagination.js @@ -19,7 +19,7 @@ couchTests.view_pagination = function(debug) { var docs = makeDocs(0, 100); db.bulkSave(docs); - var queryFun = function(doc) { emit(doc.integer, null) }; + var queryFun = function(doc) { emit(doc.integer, null); }; var i; // page through the view ascending @@ -29,13 +29,26 @@ couchTests.view_pagination = function(debug) { startkey_docid: i, limit: 10 }); - T(queryResults.rows.length == 10) - T(queryResults.total_rows == docs.length) - T(queryResults.offset == i) + T(queryResults.rows.length == 10); + T(queryResults.total_rows == docs.length); + T(queryResults.offset == i); var j; for (j = 0; j < 10;j++) { T(queryResults.rows[j].key == i + j); } + + // test aliases start_key and start_key_doc_id + queryResults = db.query(queryFun, null, { + start_key: i, + start_key_doc_id: i, + limit: 10 + }); + T(queryResults.rows.length == 10); + T(queryResults.total_rows == docs.length); + T(queryResults.offset == i); + for (j = 0; j < 10;j++) { + T(queryResults.rows[j].key == i + j); + } } // page through the view descending @@ -46,9 +59,9 @@ couchTests.view_pagination = function(debug) { descending: true, limit: 10 }); - T(queryResults.rows.length == 10) - T(queryResults.total_rows == docs.length) - T(queryResults.offset == docs.length - i - 1) + T(queryResults.rows.length == 10); + T(queryResults.total_rows == docs.length); + T(queryResults.offset == docs.length - i - 1); var j; for (j = 0; j < 10; j++) { T(queryResults.rows[j].key == i - j); @@ -63,60 +76,72 @@ couchTests.view_pagination = function(debug) { descending: false, limit: 10 }); - T(queryResults.rows.length == 10) - T(queryResults.total_rows == docs.length) - T(queryResults.offset == i) + T(queryResults.rows.length == 10); + T(queryResults.total_rows == docs.length); + T(queryResults.offset == i); var j; for (j = 0; j < 10;j++) { T(queryResults.rows[j].key == i + j); } } + function testEndkeyDocId(queryResults) { + T(queryResults.rows.length == 35); + T(queryResults.total_rows == docs.length); + T(queryResults.offset == 1); + T(queryResults.rows[0].id == "1"); + T(queryResults.rows[1].id == "10"); + T(queryResults.rows[2].id == "11"); + T(queryResults.rows[3].id == "12"); + T(queryResults.rows[4].id == "13"); + T(queryResults.rows[5].id == "14"); + T(queryResults.rows[6].id == "15"); + T(queryResults.rows[7].id == "16"); + T(queryResults.rows[8].id == "17"); + T(queryResults.rows[9].id == "18"); + T(queryResults.rows[10].id == "19"); + T(queryResults.rows[11].id == "2"); + T(queryResults.rows[12].id == "20"); + T(queryResults.rows[13].id == "21"); + T(queryResults.rows[14].id == "22"); + T(queryResults.rows[15].id == "23"); + T(queryResults.rows[16].id == "24"); + T(queryResults.rows[17].id == "25"); + T(queryResults.rows[18].id == "26"); + T(queryResults.rows[19].id == "27"); + T(queryResults.rows[20].id == "28"); + T(queryResults.rows[21].id == "29"); + T(queryResults.rows[22].id == "3"); + T(queryResults.rows[23].id == "30"); + T(queryResults.rows[24].id == "31"); + T(queryResults.rows[25].id == "32"); + T(queryResults.rows[26].id == "33"); + T(queryResults.rows[27].id == "34"); + T(queryResults.rows[28].id == "35"); + T(queryResults.rows[29].id == "36"); + T(queryResults.rows[30].id == "37"); + T(queryResults.rows[31].id == "38"); + T(queryResults.rows[32].id == "39"); + T(queryResults.rows[33].id == "4"); + T(queryResults.rows[34].id == "40"); + } + // test endkey_docid - var queryResults = db.query(function(doc) { emit(null, null);}, null, { + var queryResults = db.query(function(doc) { emit(null, null); }, null, { startkey: null, startkey_docid: 1, endkey: null, endkey_docid: 40 }); + testEndkeyDocId(queryResults); - T(queryResults.rows.length == 35) - T(queryResults.total_rows == docs.length) - T(queryResults.offset == 1) - T(queryResults.rows[0].id == "1"); - T(queryResults.rows[1].id == "10"); - T(queryResults.rows[2].id == "11"); - T(queryResults.rows[3].id == "12"); - T(queryResults.rows[4].id == "13"); - T(queryResults.rows[5].id == "14"); - T(queryResults.rows[6].id == "15"); - T(queryResults.rows[7].id == "16"); - T(queryResults.rows[8].id == "17"); - T(queryResults.rows[9].id == "18"); - T(queryResults.rows[10].id == "19"); - T(queryResults.rows[11].id == "2"); - T(queryResults.rows[12].id == "20"); - T(queryResults.rows[13].id == "21"); - T(queryResults.rows[14].id == "22"); - T(queryResults.rows[15].id == "23"); - T(queryResults.rows[16].id == "24"); - T(queryResults.rows[17].id == "25"); - T(queryResults.rows[18].id == "26"); - T(queryResults.rows[19].id == "27"); - T(queryResults.rows[20].id == "28"); - T(queryResults.rows[21].id == "29"); - T(queryResults.rows[22].id == "3"); - T(queryResults.rows[23].id == "30"); - T(queryResults.rows[24].id == "31"); - T(queryResults.rows[25].id == "32"); - T(queryResults.rows[26].id == "33"); - T(queryResults.rows[27].id == "34"); - T(queryResults.rows[28].id == "35"); - T(queryResults.rows[29].id == "36"); - T(queryResults.rows[30].id == "37"); - T(queryResults.rows[31].id == "38"); - T(queryResults.rows[32].id == "39"); - T(queryResults.rows[33].id == "4"); - T(queryResults.rows[34].id == "40"); + // test aliases end_key_doc_id and end_key + queryResults = db.query(function(doc) { emit(null, null); }, null, { + start_key: null, + start_key_doc_id: 1, + end_key: null, + end_key_doc_id: 40 + }); + testEndkeyDocId(queryResults); }; diff --git a/rel/overlay/share/www/script/test/view_update_seq.js b/rel/overlay/share/www/script/test/view_update_seq.js index 9757caa1..69b8c42d 100644 --- a/rel/overlay/share/www/script/test/view_update_seq.js +++ b/rel/overlay/share/www/script/test/view_update_seq.js @@ -73,17 +73,34 @@ couchTests.view_update_seq = function(debug) { T(resp.rows.length == 1); T(resp.update_seq == 101); + db.save({"id":"00"}); + resp = db.view('test/all_docs', + {limit: 1, stale: "update_after", update_seq: true}); + T(resp.rows.length == 1); + T(resp.update_seq == 101); + + // wait 5 seconds for the next assertions to pass in very slow machines + var t0 = new Date(), t1; + do { + CouchDB.request("GET", "/"); + t1 = new Date(); + } while ((t1 - t0) < 5000); + + resp = db.view('test/all_docs', {limit: 1, stale: "ok", update_seq: true}); + T(resp.rows.length == 1); + T(resp.update_seq == 103); + resp = db.view('test/all_docs', {limit: 1, update_seq:true}); T(resp.rows.length == 1); - T(resp.update_seq == 102); + T(resp.update_seq == 103); resp = db.view('test/all_docs',{update_seq:true},["0","1"]); - T(resp.update_seq == 102); + T(resp.update_seq == 103); resp = db.view('test/all_docs',{update_seq:true},["0","1"]); - T(resp.update_seq == 102); + T(resp.update_seq == 103); resp = db.view('test/summate',{group:true, update_seq:true},["0","1"]); - T(resp.update_seq == 102); + T(resp.update_seq == 103); }; |