diff options
Diffstat (limited to 'share')
-rw-r--r-- | share/www/script/couch_tests.js | 1 | ||||
-rw-r--r-- | share/www/script/jquery.couch.js | 8 | ||||
-rw-r--r-- | share/www/script/test/attachments_multipart.js | 9 | ||||
-rw-r--r-- | share/www/script/test/changes.js | 4 | ||||
-rw-r--r-- | share/www/script/test/regression.js | 48 | ||||
-rw-r--r-- | share/www/script/test/replication.js | 79 | ||||
-rw-r--r-- | share/www/script/test/replicator_db.js | 261 | ||||
-rw-r--r-- | share/www/script/test/rewrite.js | 25 |
8 files changed, 412 insertions, 23 deletions
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index eb573526..4c5c00d6 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -76,6 +76,7 @@ loadTest("reduce.js"); loadTest("reduce_builtin.js"); loadTest("reduce_false.js"); loadTest("reduce_false_temp.js"); +loadTest("regression.js"); loadTest("replication.js"); loadTest("replicator_db.js"); loadTest("rev_stemming.js"); diff --git a/share/www/script/jquery.couch.js b/share/www/script/jquery.couch.js index edae18fc..9bc1363b 100644 --- a/share/www/script/jquery.couch.js +++ b/share/www/script/jquery.couch.js @@ -624,8 +624,14 @@ }; 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, diff --git a/share/www/script/test/attachments_multipart.js b/share/www/script/test/attachments_multipart.js index 7f587357..4f4590fc 100644 --- a/share/www/script/test/attachments_multipart.js +++ b/share/www/script/test/attachments_multipart.js @@ -39,7 +39,7 @@ couchTests.attachments_multipart= function(debug) { }, "baz.txt": { "follows":true, - "content_type":"application/test", + "content_type":"text/plain", "length":19 } } @@ -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"); diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js index 5998f48c..ea22bfb3 100644 --- a/share/www/script/test/changes.js +++ b/share/www/script/test/changes.js @@ -503,6 +503,10 @@ couchTests.changes = function(debug) { 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/share/www/script/test/regression.js b/share/www/script/test/regression.js new file mode 100644 index 00000000..abe42b40 --- /dev/null +++ b/share/www/script/test/regression.js @@ -0,0 +1,48 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.regression = function(debug) { + var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"}); + db.deleteDb(); + db.createDb(); + if (debug) debugger; + + // 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"); + } + + // cleanup + db.deleteDb(); +};
\ No newline at end of file diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js index 7f92891e..5e85847e 100644 --- a/share/www/script/test/replication.js +++ b/share/www/script/test/replication.js @@ -12,6 +12,20 @@ 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", @@ -768,6 +782,7 @@ couchTests.replication = function(debug) { var tasksAfter = JSON.parse(xhr.responseText); TEquals(tasks.length, tasksAfter.length); + waitForSeq(dbA, dbB); T(dbB.open("30") !== null); repResult = CouchDB.replicate( @@ -785,6 +800,70 @@ couchTests.replication = function(debug) { 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/share/www/script/test/replicator_db.js b/share/www/script/test/replicator_db.js index 2810352c..4434124e 100644 --- a/share/www/script/test/replicator_db.js +++ b/share/www/script/test/replicator_db.js @@ -121,7 +121,7 @@ couchTests.replicator_db = function(debug) { T(repDoc1.source === repDoc.source); T(repDoc1.target === repDoc.target); T(repDoc1._replication_state === "completed", "simple"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); } @@ -173,7 +173,7 @@ couchTests.replicator_db = function(debug) { T(repDoc1.source === repDoc.source); T(repDoc1.target === repDoc.target); T(repDoc1._replication_state === "completed", "filtered"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); } @@ -186,7 +186,10 @@ couchTests.replicator_db = function(debug) { _id: "foo_cont_rep_doc", source: "http://" + host + "/" + dbA.name, target: dbB.name, - continuous: true + continuous: true, + user_ctx: { + roles: ["_admin"] + } }; T(repDb.save(repDoc).ok); @@ -217,13 +220,11 @@ couchTests.replicator_db = function(debug) { T(repDoc1.source === repDoc.source); T(repDoc1.target === repDoc.target); T(repDoc1._replication_state === "triggered"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); - // add a design doc to source, it will be replicated to target - // when the "user_ctx" property is not defined in the replication doc, - // the replication will be done under an _admin context, therefore - // design docs will be replicated + // Design documents are only replicated to local targets if the respective + // replication document has a user_ctx filed with the "_admin" role in it. var ddoc = { _id: "_design/foobar", language: "javascript" @@ -303,8 +304,7 @@ couchTests.replicator_db = function(debug) { T(copy === null); copy = dbB.open("_design/mydesign"); - T(copy !== null); - T(copy.language === "javascript"); + T(copy === null); } @@ -332,7 +332,7 @@ couchTests.replicator_db = function(debug) { T(repDoc1_copy.source === repDoc1.source); T(repDoc1_copy.target === repDoc1.target); T(repDoc1_copy._replication_state === "completed"); - T(typeof repDoc1_copy._replication_state_time === "number"); + T(typeof repDoc1_copy._replication_state_time === "string"); T(typeof repDoc1_copy._replication_id === "string"); var newDoc = { @@ -363,7 +363,7 @@ couchTests.replicator_db = function(debug) { T(repDoc2_copy.source === repDoc1.source); T(repDoc2_copy.target === repDoc1.target); T(repDoc2_copy._replication_state === "completed"); - T(typeof repDoc2_copy._replication_state_time === "number"); + T(typeof repDoc2_copy._replication_state_time === "string"); T(typeof repDoc2_copy._replication_id === "string"); T(repDoc2_copy._replication_id === repDoc1_copy._replication_id); } @@ -400,7 +400,7 @@ couchTests.replicator_db = function(debug) { repDoc1 = repDb.open("foo_dup_rep_doc_1"); T(repDoc1 !== null); T(repDoc1._replication_state === "completed", "identical"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); repDoc2 = repDb.open("foo_dup_rep_doc_2"); @@ -444,7 +444,7 @@ couchTests.replicator_db = function(debug) { repDoc1 = repDb.open("foo_dup_cont_rep_doc_1"); T(repDoc1 !== null); T(repDoc1._replication_state === "triggered"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); repDoc2 = repDb.open("foo_dup_cont_rep_doc_2"); @@ -470,7 +470,7 @@ couchTests.replicator_db = function(debug) { repDoc1 = repDb.open("foo_dup_cont_rep_doc_1"); T(repDoc1 !== null); T(repDoc1._replication_state === "triggered"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); var newDoc2 = { _id: "foo5000", @@ -676,7 +676,8 @@ couchTests.replicator_db = function(debug) { var repDoc = { _id: "foo_rep_doc", source: dbA.name, - target: dbB.name + target: dbB.name, + continuous: true }; T(CouchDB.login("fdmanana", "qwerty").ok); @@ -712,6 +713,225 @@ couchTests.replicator_db = function(debug) { } + function test_user_ctx_validation() { + populate_db(dbA, docs1); + populate_db(dbB, []); + populate_db(usersDb, []); + + var joeUserDoc = CouchDB.prepareUserDoc({ + name: "joe", + roles: ["erlanger", "bar"] + }, "erly"); + var fdmananaUserDoc = CouchDB.prepareUserDoc({ + name: "fdmanana", + roles: ["a", "b", "c"] + }, "qwerty"); + + TEquals(true, usersDb.save(joeUserDoc).ok); + TEquals(true, usersDb.save(fdmananaUserDoc).ok); + + T(dbB.setSecObj({ + admins: { + names: [], + roles: ["god"] + }, + readers: { + names: [], + roles: ["foo"] + } + }).ok); + + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + var repDoc = { + _id: "foo_rep", + source: CouchDB.protocol + host + "/" + dbA.name, + target: dbB.name + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, user_ctx missing."); + } catch (x) { + TEquals("forbidden", x.error); + } + + repDoc.user_ctx = { + name: "john", + roles: ["erlanger"] + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, wrong user_ctx.name."); + } catch (x) { + TEquals("forbidden", x.error); + } + + repDoc.user_ctx = { + name: "joe", + roles: ["bar", "god", "erlanger"] + }; + + try { + repDb.save(repDoc); + T(false, "Should have failed, a bad role in user_ctx.roles."); + } catch (x) { + TEquals("forbidden", x.error); + } + + // user_ctx.roles might contain only a subset of the user's roles + repDoc.user_ctx = { + name: "joe", + roles: ["erlanger"] + }; + + TEquals(true, repDb.save(repDoc).ok); + CouchDB.logout(); + + waitForRep(repDb, repDoc, "error"); + var repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("error", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + TEquals(true, CouchDB.login("fdmanana", "qwerty").ok); + TEquals("fdmanana", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + try { + T(repDb.deleteDoc(repDoc1).ok); + T(false, "Shouldn't be able to delete replication document."); + } catch (x) { + TEquals("forbidden", x.error); + } + + CouchDB.logout(); + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + T(repDb.deleteDoc(repDoc1).ok); + CouchDB.logout(); + + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + + TEquals(null, copy); + } + + T(dbB.setSecObj({ + admins: { + names: [], + roles: ["god", "erlanger"] + }, + readers: { + names: [], + roles: ["foo"] + } + }).ok); + + TEquals(true, CouchDB.login("joe", "erly").ok); + TEquals("joe", CouchDB.session().userCtx.name); + TEquals(-1, CouchDB.session().userCtx.roles.indexOf("_admin")); + + repDoc = { + _id: "foo_rep_2", + source: CouchDB.protocol + host + "/" + dbA.name, + target: dbB.name, + user_ctx: { + name: "joe", + roles: ["erlanger"] + } + }; + + TEquals(true, repDb.save(repDoc).ok); + CouchDB.logout(); + + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + for (var i = 0; i < docs1.length; i++) { + var doc = docs1[i]; + var copy = dbB.open(doc._id); + + T(copy !== null); + TEquals(doc.value, copy.value); + } + + // Admins don't need to supply a user_ctx property in replication docs. + // If they do not, the implicit user_ctx "user_ctx": {name: null, roles: []} + // is used, meaning that design documents will not be replicated into + // local targets + T(dbB.setSecObj({ + admins: { + names: [], + roles: [] + }, + readers: { + names: [], + roles: [] + } + }).ok); + + var ddoc = { _id: "_design/foo" }; + TEquals(true, dbA.save(ddoc).ok); + + repDoc = { + _id: "foo_rep_3", + source: CouchDB.protocol + host + "/" + dbA.name, + target: dbB.name + }; + + TEquals(true, repDb.save(repDoc).ok); + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + var ddoc_copy = dbB.open(ddoc._id); + T(ddoc_copy === null); + + repDoc = { + _id: "foo_rep_4", + source: CouchDB.protocol + host + "/" + dbA.name, + target: dbB.name, + user_ctx: { + roles: ["_admin"] + } + }; + + TEquals(true, repDb.save(repDoc).ok); + waitForRep(repDb, repDoc, "complete"); + repDoc1 = repDb.open(repDoc._id); + T(repDoc1 !== null); + TEquals(repDoc.source, repDoc1.source); + TEquals(repDoc.target, repDoc1.target); + TEquals("completed", repDoc1._replication_state); + TEquals("string", typeof repDoc1._replication_id); + TEquals("string", typeof repDoc1._replication_state_time); + + ddoc_copy = dbB.open(ddoc._id); + T(ddoc_copy !== null); + } + + function rep_doc_with_bad_rep_id() { populate_db(dbA, docs1); populate_db(dbB, []); @@ -738,7 +958,7 @@ couchTests.replicator_db = function(debug) { T(repDoc1.target === repDoc.target); T(repDoc1._replication_state === "completed", "replication document with bad replication id failed"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); T(repDoc1._replication_id !== "1234abc"); } @@ -929,7 +1149,7 @@ couchTests.replicator_db = function(debug) { var repDoc1 = repDb.open(repDoc._id); T(repDoc1 !== null); T(repDoc1._replication_state === "error"); - T(typeof repDoc1._replication_state_time === "number"); + T(typeof repDoc1._replication_state_time === "string"); T(typeof repDoc1._replication_id === "string"); } @@ -1110,6 +1330,11 @@ couchTests.replicator_db = function(debug) { value: usersDb.name } ]); + + repDb.deleteDb(); + restartServer(); + run_on_modified_server(server_config_2, test_user_ctx_validation); + repDb.deleteDb(); restartServer(); run_on_modified_server(server_config_2, test_replication_credentials_delegation); diff --git a/share/www/script/test/rewrite.js b/share/www/script/test/rewrite.js index 86905f8f..bb188773 100644 --- a/share/www/script/test/rewrite.js +++ b/share/www/script/test/rewrite.js @@ -119,6 +119,10 @@ couchTests.rewrite = function(debug) { "query": { "startkey": ":start", "endkey": ":end" + }, + "formats": { + "start": "int", + "end": "int" } }, { @@ -164,6 +168,18 @@ couchTests.rewrite = function(debug) { } }, { + "from": "simpleForm/complexView7/:a/:b", + "to": "_view/complexView3", + "query": { + "key": [":a", ":b"], + "include_docs": ":doc" + }, + "format": { + "doc": "bool" + } + + }, + { "from": "/", "to": "_view/basicView", } @@ -348,14 +364,14 @@ couchTests.rewrite = function(debug) { T(!(/Key: 1/.test(xhr.responseText))); T(/FirstKey: 3/.test(xhr.responseText)); T(/LastKey: 8/.test(xhr.responseText)); - + // get with query params xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/basicViewPath/3/8"); T(xhr.status == 200, "with query params"); T(!(/Key: 1/.test(xhr.responseText))); T(/FirstKey: 3/.test(xhr.responseText)); T(/LastKey: 8/.test(xhr.responseText)); - + // get with query params xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView"); T(xhr.status == 200, "with query params"); @@ -380,6 +396,11 @@ couchTests.rewrite = function(debug) { xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_rewrite/simpleForm/complexView6?a=test&b=essai"); T(xhr.status == 200, "with query params"); T(/Value: doc 4/.test(xhr.responseText)); + + 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({ |