From 59a60a319bead55aa188e8cb1516d5e5146c4492 Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Sun, 15 Feb 2009 23:59:38 +0000 Subject: Reorganize the tests into one file per test. No other changes. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@744782 13f79535-47bb-0310-9956-ffa450edef68 --- share/www/script/couch_tests.js | 3366 +-------------------------------------- 1 file changed, 46 insertions(+), 3320 deletions(-) (limited to 'share/www/script/couch_tests.js') diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index 7cf2c6a4..7b25cb4f 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -19,3329 +19,55 @@ if (typeof window == 'undefined' || !window) { CouchDB.inBrowser = true; } -var tests = { +var couchTests = {}; - // Do some basic tests. - basics: function(debug) { - var result = JSON.parse(CouchDB.request("GET", "/").responseText); - T(result.couchdb == "Welcome"); - - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - - // bug COUCHDB-100: DELETE on non-existent DB returns 500 instead of 404 - db.deleteDb(); - -db.createDb(); - - // PUT on existing DB should return 412 instead of 500 - xhr = CouchDB.request("PUT", "/test_suite_db/"); - T(xhr.status == 412); - if (debug) debugger; - - // Get the database info, check the db_name - T(db.info().db_name == "test_suite_db"); - - // Get the database info, check the doc_count - T(db.info().doc_count == 0); - - // create a document and save it to the database - var doc = {_id:"0",a:1,b:1}; - var result = db.save(doc); - - T(result.ok==true); // return object has an ok member with a value true - T(result.id); // the _id of the document is set. - T(result.rev); // the revision id of the document is set. - - // Verify the input doc is now set with the doc id and rev - // (for caller convenience). - T(doc._id == result.id && doc._rev == result.rev); - - var id = result.id; // save off the id for later - - // make sure the revs_info status is good - var doc = db.open(id, {revs_info:true}); - T(doc._revs_info[0].status == "available"); - - // Create some more documents. - // Notice the use of the ok member on the return result. - T(db.save({_id:"1",a:2,b:4}).ok); - T(db.save({_id:"2",a:3,b:9}).ok); - T(db.save({_id:"3",a:4,b:16}).ok); - - // Check the database doc count - T(db.info().doc_count == 4); - - // Test a simple map functions - - // create a map function that selects all documents whose "a" member - // has a value of 4, and then returns the document's b value. - var mapFunction = function(doc){ - if (doc.a==4) - emit(null, doc.b); - }; - - results = db.query(mapFunction); - - // verify only one document found and the result value (doc.b). - T(results.total_rows == 1 && results.rows[0].value == 16); - - // reopen document we saved earlier - existingDoc = db.open(id); - - T(existingDoc.a==1); - - //modify and save - existingDoc.a=4; - db.save(existingDoc); - - // redo the map query - results = db.query(mapFunction); - - // the modified document should now be in the results. - T(results.total_rows == 2); - - // write 2 more documents - T(db.save({a:3,b:9}).ok); - T(db.save({a:4,b:16}).ok); - - results = db.query(mapFunction); - - // 1 more document should now be in the result. - T(results.total_rows == 3); - T(db.info().doc_count == 6); - - var reduceFunction = function(keys, values){ - return sum(values); - }; - - results = db.query(mapFunction, reduceFunction); - - T(results.rows[0].value == 33); - - // delete a document - T(db.deleteDoc(existingDoc).ok); - - // make sure we can't open the doc - T(db.open(existingDoc._id) == null); - - results = db.query(mapFunction); - - // 1 less document should now be in the results. - T(results.total_rows == 2); - T(db.info().doc_count == 5); - - // make sure we can still open the old rev of the deleted doc - T(db.open(existingDoc._id, {rev: existingDoc._rev}) != null); - - // make sure restart works - T(db.ensureFullCommit().ok); - restartServer(); - - // make sure we can still open - T(db.open(existingDoc._id, {rev: existingDoc._rev}) != null); - - // test that the POST response has a Location header - var xhr = CouchDB.request("POST", "/test_suite_db", { - body: JSON.stringify({"foo":"bar"}) - }); - var resp = JSON.parse(xhr.responseText); - T(resp.ok); - 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"); - }, - - delayed_commits: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // By default, couchdb doesn't fully commit documents to disk right away, - // it waits about a second to batch the full commit flush along with any - // other updates. If it crashes or is restarted you may lose the most - // recent commits. - - T(db.save({_id:"1",a:2,b:4}).ok); - T(db.open("1") != null); - - restartServer(); - - T(db.open("1") == null); // lost the update. - // note if we waited > 1 sec before the restart, the doc would likely - // commit. - - - // Retry the same thing but with full commits on. - - var db2 = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"true"}); - - T(db2.save({_id:"1",a:2,b:4}).ok); - T(db2.open("1") != null); - - restartServer(); - - T(db2.open("1") != null); - - // You can update but without committing immediately, and then ensure - // everything is commited in the last step. - - T(db.save({_id:"2",a:2,b:4}).ok); - T(db.open("2") != null); - T(db.ensureFullCommit().ok); - restartServer(); - - T(db.open("2") != null); - - // However, it's possible even when flushed, that the server crashed between - // the update and the commit, and you don't want to check to make sure - // every doc you updated actually made it to disk. So record the instance - // start time of the database before the updates and then check it again - // after the flush (the instance start time is returned by the flush - // operation). if they are the same, we know everything was updated - // safely. - - // First try it with a crash. - - var instanceStartTime = db.info().instance_start_time; - - T(db.save({_id:"3",a:2,b:4}).ok); - T(db.open("3") != null); - - restartServer(); - - var commitResult = db.ensureFullCommit(); - T(commitResult.ok && commitResult.instance_start_time != instanceStartTime); - // start times don't match, meaning the server lost our change - - T(db.open("3") == null); // yup lost it - - // retry with no server restart - - var instanceStartTime = db.info().instance_start_time; - - T(db.save({_id:"4",a:2,b:4}).ok); - T(db.open("4") != null); - - var commitResult = db.ensureFullCommit(); - T(commitResult.ok && commitResult.instance_start_time == instanceStartTime); - // Successful commit, start times match! - - restartServer(); - - T(db.open("4") != null); - - }, - - all_docs: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // Create some more documents. - // Notice the use of the ok member on the return result. - T(db.save({_id:"0",a:1,b:1}).ok); - T(db.save({_id:"3",a:4,b:16}).ok); - T(db.save({_id:"1",a:2,b:4}).ok); - T(db.save({_id:"2",a:3,b:9}).ok); - - // Check the all docs - var results = db.allDocs(); - var rows = results.rows; - - T(results.total_rows == results.rows.length); - - for(var i=0; i < rows.length; i++) { - T(rows[i].id >= "0" && rows[i].id <= "4"); - } - - // Check _all_docs with descending=true - var desc = db.allDocs({descending:true}); - T(desc.total_rows == desc.rows.length); - - // Check _all_docs offset - var all = db.allDocs({startkey:"2"}); - T(all.offset == 2); - - // check that the docs show up in the seq view in the order they were created - var all_seq = db.allDocsBySeq(); - var ids = ["0","3","1","2"]; - for (var i=0; i < all_seq.rows.length; i++) { - var row = all_seq.rows[i]; - T(row.id == ids[i]); - }; - - // it should work in reverse as well - all_seq = db.allDocsBySeq({descending:true}); - ids = ["2","1","3","0"]; - for (var i=0; i < all_seq.rows.length; i++) { - var row = all_seq.rows[i]; - T(row.id == ids[i]); - }; - - // check that deletions also show up right - var doc1 = db.open("1"); - var deleted = db.deleteDoc(doc1); - T(deleted.ok); - all_seq = db.allDocsBySeq(); - - // the deletion should make doc id 1 have the last seq num - T(all_seq.rows.length == 4); - T(all_seq.rows[3].id == "1"); - T(all_seq.rows[3].value.deleted); - - // is this a bug? - // T(all_seq.rows.length == all_seq.total_rows); - - // do an update - var doc2 = db.open("3"); - doc2.updated = "totally"; - db.save(doc2); - all_seq = db.allDocsBySeq(); - - // the update should make doc id 3 have the last seq num - T(all_seq.rows.length == 4); - T(all_seq.rows[3].id == "3"); - - // ok now lets see what happens with include docs - all_seq = db.allDocsBySeq({include_docs: true}); - T(all_seq.rows.length == 4); - T(all_seq.rows[3].id == "3"); - T(all_seq.rows[3].doc.updated == "totally"); - - // and on the deleted one, no doc - T(all_seq.rows[2].value.deleted); - T(!all_seq.rows[2].doc); - - // 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); - }, - - // Do some edit conflict detection tests - conflicts: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // create a doc and save - var doc = {_id:"foo",a:1,b:1}; - T(db.save(doc).ok); - - // reopen - var doc2 = db.open(doc._id); - - // ensure the revisions are the same - T(doc._id == doc2._id && doc._rev == doc2._rev); - - // edit the documents. - doc.a = 2; - doc2.a = 3; - - // save one document - T(db.save(doc).ok); - - // save the other document - try { - db.save(doc2); // this should generate a conflict exception - T("no save conflict 1" && false); // we shouldn't hit here - } catch (e) { - T(e.error == "conflict"); - } - - // Now clear out the _rev member and save. This indicates this document is - // new, not based on an existing revision. - doc2._rev = undefined; - try { - db.save(doc2); // this should generate a conflict exception - T("no save conflict 2" && false); // we shouldn't hit here - } catch (e) { - T(e.error == "conflict"); - } - - // Now delete the document from the database - T(db.deleteDoc(doc).ok); - - T(db.save(doc2).ok); // we can save a new document over a deletion without - // knowing the deletion rev. - }, - - recreate_doc: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // First create a new document with the ID "foo", and delete it again - var doc = {_id: "foo", a: "bar", b: 42}; - T(db.save(doc).ok); - T(db.deleteDoc(doc).ok); - - // Now create a new document with the same ID, save it, and then modify it - // This should work fine, but currently results in a conflict error, at - // least "sometimes" - for (var i = 0; i < 10; i++) { - doc = {_id: "foo"}; - T(db.save(doc).ok); - doc = db.open("foo"); - doc.a = "baz"; - try { - T(db.save(doc).ok); - } finally { - // And now, we can't even delete the document anymore :/ - T(db.deleteDoc(doc).rev != undefined); - } - } - }, - - copy_move_doc: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // copy a doc - T(db.save({_id:"doc_to_be_copied",v:1}).ok); - var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied", { - headers: {"Destination":"doc_that_was_copied"} - }); - - T(xhr.status == 201); - T(db.open("doc_that_was_copied").v == 1); - - // move a doc - - // test error condition - var xhr = CouchDB.request("MOVE", "/test_suite_db/doc_to_be_copied", { - headers: {"Destination":"doc_that_was_moved"} - }); - T(xhr.status == 400); // bad request, MOVE requires source rev. - - var rev = db.open("doc_to_be_copied")._rev; - var xhr = CouchDB.request("MOVE", "/test_suite_db/doc_to_be_copied?rev=" + rev, { - headers: {"Destination":"doc_that_was_moved"} - }); - - T(xhr.status == 201); - T(db.open("doc_that_was_moved").v == 1); - T(db.open("doc_to_be_copied") == null); - - // COPY with existing target - T(db.save({_id:"doc_to_be_copied",v:1}).ok); - var doc = db.save({_id:"doc_to_be_overwritten",v:2}); - T(doc.ok); - - // error condition - var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied", { - headers: {"Destination":"doc_to_be_overwritten"} - }); - T(xhr.status == 409); // conflict - - var rev = db.open("doc_to_be_overwritten")._rev; - var xhr = CouchDB.request("COPY", "/test_suite_db/doc_to_be_copied", { - headers: {"Destination":"doc_to_be_overwritten?rev=" + rev} - }); - T(xhr.status == 201); - - var over = db.open("doc_to_be_overwritten"); - T(rev != over._rev); - T(over.v == 1); - }, - - uuids: function(debug) { - var testHashBustingHeaders = function(xhr) { - T(xhr.getResponseHeader("Cache-Control").match(/no-cache/)); - T(xhr.getResponseHeader("Pragma") == "no-cache"); - - var currentTime = new Date(); - var expiresHeader = Date.parse(xhr.getResponseHeader("Expires")); - var dateHeader = Date.parse(xhr.getResponseHeader("Date")); - - T(expiresHeader < currentTime); - T(currentTime - dateHeader < 3000); - }; - - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // a single UUID without an explicit count - var xhr = CouchDB.request("GET", "/_uuids"); - T(xhr.status == 200); - var result = JSON.parse(xhr.responseText); - T(result.uuids.length == 1); - var first = result.uuids[0]; - testHashBustingHeaders(xhr); - - // a single UUID with an explicit count - xhr = CouchDB.request("GET", "/_uuids?count=1"); - T(xhr.status == 200); - result = JSON.parse(xhr.responseText); - T(result.uuids.length == 1); - var second = result.uuids[0]; - T(first != second); - - // no collisions with 1,000 UUIDs - xhr = CouchDB.request("GET", "/_uuids?count=1000"); - T(xhr.status == 200); - result = JSON.parse(xhr.responseText); - T( result.uuids.length == 1000 ); - var seen = {}; - for(var i in result.uuids) { - var id = result.uuids[i]; - T(seen[id] === undefined); - seen[id] = 1; - } - - // ensure we return a 405 on POST - xhr = CouchDB.request("POST", "/_uuids?count=1000"); - T(xhr.status == 405); - }, - - bulk_docs: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var docs = makeDocs(5); - - // Create the docs - var result = db.bulkSave(docs); - T(result.ok); - T(result.new_revs.length == 5); - for (var i = 0; i < 5; i++) { - T(result.new_revs[i].id == docs[i]._id); - T(result.new_revs[i].rev); - docs[i].string = docs[i].string + ".00"; - } - - // Update the docs - result = db.bulkSave(docs); - T(result.ok); - T(result.new_revs.length == 5); - for (i = 0; i < 5; i++) { - T(result.new_revs[i].id == i.toString()); - docs[i]._deleted = true; - } - - // Delete the docs - result = db.bulkSave(docs); - T(result.ok); - T(result.new_revs.length == 5); - for (i = 0; i < 5; i++) { - T(db.open(docs[i]._id) == null); - } - - // verify creating a document with no id returns a new id - var req = CouchDB.request("POST", "/test_suite_db/_bulk_docs", { - body: JSON.stringify({"docs": [{"foo":"bar"}]}) - }); - result = JSON.parse(req.responseText); - - T(result.new_revs[0].id != ""); - T(result.new_revs[0].rev != ""); - }, - - // test saving a semi-large quanitity of documents and do some view queries. - lots_of_docs: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // keep number lowish for now to keep tests fasts. Crank up manually to - // to really test. - var numDocsToCreate = 500; - - for(var i=0; i < numDocsToCreate; i += 100) { - var createNow = Math.min(numDocsToCreate - i, 100); - var docs = makeDocs(i, i + createNow); - T(db.bulkSave(docs).ok); - } - - // query all documents, and return the doc.integer member as a key. - results = db.query(function(doc){ emit(doc.integer, null) }); - - T(results.total_rows == numDocsToCreate); - - // validate the keys are ordered ascending - for(var i=0; i 0) { - str = str + str; - } - return str; - } - - var designDoc = { - _id:"_design/test", // turn off couch.js id escaping? - language: "javascript", - 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) + "\"; };"} - } - } - T(db.save(designDoc).ok); - - T(db.bulkSave(makeDocs(1, numDocs + 1)).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 (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); - } - 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); - - var summate = function(N) {return (N+1)*N/2;}; - var result = db.view("test/summate"); - T(result.rows[0].value == summate(numDocs)); - - result = db.view("test/summate", {startkey:4,endkey:4}); - T(result.rows[0].value == 4); - - result = db.view("test/summate", {startkey:4,endkey:5}); - T(result.rows[0].value == 9); - - result = db.view("test/summate", {startkey:4,endkey:6}); - T(result.rows[0].value == 15); - - // Verify that a shared index (view def is an exact copy of "summate") - // does not confuse the reduce stage - result = db.view("test/summate2", {startkey:4,endkey:6}); - T(result.rows[0].value == 15); - - for(var i=1; i= 0; i -= 10) { - var queryResults = db.query(queryFun, null, { - startkey: i, - startkey_docid: i, - descending: true, - limit: 10 - }); - 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); - } - } - - // ignore decending=false. CouchDB should just ignore that. - for (i = 0; i < docs.length; i += 10) { - var queryResults = db.query(queryFun, null, { - startkey: i, - startkey_docid: i, - descending: false, - limit: 10 - }); - 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 endkey_docid - var queryResults = db.query(function(doc) { emit(null, null);}, null, { - startkey: null, - startkey_docid: 1, - endkey: null, - endkey_docid: 40 - }); - - 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"); - - }, - - view_sandboxing: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var doc = {integer: 1, string: "1", array: [1, 2, 3]}; - T(db.save(doc).ok); -/* - // make sure that attempting to change the document throws an error - var results = db.query(function(doc) { - doc.integer = 2; - emit(null, doc); - }); - T(results.total_rows == 0); - - var results = db.query(function(doc) { - doc.array[0] = 0; - emit(null, doc); - }); - T(results.total_rows == 0); -*/ - // make sure that a view cannot invoke interpreter internals such as the - // garbage collector - var results = db.query(function(doc) { - gc(); - emit(null, doc); - }); - T(results.total_rows == 0); - - // make sure that a view cannot access the map_funs array defined used by - // the view server - var results = db.query(function(doc) { map_funs.push(1); emit(null, doc) }); - T(results.total_rows == 0); - - // make sure that a view cannot access the map_results array defined used by - // the view server - var results = db.query(function(doc) { map_results.push(1); emit(null, doc) }); - T(results.total_rows == 0); - }, - - view_xml: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - db.save({content: "Testing XML"}); - db.save({content: "Testing E4X"}); - - var results = db.query( - "function(doc) {\n" + - " var xml = new XML(doc.content);\n" + - " emit(xml.title.text(), null);\n" + - "}"); - T(results.total_rows == 2); - T(results.rows[0].key == "Testing E4X"); - T(results.rows[1].key == "Testing XML"); - - var results = db.query( - "function(doc) {\n" + - " var xml = new XML(doc.content);\n" + - " emit(xml.title.@id, null);\n" + - "}"); - T(results.total_rows == 2); - T(results.rows[0].key == "e4x"); - T(results.rows[1].key == "xml"); - }, - - replication: function(debug) { - if (debug) debugger; - 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:"test_suite_db_b"}, - {source:"http://" + host + "/test_suite_db_a", - target:"http://" + host + "/test_suite_db_b"} - ] - var dbA = new CouchDB("test_suite_db_a"); - var dbB = new CouchDB("test_suite_db_b"); - var numDocs = 10; - var xhr; - for (var testPair = 0; testPair < dbPairs.length; testPair++) { - var A = dbPairs[testPair].source - var B = dbPairs[testPair].target - - dbA.deleteDb(); - dbA.createDb(); - dbB.deleteDb(); - dbB.createDb(); - - var repTests = { - // copy and paste and put your code in. delete unused steps. - test_template: new function () { - this.init = function(dbA, dbB) { - // before anything has happened - } - this.afterAB1 = function(dbA, dbB) { - // called after replicating src=A tgt=B first time. - }; - this.afterBA1 = function(dbA, dbB) { - // called after replicating src=B tgt=A first time. - }; - this.afterAB2 = function(dbA, dbB) { - // called after replicating src=A tgt=B second time. - }; - this.afterBA2 = function(dbA, dbB) { - // etc... - }; - }, - - simple_test: new function () { - this.init = function(dbA, dbB) { - var docs = makeDocs(0, numDocs); - T(dbA.bulkSave(docs).ok); - }; - - this.afterAB1 = function(dbA, dbB) { - for (var j = 0; j < numDocs; j++) { - var docA = dbA.open("" + j); - var docB = dbB.open("" + j); - T(docA._rev == docB._rev); - } - }; - }, - - deletes_test: new function () { - this.init = function(dbA, dbB) { - T(dbA.save({_id:"foo1",value:"a"}).ok); - }; - - this.afterAB1 = function(dbA, dbB) { - var docA = dbA.open("foo1"); - var docB = dbB.open("foo1"); - T(docA._rev == docB._rev); - - dbA.deleteDoc(docA); - }; - - this.afterAB2 = function(dbA, dbB) { - T(dbA.open("foo1") == null); - T(dbB.open("foo1") == null); - }; - }, - - slashes_in_ids_test: new function () { - // make sure docs with slashes in id replicate properly - this.init = function(dbA, dbB) { - dbA.save({ _id:"abc/def", val:"one" }); - }; - - this.afterAB1 = function(dbA, dbB) { - var docA = dbA.open("abc/def"); - var docB = dbB.open("abc/def"); - T(docA._rev == docB._rev); - }; - }, - - design_docs_test: new function() { - // make sure design docs replicate properly - this.init = function(dbA, dbB) { - dbA.save({ _id:"_design/test" }); - }; - - this.afterAB1 = function() { - var docA = dbA.open("_design/test"); - var docB = dbB.open("_design/test"); - T(docA._rev == docB._rev); - }; - }, - - attachments_test: new function () { - // Test attachments - this.init = function(dbA, dbB) { - dbA.save({ - _id:"bin_doc", - _attachments:{ - "foo.txt": { - "type":"base64", - "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" - } - } - }); - }; - - this.afterAB1 = function(dbA, dbB) { - var xhr = CouchDB.request("GET", "/test_suite_db_a/bin_doc/foo.txt"); - T(xhr.responseText == "This is a base64 encoded text") - - xhr = CouchDB.request("GET", "/test_suite_db_b/bin_doc/foo.txt"); - T(xhr.responseText == "This is a base64 encoded text") - }; - }, - - conflicts_test: new function () { - // test conflicts - this.init = function(dbA, dbB) { - dbA.save({_id:"foo",value:"a"}); - dbB.save({_id:"foo",value:"b"}); - }; - - this.afterBA1 = function(dbA, dbB) { - var docA = dbA.open("foo", {conflicts: true}); - var docB = dbB.open("foo", {conflicts: true}); - - // make sure the same rev is in each db - T(docA._rev === docB._rev); - - // make sure the conflicts are the same in each db - T(docA._conflicts[0] === docB._conflicts[0]); - - // delete a conflict. - dbA.deleteDoc({_id:"foo", _rev:docA._conflicts[0]}); - }; - - this.afterBA2 = function(dbA, dbB) { - // open documents and include the conflict meta data - var docA = dbA.open("foo", {conflicts: true}); - var docB = dbB.open("foo", {conflicts: true}); - - // We should have no conflicts this time - T(docA._conflicts === undefined) - T(docB._conflicts === undefined); - }; - } - }; - - var test; - for(test in repTests) { - if(repTests[test].init) { - repTests[test].init(dbA, dbB); - } - } - - T(CouchDB.replicate(A, B).ok); - - for(test in repTests) { - if(repTests[test].afterAB1) repTests[test].afterAB1(dbA, dbB); - } - - T(CouchDB.replicate(B, A).ok); - - for(test in repTests) { - if(repTests[test].afterBA1) repTests[test].afterBA1(dbA, dbB); - } - - T(CouchDB.replicate(A, B).ok); - - for(test in repTests) { - if(repTests[test].afterAB2) repTests[test].afterAB2(dbA, dbB); - } - - T(CouchDB.replicate(B, A).ok); - - for(test in repTests) { - if(repTests[test].afterBA2) repTests[test].afterBA2(dbA, dbB); - } - - } - }, - - etags_head: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var xhr; - - // create a new doc - xhr = CouchDB.request("PUT", "/test_suite_db/1", { - body: "{}" - }); - T(xhr.status == 201); - - // extract the ETag header values - var etag = xhr.getResponseHeader("etag"); - - // get the doc and verify the headers match - xhr = CouchDB.request("GET", "/test_suite_db/1"); - T(etag == xhr.getResponseHeader("etag")); - - // 'head' the doc and verify the headers match - xhr = CouchDB.request("HEAD", "/test_suite_db/1", { - headers: {"if-none-match": "s"} - }); - T(etag == xhr.getResponseHeader("etag")); - - // replace a doc - xhr = CouchDB.request("PUT", "/test_suite_db/1", { - body: "{}", - headers: {"if-match": etag} - }); - T(xhr.status == 201); - - // extract the new ETag value - var etagOld= etag; - etag = xhr.getResponseHeader("etag"); - - // fail to replace a doc - xhr = CouchDB.request("PUT", "/test_suite_db/1", { - body: "{}" - }); - T(xhr.status == 409); - - // verify get w/Etag - xhr = CouchDB.request("GET", "/test_suite_db/1", { - headers: {"if-none-match": etagOld} - }); - T(xhr.status == 200); - xhr = CouchDB.request("GET", "/test_suite_db/1", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - - // fail to delete a doc - xhr = CouchDB.request("DELETE", "/test_suite_db/1", { - headers: {"if-match": etagOld} - }); - T(xhr.status == 409); - - //now do it for real - xhr = CouchDB.request("DELETE", "/test_suite_db/1", { - headers: {"if-match": etag} - }); - T(xhr.status == 200); - }, - etags_views: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var designDoc = { - _id:"_design/etags", - language: "javascript", - views : { - basicView : { - map : stringFun(function(doc) { - emit(doc.integer, doc.string); - }) - }, - withReduce : { - map : stringFun(function(doc) { - emit(doc.integer, doc.string); - }), - reduce : stringFun(function(keys, values, rereduce) { - if (rereduce) { - return sum(values); - } else { - return values.length; - } - }) - } - } - } - T(db.save(designDoc).ok); - var xhr; - var docs = makeDocs(0, 10); - var saveResult = db.bulkSave(docs); - T(saveResult.ok); - - // verify get w/Etag on map view - xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView"); - T(xhr.status == 200); - var etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - // TODO GET with keys (when that is available) - - // reduce view - xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce"); - T(xhr.status == 200); - var etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - - // all docs - xhr = CouchDB.request("GET", "/test_suite_db/_all_docs"); - T(xhr.status == 200); - var etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_all_docs", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - - // by seq - xhr = CouchDB.request("GET", "/test_suite_db/_all_docs_by_seq"); - T(xhr.status == 200); - var etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_all_docs_by_seq", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - - // list etag - // in the list test for now - }, - - show_documents: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var designDoc = { - _id:"_design/template", - language: "javascript", - shows: { - "hello" : stringFun(function(doc) { - if (doc) { - return "Hello World"; - } else { - return "Empty World"; - } - }), - "just-name" : stringFun(function(doc, req) { - return { - body : "Just " + doc.name - }; - }), - "req-info" : stringFun(function(doc, req) { - return { - json : req - } - }), - "xml-type" : stringFun(function(doc, req) { - return { - "headers" : { - "Content-Type" : "application/xml" - }, - "body" : new XML('') - } - }), - "no-set-etag" : stringFun(function(doc, req) { - return { - headers : { - "Etag" : "skipped" - }, - "body" : "something" - } - }), - "accept-switch" : stringFun(function(doc, req) { - if (req.headers["Accept"].match(/image/)) { - return { - // a 16x16 px version of the CouchDB logo - "base64" : -["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV", -"BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/", -"AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7", -"/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6", -"wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA", -"AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5", -"zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx", -"vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT", -"LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''), - headers : { - "Content-Type" : "image/png", - "Vary" : "Accept" // we set this for proxy caches - } - }; - } else { - return { - "body" : "accepting text requests", - headers : { - "Content-Type" : "text/html", - "Vary" : "Accept" - } - }; - } - }), - "respondWith" : stringFun(function(doc, req) { - registerType("foo", "application/foo","application/x-foo"); - return respondWith(req, { - html : function() { - return { - body:"Ha ha, you said \"" + doc.word + "\"." - }; - }, - xml : function() { - var xml = new XML(''); - // Becase Safari can't stand to see that dastardly - // E4X outside of a string. Outside of tests you - // can just use E4X literals. - this.eval('xml.node.@foo = doc.word'); - return { - body: xml - }; - }, - foo : function() { - return { - body: "foofoo" - }; - }, - fallback : "html" - }); - }) - } - }; - T(db.save(designDoc).ok); - - var doc = {"word":"plankton", "name":"Rusty"} - var resp = db.save(doc); - T(resp.ok); - var docid = resp.id; - - // show error - var xhr = CouchDB.request("GET", "/test_suite_db/_show/"); - T(xhr.status == 404); - T(JSON.parse(xhr.responseText).reason == "Invalid path."); - - // hello template world - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/"+docid); - T(xhr.responseText == "Hello World"); - - // hello template world (no docid) - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello"); - T(xhr.responseText == "Empty World"); - - - // show with doc - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid); - T(xhr.responseText == "Just Rusty"); - - // show with missing doc - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/missingdoc"); - T(xhr.status == 404); - var resp = JSON.parse(xhr.responseText); - T(resp.error == "not_found"); - T(resp.reason == "missing"); - - // show with missing func - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid); - T(xhr.status == 404); - - // missing design doc - xhr = CouchDB.request("GET", "/test_suite_db/_show/missingdoc/just-name/"+docid); - T(xhr.status == 404); - var resp = JSON.parse(xhr.responseText); - T(resp.error == "not_found"); - - // query parameters - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/req-info/"+docid+"?foo=bar", { - headers: { - "Accept": "text/html;text/plain;*/*", - "X-Foo" : "bar" - } - }); - var resp = JSON.parse(xhr.responseText); - T(equals(resp.headers["X-Foo"], "bar")); - T(equals(resp.query, {foo:"bar"})); - T(equals(resp.verb, "GET")); - T(equals(resp.path[4], docid)); - T(equals(resp.info.db_name, "test_suite_db")); - - // returning a content-type - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/xml-type/"+docid); - T("application/xml" == xhr.getResponseHeader("Content-Type")); - T("Accept" == xhr.getResponseHeader("Vary")); - - // accept header switching - // different mime has different etag - - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, { - headers: {"Accept": "text/html;text/plain;*/*"} - }); - T("text/html" == xhr.getResponseHeader("Content-Type")); - T("Accept" == xhr.getResponseHeader("Vary")); - var etag = xhr.getResponseHeader("etag"); - - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, { - headers: {"Accept": "image/png;*/*"} - }); - T(xhr.responseText.match(/PNG/)) - T("image/png" == xhr.getResponseHeader("Content-Type")); - var etag2 = xhr.getResponseHeader("etag"); - T(etag2 != etag); - - // proper etags - // show with doc - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid); - // extract the ETag header values - etag = xhr.getResponseHeader("etag"); - // get again with etag in request - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { - headers: {"if-none-match": etag} - }); - // should be 304 - T(xhr.status == 304); - - // update the doc - doc.name = "Crusty"; - resp = db.save(doc); - T(resp.ok); - // req with same etag - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { - headers: {"if-none-match": etag} - }); - // status is 200 - T(xhr.status == 200); - - // get new etag and request again - etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { - headers: {"if-none-match": etag} - }); - // should be 304 - T(xhr.status == 304); - - // update design doc (but not function) - designDoc.isChanged = true; - T(db.save(designDoc).ok); - - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { - headers: {"if-none-match": etag} - }); - // should be 304 - T(xhr.status == 304); - - // update design doc function - designDoc.shows["just-name"] = (function(doc, req) { - return { - body : "Just old " + doc.name - }; - }).toString(); - T(db.save(designDoc).ok); - - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { - headers: {"if-none-match": etag} - }); - // status is 200 - T(xhr.status == 200); - - - // JS can't set etag - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/no-set-etag/"+docid); - // extract the ETag header values - etag = xhr.getResponseHeader("etag"); - T(etag != "skipped") - - // test the respondWith mime matcher - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { - headers: { - "Accept": 'text/html,application/atom+xml; q=0.9' - } - }); - T(xhr.getResponseHeader("Content-Type") == "text/html"); - T(xhr.responseText == "Ha ha, you said \"plankton\"."); - - // now with xml - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { - headers: { - "Accept": 'application/xml' - } - }); - T(xhr.getResponseHeader("Content-Type") == "application/xml"); - T(xhr.responseText.match(/node/)); - T(xhr.responseText.match(/plankton/)); - - // registering types works - xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { - headers: { - "Accept": "application/x-foo" - } - }); - T(xhr.getResponseHeader("Content-Type") == "application/x-foo"); - T(xhr.responseText.match(/foofoo/)); - }, - - list_views : function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - var designDoc = { - _id:"_design/lists", - language: "javascript", - views : { - basicView : { - map : stringFun(function(doc) { - emit(doc.integer, doc.string); - }) - }, - withReduce : { - map : stringFun(function(doc) { - emit(doc.integer, doc.string); - }), - reduce : stringFun(function(keys, values, rereduce) { - if (rereduce) { - return sum(values); - } else { - return values.length; - } - }) - } - }, - lists: { - simpleForm: stringFun(function(head, row, req, row_info) { - if (row) { - // we ignore headers on rows and tail - return { - body : '\n
  • Key: '+row.key - +' Value: '+row.value - +' LineNo: '+row_info.row_number+'
  • ' - }; - } else if (head) { - // we return an object (like those used by external and show) - // so that we can specify headers - return { - body : '

    Total Rows: ' - + head.total_rows - + ' Offset: ' + head.offset - + '

      ' - }; - } else { - // tail - return {body : '
    '+ - '

    FirstKey: '+row_info.first_key+ - ' LastKey: '+row_info.prev_key+'

    '}; - } - }), - acceptSwitch: stringFun(function(head, row, req, row_info) { - return respondWith(req, { - html : function() { - // If you're outputting text and you're not setting - // any headers, you can just return a string. - if (head) { - return "HTML
      "; - } else if (row) { - return '\n
    • Key: ' - +row.key+' Value: '+row.value - +' LineNo: '+row_info.row_number+'
    • '; - } else { // tail - return '
    '; - - } - }, - xml : function() { - if (head) { - return '' - +'Test XML Feed'; - } else if (row) { - // Becase Safari can't stand to see that dastardly - // E4X outside of a string. Outside of tests you - // can just use E4X literals. - var entry = new XML(''); - entry.id = row.id; - entry.title = row.key; - entry.content = row.value; - // We'll also let you return just an E4X object - // if you aren't setting headers. - return entry; - } else { - return ""; - } - } - }) - }), - qsParams: stringFun(function(head, row, req, row_info) { - if(head) return {body: req.query.foo}; - else return {body: "\n"}; - }), - stopIter: stringFun(function(head, row, req, row_info) { - if(head) { - return {body: "head"}; - } else if(row) { - if(row_info.row_number > 2) return {stop: true}; - return {body: " " + row_info.row_number}; - } else { - return {body: " tail"}; - } - }), - stopIter2: stringFun(function(head, row, req, row_info) { - return respondWith(req, { - html: function() { - if(head) { - return "head"; - } else if(row) { - if(row_info.row_number > 2) return {stop: true}; - return " " + row_info.row_number; - } else { - return " tail"; - } - } - }); - }) - } - }; - - T(db.save(designDoc).ok); - - var docs = makeDocs(0, 10); - var saveResult = db.bulkSave(docs); - T(saveResult.ok); - - var view = db.view('lists/basicView'); - T(view.total_rows == 10); - - // standard get - var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView"); - T(xhr.status == 200); - T(/Total Rows/.test(xhr.responseText)); - T(/Key: 1/.test(xhr.responseText)); - T(/LineNo: 0/.test(xhr.responseText)); - T(/LineNo: 5/.test(xhr.responseText)); - T(/FirstKey: 0/.test(xhr.responseText)); - T(/LastKey: 9/.test(xhr.responseText)); - - - var lines = xhr.responseText.split('\n'); - T(/LineNo: 5/.test(lines[6])); - - // test that etags are available - var etag = xhr.getResponseHeader("etag"); - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView", { - headers: {"if-none-match": etag} - }); - T(xhr.status == 304); - - // get with query params - var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=3"); - T(xhr.status == 200); - T(/Total Rows/.test(xhr.responseText)); - T(!(/Key: 1/.test(xhr.responseText))); - T(/FirstKey: 3/.test(xhr.responseText)); - T(/LastKey: 9/.test(xhr.responseText)); - - - // with 0 rows - var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=30"); - T(xhr.status == 200); - T(/Total Rows/.test(xhr.responseText)); - T(/Offset: null/.test(xhr.responseText)); - - // when there is a reduce present, but not used - var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?reduce=false"); - T(xhr.status == 200); - T(/Total Rows/.test(xhr.responseText)); - T(/Key: 1/.test(xhr.responseText)); - - // with accept headers for HTML - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", { - headers: { - "Accept": 'text/html' - } - }); - T(xhr.getResponseHeader("Content-Type") == "text/html"); - T(xhr.responseText.match(/HTML/)); - T(xhr.responseText.match(/Value/)); - - // now with xml - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", { - headers: { - "Accept": 'application/xml' - } - }); - T(xhr.getResponseHeader("Content-Type") == "application/xml"); - T(xhr.responseText.match(/XML/)); - T(xhr.responseText.match(/entry/)); - - // now with extra qs params - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/qsParams/basicView?foo=blam"); - T(xhr.responseText.match(/blam/)); - - - // aborting iteration - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter/basicView"); - T(xhr.responseText.match(/^head 0 1 2 tail$/)); - xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter2/basicView"); - T(xhr.responseText.match(/^head 0 1 2 tail$/)); - - - }, - - compact: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - var docs = makeDocs(0, 10); - var saveResult = db.bulkSave(docs); - T(saveResult.ok); - - var binAttDoc = { - _id: "bin_doc", - _attachments:{ - "foo.txt": { - content_type:"text/plain", - data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" - } - } - } - - T(db.save(binAttDoc).ok); - - var originalsize = db.info().disk_size; - - for(var i in docs) { - db.deleteDoc(docs[i]); - } - db.setAdmins(["Foo bar"]); - var deletesize = db.info().disk_size; - T(deletesize > originalsize); - - var xhr = CouchDB.request("POST", "/test_suite_db/_compact"); - T(xhr.status == 202); - // compaction isn't instantaneous, loop until done - while (db.info().compact_running) {}; - - T(db.ensureFullCommit().ok); - restartServer(); - var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt"); - T(xhr.responseText == "This is a base64 encoded text") - T(xhr.getResponseHeader("Content-Type") == "text/plain") - T(db.info().doc_count == 1); - T(db.info().disk_size < deletesize); - - }, - - purge: function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - /* - purge is not to be confused with a document deletion. It removes the - document and all edit history from the local instance of the database. - */ - - var numDocs = 10; - - var designDoc = { - _id:"_design/test", - language: "javascript", - views: { - all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"}, - single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"} - } - } - - T(db.save(designDoc).ok); - - T(db.bulkSave(makeDocs(1, numDocs + 1)).ok); - - // go ahead and validate the views before purging - 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); - } - T(db.view("test/single_doc").total_rows == 1); - - var info = db.info(); - var doc1 = db.open("1"); - var doc2 = db.open("2"); - - // purge the documents - var xhr = CouchDB.request("POST", "/test_suite_db/_purge", { - body: JSON.stringify({"1":[doc1._rev], "2":[doc2._rev]}), - }); - T(xhr.status == 200); - - var newInfo = db.info(); - // purging increments the update sequence - T(info.update_seq+1 == newInfo.update_seq); - // and it increments the purge_seq - T(info.purge_seq+1 == newInfo.purge_seq); - - var result = JSON.parse(xhr.responseText); - T(result.purged["1"][0] == doc1._rev); - T(result.purged["2"][0] == doc2._rev); - - T(db.open("1") == null); - T(db.open("2") == null); - - var rows = db.view("test/all_docs_twice").rows; - for (var i = 2; i < numDocs; i++) { - T(rows[2*(i-2)].key == i+1); - T(rows[(2*(i-2))+1].key == i+1); - } - T(db.view("test/single_doc").total_rows == 0); - - // purge documents twice in a row without loading views - // (causes full view rebuilds) - - var doc3 = db.open("3"); - var doc4 = db.open("4"); - - xhr = CouchDB.request("POST", "/test_suite_db/_purge", { - body: JSON.stringify({"3":[doc3._rev]}), - }); - - T(xhr.status == 200); - - xhr = CouchDB.request("POST", "/test_suite_db/_purge", { - body: JSON.stringify({"4":[doc4._rev]}), - }); - - T(xhr.status == 200); - - var rows = db.view("test/all_docs_twice").rows; - for (var i = 4; i < numDocs; i++) { - T(rows[2*(i-4)].key == i+1); - T(rows[(2*(i-4))+1].key == i+1); - } - T(db.view("test/single_doc").total_rows == 0); - }, - - config : function(debug) { - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - // test that /_config returns all the settings - var xhr = CouchDB.request("GET", "/_config"); - var config = JSON.parse(xhr.responseText); - - /* - if we run on standard ports, we can't extract - the number from the URL. Instead we try to guess - from the protocol what port we are running on. - If we can't guess, we don't test for the port. - Overengineering FTW. - */ - var server_port = CouchDB.host.split(':'); - if(server_port.length == 1 && CouchDB.inBrowser) { - var proto = window.location.protocol; - if(proto == "http:") { - port = 80; - } - if(proto == "https:") { - port = 443; - } - } else { - port = server_port.pop(); - } - - if(port) { - T(config.httpd.port == port); - } - - T(config.couchdb.database_dir); - T(config.daemons.httpd); - T(config.httpd_global_handlers._config); - T(config.log.level); - T(config.query_servers.javascript); - - // test that settings can be altered - xhr = CouchDB.request("PUT", "/_config/test/foo",{ - body : JSON.stringify("bar"), - headers: {"X-Couch-Persist": "false"} - }); - T(xhr.status == 200); - xhr = CouchDB.request("GET", "/_config/test"); - config = JSON.parse(xhr.responseText); - T(config.foo == "bar"); - - // you can get a single key - xhr = CouchDB.request("GET", "/_config/test/foo"); - T(xhr.responseText == '"bar"'); - }, - - security_validation : function(debug) { - // This tests couchdb's security and validation features. This does - // not test authentication, except to use test authentication code made - // specifically for this testing. It is a WWWW-Authenticate scheme named - // X-Couch-Test-Auth, and the user names and passwords are hard coded - // on the server-side. - // - // We could have used Basic authentication, however the XMLHttpRequest - // implementation for Firefox and Safari, and probably other browsers are - // broken (Firefox always prompts the user on 401 failures, Safari gives - // odd security errors when using different name/passwords, perhaps due - // to cross site scripting prevention). These problems essentially make Basic - // authentication testing in the browser impossible. But while hard to - // test automated in the browser, Basic auth may still useful for real - // world use where these bugs/behaviors don't matter. - // - // So for testing purposes we are using this custom X-Couch-Test-Auth. - // It's identical to Basic auth, except it doesn't even base64 encode - // the "username:password" string, it's sent completely plain text. - // Firefox and Safari both deal with this correctly (which is to say - // they correctly do nothing special). - - - var db = new CouchDB("test_suite_db"); - db.deleteDb(); - db.createDb(); - if (debug) debugger; - - run_on_modified_server( - [{section: "httpd", - key: "authentication_handler", - value: "{couch_httpd, special_test_authentication_handler}"}, - {section:"httpd", - key: "WWW-Authenticate", - value: "X-Couch-Test-Auth"}], - - function () { - - // try saving document usin the wrong credentials - var wrongPasswordDb = new CouchDB("test_suite_db", - {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"} - ); - - try { - wrongPasswordDb.save({foo:1,author:"Damien Katz"}); - T(false && "Can't get here. Should have thrown an error 1"); - } catch (e) { - T(e.error == "unauthorized"); - T(wrongPasswordDb.last_req.status == 401); - } - - - // Create the design doc that will run custom validation code - var designDoc = { - _id:"_design/test", - language: "javascript", - validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) { - // docs should have an author field. - if (!newDoc._deleted && !newDoc.author) { - throw {forbidden: - "Documents must have an author field"}; - } - if (oldDoc && oldDoc.author != userCtx.name) { - throw {unauthorized: - "You are not the author of this document. You jerk."}; - } - }).toString() + ")" - } - - // Save a document normally - var userDb = new CouchDB("test_suite_db", - {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"} - ); - - T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok); - - // Attempt to save the design as a non-admin - try { - userDb.save(designDoc); - T(false && "Can't get here. Should have thrown an error on design doc"); - } catch (e) { - T(e.error == "unauthorized"); - T(userDb.last_req.status == 401); - } - - // add user as admin - db.setAdmins(["Damien Katz"]); - - T(userDb.save(designDoc).ok); - - // update the document - var doc = userDb.open("testdoc"); - doc.foo=2; - T(userDb.save(doc).ok); - - // Save a document that's missing an author field. - try { - userDb.save({foo:1}); - T(false && "Can't get here. Should have thrown an error 2"); - } catch (e) { - T(e.error == "forbidden"); - T(userDb.last_req.status == 403); - } - - // Now attempt to update the document as a different user, Jan - var user2Db = new CouchDB("test_suite_db", - {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"} - ); - - var doc = user2Db.open("testdoc"); - doc.foo=3; - try { - user2Db.save(doc); - T(false && "Can't get here. Should have thrown an error 3"); - } catch (e) { - T(e.error == "unauthorized"); - T(user2Db.last_req.status == 401); - } - - // Now have Damien change the author to Jan - doc = userDb.open("testdoc"); - doc.author="Jan Lehnardt"; - T(userDb.save(doc).ok); - - // Now update the document as Jan - doc = user2Db.open("testdoc"); - doc.foo = 3; - T(user2Db.save(doc).ok); - - // Damien can't delete it - try { - userDb.deleteDoc(doc); - T(false && "Can't get here. Should have thrown an error 4"); - } catch (e) { - T(e.error == "unauthorized"); - T(userDb.last_req.status == 401); - } - - // Now delete document - T(user2Db.deleteDoc(doc).ok); - }); - }, - - - max_dbs_open : function(debug) { - if (debug) debugger; - restartServer(); - var max = 5; - run_on_modified_server( - [{section: "couchdb", - key: "max_dbs_open", - value: max.toString()}], - - function () { - for(var i=0; i