diff options
| author | John Christopher Anderson <jchris@apache.org> | 2009-02-15 23:59:38 +0000 | 
|---|---|---|
| committer | John Christopher Anderson <jchris@apache.org> | 2009-02-15 23:59:38 +0000 | 
| commit | 59a60a319bead55aa188e8cb1516d5e5146c4492 (patch) | |
| tree | 223063b38170d593def1e31ed12b6505dcd70c79 | |
| parent | 146bc594aef47b675670e7a7fd7f89b7c6a10843 (diff) | |
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
47 files changed, 3839 insertions, 3330 deletions
| diff --git a/share/www/couch_tests.html b/share/www/couch_tests.html index 1bfc7813..5d1b4178 100644 --- a/share/www/couch_tests.html +++ b/share/www/couch_tests.html @@ -34,7 +34,7 @@ specific language governing permissions and limitations under the License.          });        });        var testsPath = document.location.toString().split('?')[1]; -      loadTests(testsPath||"script/couch_tests.js") +      loadScript(testsPath||"script/couch_tests.js")      </script>    </head>    <body><div id="wrap"> diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js index 31bd4a4a..ae357aeb 100644 --- a/share/www/script/couch_test_runner.js +++ b/share/www/script/couch_test_runner.js @@ -12,8 +12,8 @@  // *********************** Test Framework of Sorts ************************* // -function loadTests(url) {   -  document.write('<script src="'+url+'"></script>'); +function loadScript(url) {   +  if (typeof document != "undefined") document.write('<script src="'+url+'"></script>');  };  function patchTest(fun) { @@ -67,7 +67,7 @@ function runTest(button, callback, debug) {    var row = currentRow = $(button).parents("tr").get(0);    $("td.status", row).removeClass("error").removeClass("failure").removeClass("success");    $("td", row).text(""); -  var testFun = tests[row.id]; +  var testFun = couchTests[row.id];    function run() {      numFailures = 0;      var start = new Date().getTime(); @@ -110,12 +110,12 @@ function showSource(cell) {    var name = $(cell).text();    var win = window.open("", name, "width=700,height=500,resizable=yes,scrollbars=yes");    win.document.title = name; -  $("<pre></pre>").text(tests[name].toString()).appendTo(win.document.body).fadeIn(); +  $("<pre></pre>").text(couchTests[name].toString()).appendTo(win.document.body).fadeIn();  }  function updateTestsListing() { -  for (var name in tests) { -    var testFunction = tests[name]; +  for (var name in couchTests) { +    var testFunction = couchTests[name];      var row = $("<tr><th></th><td></td><td></td><td></td></tr>")        .find("th").text(name).attr("title", "Show source").click(function() {          showSource(this); 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<numDocsToCreate; i++) { -      T(results.rows[i].key==i); -    } - -    // do the query again, but with descending output -    results = db.query(function(doc){ emit(doc.integer, null) }, null, { -      descending: true -    }); - -    T(results.total_rows == numDocsToCreate); - -    // validate the keys are ordered descending -    for(var i=0; i<numDocsToCreate; i++) { -      T(results.rows[numDocsToCreate-1-i].key==i); -    } - -    // Check _all_docs with descending=true again (now that there are many docs) -    var desc = db.allDocs({descending:true}); -    T(desc.total_rows == desc.rows.length); -  }, - -  reduce: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; -    var numDocs = 500 -    var docs = makeDocs(1,numDocs + 1); -    T(db.bulkSave(docs).ok); -    var summate = function(N) {return (N+1)*N/2;}; - -    var map = function (doc) { -        emit(doc.integer, doc.integer); -        emit(doc.integer, doc.integer)}; -    var reduce = function (keys, values) { return sum(values); }; -    var result = db.query(map, reduce); -    T(result.rows[0].value == 2*summate(numDocs)); - -    result = db.query(map, reduce, {startkey: 4, endkey: 4}); -    T(result.rows[0].value == 8); - -    result = db.query(map, reduce, {startkey: 4, endkey: 5}); -    T(result.rows[0].value == 18); - -    result = db.query(map, reduce, {startkey: 4, endkey: 6}); -    T(result.rows[0].value == 30); - -    result = db.query(map, reduce, {group:true, limit:3}); -    T(result.rows[0].value == 2); -    T(result.rows[1].value == 4); -    T(result.rows[2].value == 6); - -    for(var i=1; i<numDocs/2; i+=30) { -      result = db.query(map, reduce, {startkey: i, endkey: numDocs - i}); -      T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1))); -    } - -    db.deleteDb(); -    db.createDb(); - -    for(var i=1; i <= 5; i++) { - -      for(var j=0; j < 10; j++) { -        // these docs are in the order of the keys collation, for clarity -        var docs = []; -        docs.push({keys:["a"]}); -        docs.push({keys:["a"]}); -        docs.push({keys:["a", "b"]}); -        docs.push({keys:["a", "b"]}); -        docs.push({keys:["a", "b", "c"]}); -        docs.push({keys:["a", "b", "d"]}); -        docs.push({keys:["a", "c", "d"]}); -        docs.push({keys:["d"]}); -        docs.push({keys:["d", "a"]}); -        docs.push({keys:["d", "b"]}); -        docs.push({keys:["d", "c"]}); -        T(db.bulkSave(docs).ok); -        T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11)); -      } - -      map = function (doc) {emit(doc.keys, 1)}; -      reduce = function (keys, values) { return sum(values); }; - -      var results = db.query(map, reduce, {group:true}); - -      //group by exact key match -      T(equals(results.rows[0], {key:["a"],value:20*i})); -      T(equals(results.rows[1], {key:["a","b"],value:20*i})); -      T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i})); -      T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i})); - -      // test to make sure group reduce and limit params provide valid json -      var results = db.query(map, reduce, {group: true, limit: 2}); -      T(equals(results.rows[0], {key: ["a"], value: 20*i})); -      T(equals(results.rows.length, 2)); - -      //group by the first element in the key array -      var results = db.query(map, reduce, {group_level:1}); -      T(equals(results.rows[0], {key:["a"],value:70*i})); -      T(equals(results.rows[1], {key:["d"],value:40*i})); - -      //group by the first 2 elements in the key array -      var results = db.query(map, reduce, {group_level:2}); -      T(equals(results.rows[0], {key:["a"],value:20*i})); -      T(equals(results.rows[1], {key:["a","b"],value:40*i})); -      T(equals(results.rows[2], {key:["a","c"],value:10*i})); -      T(equals(results.rows[3], {key:["d"],value:10*i})); -      T(equals(results.rows[4], {key:["d","a"],value:10*i})); -      T(equals(results.rows[5], {key:["d","b"],value:10*i})); -      T(equals(results.rows[6], {key:["d","c"],value:10*i})); -    } - -    // now test out more complex reductions that need to use the combine option. - -    db.deleteDb(); -    db.createDb(); - - -    var map = function (doc) {emit(doc.val, doc.val)}; -    var reduceCombine = function (keys, values, rereduce) { -        // This computes the standard deviation of the mapped results -        var stdDeviation=0.0; -        var count=0; -        var total=0.0; -        var sqrTotal=0.0; - -        if (!rereduce) { -          // This is the reduce phase, we are reducing over emitted values from -          // the map functions. -          for(var i in values) { -            total = total + values[i]; -            sqrTotal = sqrTotal + (values[i] * values[i]); -          } -          count = values.length; -        } -        else { -          // This is the rereduce phase, we are re-reducing previosuly -          // reduced values. -          for(var i in values) { -            count = count + values[i].count; -            total = total + values[i].total; -            sqrTotal = sqrTotal + values[i].sqrTotal; -          } -        } - -        var variance =  (sqrTotal - ((total * total)/count)) / count; -        stdDeviation = Math.sqrt(variance); - -        // the reduce result. It contains enough information to be rereduced -        // with other reduce results. -        return {"stdDeviation":stdDeviation,"count":count, -            "total":total,"sqrTotal":sqrTotal}; -      }; - -      // Save a bunch a docs. - -    for(var i=0; i < 10; i++) { -      var docs = []; -      docs.push({val:10}); -      docs.push({val:20}); -      docs.push({val:30}); -      docs.push({val:40}); -      docs.push({val:50}); -      docs.push({val:60}); -      docs.push({val:70}); -      docs.push({val:80}); -      docs.push({val:90}); -      docs.push({val:100}); -      T(db.bulkSave(docs).ok); -    } -     -    var results = db.query(map, reduceCombine); -     -    var difference = results.rows[0].value.stdDeviation - 28.722813232690143; -    // account for floating point rounding error -    T(Math.abs(difference) < 0.0000000001); -     -  }, - -  reduce_false: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var numDocs = 5; -    var docs = makeDocs(1,numDocs + 1); -    T(db.bulkSave(docs).ok); -    var summate = function(N) {return (N+1)*N/2;}; - -    var designDoc = { -      _id:"_design/test", -      language: "javascript", -      views: { -        summate: {map:"function (doc) {emit(doc.integer, doc.integer)};", -                  reduce:"function (keys, values) { return sum(values); };"}, -      } -    }; -    T(db.save(designDoc).ok); - -    // Test that the reduce works -    var res = db.view('test/summate'); -    T(res.rows.length == 1 && res.rows[0].value == summate(5)); -     -    //Test that we get our docs back -    res = db.view('test/summate', {reduce: false}); -    T(res.rows.length == 5); -    for(var i=0; i<5; i++) -    { -      T(res.rows[i].value == i+1); -    } -  }, - -  design_options: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    //// test the includes_design option -    var map = "function (doc) {emit(null, doc._id);}"; - -    // we need a design doc even to test temp views with it -    var designDoc = { -      _id:"_design/fu", -      language: "javascript", -      options: { -        include_design: true         -      }, -      views: { -        data: {"map": map} -      } -    }; -    T(db.save(designDoc).ok); - -    // should work for temp views -    var rows = db.query(map, null, {options:{include_design: true}}).rows; -    T(rows.length == 1); -    T(rows[0].value == "_design/fu"); - -    rows = db.query(map).rows; -    T(rows.length == 0); - -    // when true, should include design docs in views -    rows = db.view("fu/data").rows; -    T(rows.length == 1); -    T(rows[0].value == "_design/fu"); - -    // when false, should not -    designDoc.options.include_design = false; -    delete designDoc._rev; -    designDoc._id = "_design/bingo"; -    T(db.save(designDoc).ok); -    rows = db.view("bingo/data").rows; -    T(rows.length == 0); - -    // should default to false -    delete designDoc.options; -    delete designDoc._rev; -    designDoc._id = "_design/bango"; -    T(db.save(designDoc).ok); -    rows = db.view("bango/data").rows; -    T(rows.length == 0); -  }, - -  multiple_rows: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var nc = {_id:"NC", cities:["Charlotte", "Raleigh"]}; -    var ma = {_id:"MA", cities:["Boston", "Lowell", "Worcester", "Cambridge", "Springfield"]}; -    var fl = {_id:"FL", cities:["Miami", "Tampa", "Orlando", "Springfield"]}; - -    T(db.save(nc).ok); -    T(db.save(ma).ok); -    T(db.save(fl).ok); - -    var generateListOfCitiesAndState = "function(doc) {" + -    " for (var i = 0; i < doc.cities.length; i++)" + -    "  emit(doc.cities[i] + \", \" + doc._id, null);" + -    "}"; - -    var results = db.query(generateListOfCitiesAndState); -    var rows = results.rows; - -    T(rows[0].key == "Boston, MA"); -    T(rows[1].key == "Cambridge, MA"); -    T(rows[2].key == "Charlotte, NC"); -    T(rows[3].key == "Lowell, MA"); -    T(rows[4].key == "Miami, FL"); -    T(rows[5].key == "Orlando, FL"); -    T(rows[6].key == "Raleigh, NC"); -    T(rows[7].key == "Springfield, FL"); -    T(rows[8].key == "Springfield, MA"); -    T(rows[9].key == "Tampa, FL"); -    T(rows[10].key == "Worcester, MA"); - -    // add another city to NC -    nc.cities.push("Wilmington"); -    T(db.save(nc).ok); - -    var results = db.query(generateListOfCitiesAndState); -    var rows = results.rows; - -    T(rows[0].key == "Boston, MA"); -    T(rows[1].key == "Cambridge, MA"); -    T(rows[2].key == "Charlotte, NC"); -    T(rows[3].key == "Lowell, MA"); -    T(rows[4].key == "Miami, FL"); -    T(rows[5].key == "Orlando, FL"); -    T(rows[6].key == "Raleigh, NC"); -    T(rows[7].key == "Springfield, FL"); -    T(rows[8].key == "Springfield, MA"); -    T(rows[9].key == "Tampa, FL"); -    T(rows[10].key == "Wilmington, NC"); -    T(rows[11].key == "Worcester, MA"); - -    // now delete MA -    T(db.deleteDoc(ma).ok); - -    var results = db.query(generateListOfCitiesAndState); -    var rows = results.rows; - -    T(rows[0].key == "Charlotte, NC"); -    T(rows[1].key == "Miami, FL"); -    T(rows[2].key == "Orlando, FL"); -    T(rows[3].key == "Raleigh, NC"); -    T(rows[4].key == "Springfield, FL"); -    T(rows[5].key == "Tampa, FL"); -    T(rows[6].key == "Wilmington, NC"); -  }, - -  large_docs: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var longtext = "0123456789\n"; - -    for (var i=0; i<10; i++) { -      longtext = longtext + longtext -    } -    T(db.save({"longtest":longtext}).ok); -    T(db.save({"longtest":longtext}).ok); -    T(db.save({"longtest":longtext}).ok); -    T(db.save({"longtest":longtext}).ok); - -    // query all documents, and return the doc.foo member as a key. -    results = db.query(function(doc){ -        emit(null, doc.longtest); -    }); -  }, - -  utf8: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var texts = []; - -    texts[0] = "1. Ascii: hello" -    texts[1] = "2. Russian: На берегу пустынных волн" -    texts[2] = "3. Math: ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i)," -    texts[3] = "4. Geek: STARGΛ̊TE SG-1" -    texts[4] = "5. Braille: ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌" - -    // check that we can save a reload with full fidelity -    for (var i=0; i<texts.length; i++) { -      T(db.save({_id:i.toString(), text:texts[i]}).ok); -    } - -    for (var i=0; i<texts.length; i++) { -      T(db.open(i.toString()).text == texts[i]); -    } - -    // check that views and key collation don't blow up -    var rows = db.query(function(doc) { emit(null, doc.text) }).rows; -    for (var i=0; i<texts.length; i++) { -      T(rows[i].value == texts[i]); -    } -  }, - -  attachments: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var binAttDoc = { -      _id: "bin_doc", -      _attachments:{ -        "foo.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        } -      } -    } - -    var save_response = db.save(binAttDoc); -    T(save_response.ok); - -    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(xhr.getResponseHeader("Etag") == save_response.rev); - -    // empty attachment -    var binAttDoc2 = { -      _id: "bin_doc2", -      _attachments:{ -        "foo.txt": { -          content_type:"text/plain", -          data: "" -        } -      } -    } - -    T(db.save(binAttDoc2).ok); - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo.txt"); -    T(xhr.responseText.length == 0); -    T(xhr.getResponseHeader("Content-Type") == "text/plain"); - -    // test RESTful doc API - -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt?rev=" + binAttDoc2._rev, { -      body:"This is no base64 encoded text", -      headers:{"Content-Type": "text/plain;charset=utf-8"} -    }); -    T(xhr.status == 201); -    var rev = JSON.parse(xhr.responseText).rev; - -    binAttDoc2 = db.open("bin_doc2"); - -    T(binAttDoc2._attachments["foo.txt"] !== undefined); -    T(binAttDoc2._attachments["foo2.txt"] !== undefined); -    T(binAttDoc2._attachments["foo2.txt"].content_type == "text/plain;charset=utf-8"); -    T(binAttDoc2._attachments["foo2.txt"].length == 30); - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo2.txt"); -    T(xhr.responseText == "This is no base64 encoded text"); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); -     -    // test without rev, should fail -    var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt"); -    T(xhr.status == 409); - -    // test with rev, should not fail -    var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt?rev=" + rev); -    T(xhr.status == 200); -     -     -    // test binary data -    var bin_data = "JHAPDO*AU£PN ){(3u[d 93DQ9¡€])}    ææøo'∂ƒæ≤çæππ•¥∫¶®#†π¶®¥π€ª®˙π8np"; -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", { -      headers:{"Content-Type":"text/plain;charset=utf-8"}, -      body:bin_data -    }); -    T(xhr.status == 201); -    var rev = JSON.parse(xhr.responseText).rev; -     -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); -    T(xhr.responseText == bin_data); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); -     -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", { -      headers:{"Content-Type":"text/plain;charset=utf-8"}, -      body:bin_data -    }); -    T(xhr.status == 409); - -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev, { -      headers:{"Content-Type":"text/plain;charset=utf-8"}, -      body:bin_data -    }); -    T(xhr.status == 201); -    var rev = JSON.parse(xhr.responseText).rev; - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); -    T(xhr.responseText == bin_data); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); -    T(xhr.responseText == bin_data); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); - -    var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); -    T(xhr.status == 200); -     -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); -    T(xhr.status == 404); - -    // empty attachments -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt", { -      headers:{"Content-Type":"text/plain;charset=utf-8"}, -      body:"" -    }); -    T(xhr.status == 201); -    var rev = JSON.parse(xhr.responseText).rev; - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt"); -    T(xhr.status == 200); -    T(xhr.responseText.length == 0); -     -    // overwrite previsously empty attachment -    var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt?rev=" + rev, { -      headers:{"Content-Type":"text/plain;charset=utf-8"}, -      body:"This is a string" -    }); -    T(xhr.status == 201); - -    var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt"); -    T(xhr.status == 200); -    T(xhr.responseText == "This is a string"); - -  }, - -  attachment_paths : function(debug) { -    if (debug) debugger; -    var dbNames = ["test_suite_db", "test_suite_db/with_slashes"]; -    for (var i=0; i < dbNames.length; i++) { -      var db = new CouchDB(dbNames[i]); -      var dbName = encodeURIComponent(dbNames[i]); -      db.deleteDb(); -      db.createDb(); - -      // first just save a regular doc with an attachment that has a slash in the url. -      // (also gonna run an encoding check case) -      var binAttDoc = { -        _id: "bin_doc", -        _attachments:{ -          "foo/bar.txt": { -            content_type:"text/plain", -            data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -          }, -          "foo%2Fbaz.txt": { -            content_type:"text/plain", -            data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg==" -          } -        } -      } - -      T(db.save(binAttDoc).ok); - -      var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/bar.txt"); -      T(xhr.responseText == "This is a base64 encoded text"); -      T(xhr.getResponseHeader("Content-Type") == "text/plain"); - -      // lets try it with an escaped attachment id... -      // weird that it's at two urls -      var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%2Fbar.txt"); -      T(xhr.status == 200);  -      // xhr.responseText == "This is a base64 encoded text" - -      var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/baz.txt"); -      T(xhr.status == 404); - -      var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%252Fbaz.txt"); -      T(xhr.status == 200);  -      T(xhr.responseText == "We like percent two F."); - -      // require a _rev to PUT -      var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/attachment.txt", { -        headers:{"Content-Type":"text/plain;charset=utf-8"}, -        body:"Just some text" -      }); -      T(xhr.status == 409);     - -      var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, { -        body:"This is no base64 encoded text", -        headers:{"Content-Type": "text/plain;charset=utf-8"} -      }); -      T(xhr.status == 201); -      var rev = JSON.parse(xhr.responseText).rev; - -      binAttDoc = db.open("bin_doc"); - -      T(binAttDoc._attachments["foo/bar.txt"] !== undefined); -      T(binAttDoc._attachments["foo%2Fbaz.txt"] !== undefined); -      T(binAttDoc._attachments["foo/bar2.txt"] !== undefined); -      T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8"); -      T(binAttDoc._attachments["foo/bar2.txt"].length == 30); - -      //// now repeat the while thing with a design doc -     -      // first just save a regular doc with an attachment that has a slash in the url. -      // (also gonna run an encoding check case) -      var binAttDoc = { -        _id: "_design/bin_doc", -        _attachments:{ -          "foo/bar.txt": { -            content_type:"text/plain", -            data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -          }, -          "foo%2Fbaz.txt": { -            content_type:"text/plain", -            data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg==" -          } -        } -      } - -      T(db.save(binAttDoc).ok); - -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/bar.txt"); -      T(xhr.responseText == "This is a base64 encoded text"); -      T(xhr.getResponseHeader("Content-Type") == "text/plain"); - -      // lets try it with an escaped attachment id... -      // weird that it's at two urls -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%2Fbar.txt"); -      T(xhr.responseText == "This is a base64 encoded text"); -      T(xhr.status == 200); - -      // err, 3 urls -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo%2Fbar.txt"); -      T(xhr.responseText == "This is a base64 encoded text"); -      T(xhr.status == 200); - -      // I mean um, 4 urls -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo/bar.txt"); -      T(xhr.responseText == "This is a base64 encoded text"); -      T(xhr.status == 200); - -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/baz.txt"); -      T(xhr.status == 404); - -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%252Fbaz.txt"); -      T(xhr.status == 200);  -      T(xhr.responseText == "We like percent two F."); - -      // require a _rev to PUT -      var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/attachment.txt", { -        headers:{"Content-Type":"text/plain;charset=utf-8"}, -        body:"Just some text" -      }); -      T(xhr.status == 409);     - -      var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, { -        body:"This is no base64 encoded text", -        headers:{"Content-Type": "text/plain;charset=utf-8"} -      }); -      T(xhr.status == 201); -      var rev = JSON.parse(xhr.responseText).rev; - -      binAttDoc = db.open("_design/bin_doc"); - -      T(binAttDoc._attachments["foo/bar.txt"] !== undefined); -      T(binAttDoc._attachments["foo/bar2.txt"] !== undefined); -      T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8"); -      T(binAttDoc._attachments["foo/bar2.txt"].length == 30); -    } -  }, - -  attachment_views: function(debug) { - -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    // count attachments in a view - -    db.bulkSave(makeDocs(0, 10)); - -    db.bulkSave(makeDocs(10, 20, { -      _attachments:{ -        "foo.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        } -      } -    })); - -    db.bulkSave(makeDocs(20, 30, { -      _attachments:{ -        "foo.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        }, -        "bar.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        } -      } -    })); - -    db.bulkSave(makeDocs(30, 40, { -      _attachments:{ -        "foo.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        }, -        "bar.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        }, -        "baz.txt": { -          content_type:"text/plain", -          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" -        } -      } -    })); - -    var mapFunction = function(doc) { -      var count = 0; - -      for(var idx in doc._attachments) { -        count = count + 1; -      } - -      emit(parseInt(doc._id), count); -    } - -    var reduceFunction = function(key, values) { -      return sum(values); -    } -     -    var result = db.query(mapFunction, reduceFunction); - -    T(result.rows.length == 1); -    T(result.rows[0].value == 60); - -    var result = db.query(mapFunction, reduceFunction, { -      startkey:10, -      endkey:19 -    }); - -    T(result.rows.length == 1); -    T(result.rows[0].value == 10); - -    var result = db.query(mapFunction, reduceFunction, { -      startkey:20, -      endkey:29 -    }); - -    T(result.rows.length == 1); -    T(result.rows[0].value == 20); - -  }, - -  design_paths : function(debug) { -    if (debug) debugger; -    var dbNames = ["test_suite_db", "test_suite_db/with_slashes"]; -    for (var i=0; i < dbNames.length; i++) { -      var db = new CouchDB(dbNames[i]); -      var dbName = encodeURIComponent(dbNames[i]); -      db.deleteDb(); -      db.createDb(); -       -      // create a ddoc w bulk_docs -      db.bulkSave([{ -        _id : "_design/test", -        views : { -          "testing" : { -            "map" : "function(){emit(1,1)}" -          } -        } -      }]); - -      // ddoc is getable -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test"); -      var resp = JSON.parse(xhr.responseText); -      T(resp._id == "_design/test"); - -      // it's at 2 urls... -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest"); -      var resp = JSON.parse(xhr.responseText); -      T(resp._id == "_design/test"); - -      // ensure that views are addressable -      resp = db.view("test/testing") -      T(resp.total_rows == 0) - -      // create a ddoc by putting to url with raw slash -      var xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test2",{ -        body : JSON.stringify({ -          _id : "_design/test2", -          views : { -            "testing" : { -              "map" : "function(){emit(1,1)}" -            } -          } -        }) -      }); - -      // ddoc is getable -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test2"); -      var resp = JSON.parse(xhr.responseText); -      T(resp._id == "_design/test2"); - -      // it's at 2 urls... -      var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest2"); -      var resp = JSON.parse(xhr.responseText); -      T(resp._id == "_design/test2"); - -      // ensure that views are addressable -      resp = db.view("test2/testing"); -      T(resp.total_rows == 0); -    }; -  }, - -  content_negotiation: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; -    var xhr; - -    xhr = CouchDB.request("GET", "/test_suite_db/"); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); - -    xhr = CouchDB.request("GET", "/test_suite_db/", { -      headers: {"Accept": "text/html;text/plain;*/*"} -    }); -    T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); - -    xhr = CouchDB.request("GET", "/test_suite_db/", { -      headers: {"Accept": "application/json"} -    }); -    T(xhr.getResponseHeader("Content-Type") == "application/json"); -  }, - -  design_docs: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    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", -      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<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); -  }, - -  invalid_docids: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    // Test _local explicitly first. -    T(db.save({"_id": "_local/foo"}).ok); -    T(db.open("_local/foo")._id == "_local/foo"); -     -    //Test non-string -    try { -      db.save({"_id": 1}); -      T(1 == 0); -    } catch(e) { -        T(db.last_req.status == 400); -        T(e.error == "invalid_doc"); -    } - -    // Test invalid _prefix -    try { -      db.save({"_id": "_invalid"}); -      T(1 == 0); -    } catch(e) { -        T(db.last_req.status == 400); -        T(e.error == "invalid_doc"); -    } - -    // Test _bulk_docs explicitly. -    var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}]; -    T(db.bulkSave(docs).ok); -    docs.forEach(function(d) {T(db.open(d._id)._id == d._id);}); - -    docs = [{"_id": "_invalid"}]; -    try { -      db.bulkSave(docs); -      T(1 == 0); -    } catch(e) { -        T(db.last_req.status == 400); -        T(e.error == "invalid_doc"); -    } -  }, - -  view_collation: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    // NOTE, the values are already in their correct sort order. Consider this -    // a specification of collation of json types. - -    var values = []; - -    // special values sort before all other types -    values.push(null); -    values.push(false); -    values.push(true); - -    // then numbers -    values.push(1); -    values.push(2); -    values.push(3.0); -    values.push(4); - -    // then text, case sensitive -    values.push("a"); -    values.push("A"); -    values.push("aa"); -    values.push("b"); -    values.push("B"); -    values.push("ba"); -    values.push("bb"); - -    // then arrays. compared element by element until different. -    // Longer arrays sort after their prefixes -    values.push(["a"]); -    values.push(["b"]); -    values.push(["b","c"]); -    values.push(["b","c", "a"]); -    values.push(["b","d"]); -    values.push(["b","d", "e"]); - -    // then object, compares each key value in the list until different. -    // larger objects sort after their subset objects. -    values.push({a:1}); -    values.push({a:2}); -    values.push({b:1}); -    values.push({b:2}); -    values.push({b:2, a:1}); // Member order does matter for collation. -                             // CouchDB preserves member order -                             // but doesn't require that clients will. -                             // (this test might fail if used with a js engine -                             // that doesn't preserve order) -    values.push({b:2, c:2}); - -    for (var i=0; i<values.length; i++) { -      db.save({_id:(i).toString(), foo:values[i]}); -    } - -    var queryFun = function(doc) { emit(doc.foo, null); }; -    var rows = db.query(queryFun).rows; -    for (i=0; i<values.length; i++) { -      T(equals(rows[i].key, values[i])); -    } - -    // everything has collated correctly. Now to check the descending output -    rows = db.query(queryFun, null, {descending: true}).rows; -    for (i=0; i<values.length; i++) { -      T(equals(rows[i].key, values[values.length - 1 -i])); -    } - -    // now check the key query args -    for (i=1; i<values.length; i++) { -      var queryOptions = {key:values[i]}; -      rows = db.query(queryFun, null, queryOptions).rows; -      T(rows.length == 1 && equals(rows[0].key, values[i])); -    } -  }, - -  view_conflicts: function(debug) { -    var dbA = new CouchDB("test_suite_db_a"); -    dbA.deleteDb(); -    dbA.createDb(); -    var dbB = new CouchDB("test_suite_db_b"); -    dbB.deleteDb(); -    dbB.createDb(); -    if (debug) debugger; - -    var docA = {_id: "foo", bar: 42}; -    T(dbA.save(docA).ok); -    CouchDB.replicate(dbA.name, dbB.name); - -    var docB = dbB.open("foo"); -    docB.bar = 43; -    dbB.save(docB); -    docA.bar = 41; -    dbA.save(docA); -    CouchDB.replicate(dbA.name, dbB.name); - -    var doc = dbB.open("foo", {conflicts: true}); -    T(doc._conflicts.length == 1); -    var conflictRev = doc._conflicts[0]; -    if (doc.bar == 41) { // A won -      T(conflictRev == docB._rev); -    } else { // B won -      T(doc.bar == 43); -      T(conflictRev == docA._rev); -    } - -    var results = dbB.query(function(doc) { -      if (doc._conflicts) { -        emit(doc._id, doc._conflicts); -      } -    }); -    T(results.rows[0].value[0] == conflictRev); -  }, - -  view_errors: 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); - -    // emitting a key value that is undefined should result in that row not -    // being included in the view results -    var results = db.query(function(doc) { -      emit(doc.undef, null); -    }); -    T(results.total_rows == 0); - -    // if a view function throws an exception, its results are not included in -    // the view index, but the view does not itself raise an error -    var results = db.query(function(doc) { -      doc.undef(); // throws an error -    }); -    T(results.total_rows == 0); - -    // if a view function includes an undefined value in the emitted key or -    // value, an error is logged and the result is not included in the view -    // index, and the view itself does not raise an error -    var results = db.query(function(doc) { -      emit([doc._id, doc.undef], null); -    }); -    T(results.total_rows == 0); -  }, - -  view_include_docs: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var docs = makeDocs(0, 100); -    T(db.bulkSave(docs).ok); - -    var designDoc = { -      _id:"_design/test", -      language: "javascript", -      views: { -        all_docs: { -          map: "function(doc) { emit(doc.integer, doc.string) }" -        }, -        with_prev: { -          map: "function(doc){if(doc.prev) emit(doc._id,{'_rev':doc.prev}); else emit(doc._id,{'_rev':doc._rev});}" -        }, -        summate: { -          map:"function (doc) {emit(doc.integer, doc.integer)};", -          reduce:"function (keys, values) { return sum(values); };" -        } -      } -    } -    T(db.save(designDoc).ok); - -    var resp = db.view('test/all_docs', {include_docs: true, limit: 2}); -    T(resp.rows.length == 2); -    T(resp.rows[0].id == "0"); -    T(resp.rows[0].doc._id == "0"); -    T(resp.rows[1].id == "1"); -    T(resp.rows[1].doc._id == "1"); - -    resp = db.view('test/all_docs', {include_docs: true}, [29, 74]); -    T(resp.rows.length == 2); -    T(resp.rows[0].doc._id == "29"); -    T(resp.rows[1].doc.integer == 74); - -    resp = db.allDocs({limit: 2, skip: 1, include_docs: true}); -    T(resp.rows.length == 2); -    T(resp.rows[0].doc.integer == 1); -    T(resp.rows[1].doc.integer == 10); - -    resp = db.allDocs({include_docs: true}, ['not_a_doc']); -    T(resp.rows.length == 1); -    T(!resp.rows[0].doc); - -    resp = db.allDocs({include_docs: true}, ["1", "foo"]); -    T(resp.rows.length == 2); -    T(resp.rows[0].doc.integer == 1); -    T(!resp.rows[1].doc); - -    resp = db.allDocs({include_docs: true, limit: 0}); -    T(resp.rows.length == 0); - -    // No reduce support -    try { -        resp = db.view('test/summate', {include_docs: true}); -        alert(JSON.stringify(resp)); -        T(0==1); -    } catch (e) { -        T(e.error == 'query_parse_error'); -    } - -    // Reduce support when reduce=false -    resp = db.view('test/summate', {reduce: false, include_docs: true}); -    T(resp.rows.length == 100); - -    // Check emitted _rev controls things -    resp = db.allDocs({include_docs: true}, ["0"]); -    var before = resp.rows[0].doc; -    var after = db.open("0"); -    after.integer = 100 -    after.prev = after._rev; -    db.save(after); -    after = db.open("0"); -    T(after._rev != after.prev); -    T(after.integer == 100); - -    // should emit the previous revision -    resp = db.view("test/with_prev", {include_docs: true}, ["0"]); -    T(resp.rows[0].doc._id == "0"); -    T(resp.rows[0].doc._rev == before._rev); -    T(!resp.rows[0].doc.prev); -    T(resp.rows[0].doc.integer == 0); - -    var xhr = CouchDB.request("POST", "/test_suite_db/_compact"); -    T(xhr.status == 202) -    while (db.info().compact_running) {} - -    resp = db.view("test/with_prev", {include_docs: true}, ["0", "23"]); -    T(resp.rows.length == 2); -    T(resp.rows[0].key == "0"); -    T(resp.rows[0].id == "0"); -    T(!resp.rows[0].doc); -    T(resp.rows[0].error == "missing"); -    T(resp.rows[1].doc.integer == 23); -  }, - -  view_multi_key_all_docs: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var docs = makeDocs(0, 100); -    T(db.bulkSave(docs).ok); - -    var keys = ["10","15","30","37","50"]; -    var rows = db.allDocs({},keys).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]); - -    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]); - -    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]); - -    rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).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); -    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"); -  }, - -  view_multi_key_design: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var docs = makeDocs(0, 100); -    T(db.bulkSave(docs).ok); - -    var designDoc = { -      _id:"_design/test", -      language: "javascript", -      views: { -        all_docs: { -          map: "function(doc) { emit(doc.integer, doc.string) }" -        }, -        multi_emit: { -          map: "function(doc) {for(var i = 0 ; i < 3 ; i++) { emit(i, doc.integer) ; } }" -        }, -        summate: { -          map:"function (doc) {emit(doc.integer, doc.integer)};", -          reduce:"function (keys, values) { return sum(values); };" -        } -      } -    } -    T(db.save(designDoc).ok); - -    // Test that missing keys work too -    var keys = [101,30,15,37,50] -    var reduce = db.view("test/summate",{group:true},keys).rows; -    T(reduce.length == keys.length-1); // 101 is missing -    for(var i=0; i<reduce.length; i++) { -      T(keys.indexOf(reduce[i].key) != -1); -      T(reduce[i].key == reduce[i].value); -    } - -    // First, the goods: -    var keys = [10,15,30,37,50]; -    var rows = db.view("test/all_docs",{},keys).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++) { -      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}]; -    for(var i in badargs) -    { -        try { -            db.view("test/all_docs",badargs[i],keys); -            T(0==1); -        } catch (e) { -            T(e.error == "query_parse_error"); -        } -    } - -    try { -        db.view("test/summate",{},keys); -        T(0==1); -    } catch (e) { -        T(e.error == "query_parse_error"); -    } - -    // Test that a map & reduce containing func support keys when reduce=false -    resp = db.view("test/summate", {reduce: false}, keys); -    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; -    var exp_key = [ 0,  0,  0,  2,  2,  2] ; -    var exp_val = [21, 22, 23, 21, 22, 23] ; -    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); - -    // Check offset works -    curr = db.view("test/multi_emit", {skip: 1}, [0]).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); - -    // 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", {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: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).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); -  }, - -  view_multi_key_temp: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var docs = makeDocs(0, 100); -    T(db.bulkSave(docs).ok); - -    var queryFun = function(doc) { emit(doc.integer, doc.integer) }; -    var reduceFun = function (keys, values) { return sum(values); }; - -    var keys = [10,15,30,37,50]; -    var rows = db.query(queryFun, null, {}, keys).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.query(queryFun, reduceFun, {group:true}, keys).rows; -    for(var i=0; i<reduce.length; i++) { -      T(keys.indexOf(reduce[i].key) != -1); -      T(reduce[i].key == reduce[i].value); -    } -  }, - -  view_pagination: function(debug) { -    var db = new CouchDB("test_suite_db"); -    db.deleteDb(); -    db.createDb(); -    if (debug) debugger; - -    var docs = makeDocs(0, 100); -    T(db.bulkSave(docs).ok); - -    var queryFun = function(doc) { emit(doc.integer, null) }; -    var i; - -    // page through the view ascending -    for (i = 0; i < docs.length; i += 10) { -      var queryResults = db.query(queryFun, null, { -        startkey: i, -        startkey_docid: i, -        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); -      } -    } - -    // page through the view descending -    for (i = docs.length - 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: "<doc><title id='xml'>Testing XML</title></doc>"}); -    db.save({content: "<doc><title id='e4x'>Testing E4X</title></doc>"}); - -    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('<xml><node foo="bar"/></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('<xml><node/></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<li>Key: '+row.key -                    +' Value: '+row.value -                    +' LineNo: '+row_info.row_number+'</li>' -            }; -          } else if (head) { -            // we return an object (like those used by external and show) -            // so that we can specify headers -            return { -              body : '<h1>Total Rows: ' -                + head.total_rows -                + ' Offset: ' + head.offset -                + '</h1><ul>' -            }; -          } else { -            // tail -            return {body : '</ul>'+ -                '<p>FirstKey: '+row_info.first_key+  -                ' LastKey: '+row_info.prev_key+'</p>'}; -          } -        }), -        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 <ul>"; -              } else if (row) { -                return '\n<li>Key: ' -                  +row.key+' Value: '+row.value -                  +' LineNo: '+row_info.row_number+'</li>'; -              } else { // tail -                return '</ul>'; - -              } -            }, -            xml : function() { -              if (head) { -                return '<feed xmlns="http://www.w3.org/2005/Atom">' -                  +'<title>Test XML Feed</title>'; -              } 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/>'); -                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 "</feed>"; -              } -            } -          }) -        }), -        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<max*2; i++) { -          var db = new CouchDB("test_suite_db"+ i); -          db.deleteDb(); -          db.createDb(); -        } -         -        var stats = JSON.parse(CouchDB.request("GET", "/_stats").responseText); -        T(stats.dbs_open == max); -         -         -        for(var i=0; i<max*2; i++) { -          var db = new CouchDB("test_suite_db"+ i); -          db.deleteDb(); -        } -         -        var stats = JSON.parse(CouchDB.request("GET", "/_stats").responseText); -        T(stats.dbs_open == 0); -      }) -  }, +function loadTest(file) { +  loadScript("script/test/"+file);  }; +loadTest("basics.js"); +loadTest("delayed_commits.js"); +loadTest("all_docs.js"); +loadTest("conflicts.js"); +loadTest("recreate_doc.js"); +loadTest("copy_move_doc.js"); +loadTest("uuids.js"); +loadTest("bulk_docs.js"); +loadTest("lots_of_docs.js"); +loadTest("reduce.js"); +loadTest("reduce_false.js"); +loadTest("design_options.js"); +loadTest("multiple_rows.js"); +loadTest("large_docs.js"); +loadTest("utf8.js"); +loadTest("attachments.js"); +loadTest("attachment_paths.js"); +loadTest("attachment_views.js"); +loadTest("design_paths.js"); +loadTest("content_negotiation.js"); +loadTest("design_docs.js"); +loadTest("invalid_docids.js"); +loadTest("view_collation.js"); +loadTest("view_conflicts.js"); +loadTest("view_errors.js"); +loadTest("view_include_docs.js"); +loadTest("view_multi_key_all_docs.js"); +loadTest("view_multi_key_design.js"); +loadTest("view_multi_key_temp.js"); +loadTest("view_pagination.js"); +loadTest("view_sandboxing.js"); +loadTest("view_xml.js"); +loadTest("replication.js"); +loadTest("etags_head.js"); +loadTest("etags_views.js"); +loadTest("show_documents.js"); +loadTest("list_views.js"); +loadTest("compact.js"); +loadTest("purge.js"); +loadTest("config.js"); +loadTest("security_validation.js"); +loadTest("max_dbs_open.js"); +  function makeDocs(start, end, templateDoc) {    var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}"    if (end === undefined) { diff --git a/share/www/script/test/all_docs.js b/share/www/script/test/all_docs.js new file mode 100644 index 00000000..3dd3aa53 --- /dev/null +++ b/share/www/script/test/all_docs.js @@ -0,0 +1,100 @@ +// 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.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); +}; diff --git a/share/www/script/test/attachment_paths.js b/share/www/script/test/attachment_paths.js new file mode 100644 index 00000000..ef9fa869 --- /dev/null +++ b/share/www/script/test/attachment_paths.js @@ -0,0 +1,147 @@ +// 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.attachment_paths = function(debug) { +  if (debug) debugger; +  var dbNames = ["test_suite_db", "test_suite_db/with_slashes"]; +  for (var i=0; i < dbNames.length; i++) { +    var db = new CouchDB(dbNames[i]); +    var dbName = encodeURIComponent(dbNames[i]); +    db.deleteDb(); +    db.createDb(); + +    // first just save a regular doc with an attachment that has a slash in the url. +    // (also gonna run an encoding check case) +    var binAttDoc = { +      _id: "bin_doc", +      _attachments:{ +        "foo/bar.txt": { +          content_type:"text/plain", +          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +        }, +        "foo%2Fbaz.txt": { +          content_type:"text/plain", +          data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg==" +        } +      } +    } + +    T(db.save(binAttDoc).ok); + +    var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/bar.txt"); +    T(xhr.responseText == "This is a base64 encoded text"); +    T(xhr.getResponseHeader("Content-Type") == "text/plain"); + +    // lets try it with an escaped attachment id... +    // weird that it's at two urls +    var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%2Fbar.txt"); +    T(xhr.status == 200);  +    // xhr.responseText == "This is a base64 encoded text" + +    var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo/baz.txt"); +    T(xhr.status == 404); + +    var xhr = CouchDB.request("GET", "/"+dbName+"/bin_doc/foo%252Fbaz.txt"); +    T(xhr.status == 200);  +    T(xhr.responseText == "We like percent two F."); + +    // require a _rev to PUT +    var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/attachment.txt", { +      headers:{"Content-Type":"text/plain;charset=utf-8"}, +      body:"Just some text" +    }); +    T(xhr.status == 409);     + +    var xhr = CouchDB.request("PUT", "/"+dbName+"/bin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, { +      body:"This is no base64 encoded text", +      headers:{"Content-Type": "text/plain;charset=utf-8"} +    }); +    T(xhr.status == 201); +    var rev = JSON.parse(xhr.responseText).rev; + +    binAttDoc = db.open("bin_doc"); + +    T(binAttDoc._attachments["foo/bar.txt"] !== undefined); +    T(binAttDoc._attachments["foo%2Fbaz.txt"] !== undefined); +    T(binAttDoc._attachments["foo/bar2.txt"] !== undefined); +    T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8"); +    T(binAttDoc._attachments["foo/bar2.txt"].length == 30); + +    //// now repeat the while thing with a design doc +   +    // first just save a regular doc with an attachment that has a slash in the url. +    // (also gonna run an encoding check case) +    var binAttDoc = { +      _id: "_design/bin_doc", +      _attachments:{ +        "foo/bar.txt": { +          content_type:"text/plain", +          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +        }, +        "foo%2Fbaz.txt": { +          content_type:"text/plain", +          data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg==" +        } +      } +    } + +    T(db.save(binAttDoc).ok); + +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/bar.txt"); +    T(xhr.responseText == "This is a base64 encoded text"); +    T(xhr.getResponseHeader("Content-Type") == "text/plain"); + +    // lets try it with an escaped attachment id... +    // weird that it's at two urls +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%2Fbar.txt"); +    T(xhr.responseText == "This is a base64 encoded text"); +    T(xhr.status == 200); + +    // err, 3 urls +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo%2Fbar.txt"); +    T(xhr.responseText == "This is a base64 encoded text"); +    T(xhr.status == 200); + +    // I mean um, 4 urls +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/bin_doc/foo/bar.txt"); +    T(xhr.responseText == "This is a base64 encoded text"); +    T(xhr.status == 200); + +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo/baz.txt"); +    T(xhr.status == 404); + +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Fbin_doc/foo%252Fbaz.txt"); +    T(xhr.status == 200);  +    T(xhr.responseText == "We like percent two F."); + +    // require a _rev to PUT +    var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/attachment.txt", { +      headers:{"Content-Type":"text/plain;charset=utf-8"}, +      body:"Just some text" +    }); +    T(xhr.status == 409);     + +    var xhr = CouchDB.request("PUT", "/"+dbName+"/_design%2Fbin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, { +      body:"This is no base64 encoded text", +      headers:{"Content-Type": "text/plain;charset=utf-8"} +    }); +    T(xhr.status == 201); +    var rev = JSON.parse(xhr.responseText).rev; + +    binAttDoc = db.open("_design/bin_doc"); + +    T(binAttDoc._attachments["foo/bar.txt"] !== undefined); +    T(binAttDoc._attachments["foo/bar2.txt"] !== undefined); +    T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8"); +    T(binAttDoc._attachments["foo/bar2.txt"].length == 30); +  } +}; diff --git a/share/www/script/test/attachment_views.js b/share/www/script/test/attachment_views.js new file mode 100644 index 00000000..b97a4130 --- /dev/null +++ b/share/www/script/test/attachment_views.js @@ -0,0 +1,98 @@ +// 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.attachment_views= function(debug) { + +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  // count attachments in a view + +  db.bulkSave(makeDocs(0, 10)); + +  db.bulkSave(makeDocs(10, 20, { +    _attachments:{ +      "foo.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      } +    } +  })); + +  db.bulkSave(makeDocs(20, 30, { +    _attachments:{ +      "foo.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      }, +      "bar.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      } +    } +  })); + +  db.bulkSave(makeDocs(30, 40, { +    _attachments:{ +      "foo.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      }, +      "bar.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      }, +      "baz.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      } +    } +  })); + +  var mapFunction = function(doc) { +    var count = 0; + +    for(var idx in doc._attachments) { +      count = count + 1; +    } + +    emit(parseInt(doc._id), count); +  } + +  var reduceFunction = function(key, values) { +    return sum(values); +  } +   +  var result = db.query(mapFunction, reduceFunction); + +  T(result.rows.length == 1); +  T(result.rows[0].value == 60); + +  var result = db.query(mapFunction, reduceFunction, { +    startkey:10, +    endkey:19 +  }); + +  T(result.rows.length == 1); +  T(result.rows[0].value == 10); + +  var result = db.query(mapFunction, reduceFunction, { +    startkey:20, +    endkey:29 +  }); + +  T(result.rows.length == 1); +  T(result.rows[0].value == 20); + +}; diff --git a/share/www/script/test/attachments.js b/share/www/script/test/attachments.js new file mode 100644 index 00000000..409426c7 --- /dev/null +++ b/share/www/script/test/attachments.js @@ -0,0 +1,146 @@ +// 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.attachments= function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var binAttDoc = { +    _id: "bin_doc", +    _attachments:{ +      "foo.txt": { +        content_type:"text/plain", +        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=" +      } +    } +  } + +  var save_response = db.save(binAttDoc); +  T(save_response.ok); + +  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(xhr.getResponseHeader("Etag") == save_response.rev); + +  // empty attachment +  var binAttDoc2 = { +    _id: "bin_doc2", +    _attachments:{ +      "foo.txt": { +        content_type:"text/plain", +        data: "" +      } +    } +  } + +  T(db.save(binAttDoc2).ok); + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo.txt"); +  T(xhr.responseText.length == 0); +  T(xhr.getResponseHeader("Content-Type") == "text/plain"); + +  // test RESTful doc API + +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt?rev=" + binAttDoc2._rev, { +    body:"This is no base64 encoded text", +    headers:{"Content-Type": "text/plain;charset=utf-8"} +  }); +  T(xhr.status == 201); +  var rev = JSON.parse(xhr.responseText).rev; + +  binAttDoc2 = db.open("bin_doc2"); + +  T(binAttDoc2._attachments["foo.txt"] !== undefined); +  T(binAttDoc2._attachments["foo2.txt"] !== undefined); +  T(binAttDoc2._attachments["foo2.txt"].content_type == "text/plain;charset=utf-8"); +  T(binAttDoc2._attachments["foo2.txt"].length == 30); + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc2/foo2.txt"); +  T(xhr.responseText == "This is no base64 encoded text"); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); +   +  // test without rev, should fail +  var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt"); +  T(xhr.status == 409); + +  // test with rev, should not fail +  var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc2/foo2.txt?rev=" + rev); +  T(xhr.status == 200); +   +   +  // test binary data +  var bin_data = "JHAPDO*AU£PN ){(3u[d 93DQ9¡€])}    ææøo'∂ƒæ≤çæππ•¥∫¶®#†π¶®¥π€ª®˙π8np"; +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", { +    headers:{"Content-Type":"text/plain;charset=utf-8"}, +    body:bin_data +  }); +  T(xhr.status == 201); +  var rev = JSON.parse(xhr.responseText).rev; +   +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); +  T(xhr.responseText == bin_data); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); +   +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt", { +    headers:{"Content-Type":"text/plain;charset=utf-8"}, +    body:bin_data +  }); +  T(xhr.status == 409); + +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev, { +    headers:{"Content-Type":"text/plain;charset=utf-8"}, +    body:bin_data +  }); +  T(xhr.status == 201); +  var rev = JSON.parse(xhr.responseText).rev; + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt"); +  T(xhr.responseText == bin_data); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); +  T(xhr.responseText == bin_data); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); + +  var xhr = CouchDB.request("DELETE", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); +  T(xhr.status == 200); +   +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc3/attachment.txt?rev=" + rev); +  T(xhr.status == 404); + +  // empty attachments +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt", { +    headers:{"Content-Type":"text/plain;charset=utf-8"}, +    body:"" +  }); +  T(xhr.status == 201); +  var rev = JSON.parse(xhr.responseText).rev; + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt"); +  T(xhr.status == 200); +  T(xhr.responseText.length == 0); +   +  // overwrite previsously empty attachment +  var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc4/attachment.txt?rev=" + rev, { +    headers:{"Content-Type":"text/plain;charset=utf-8"}, +    body:"This is a string" +  }); +  T(xhr.status == 201); + +  var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc4/attachment.txt"); +  T(xhr.status == 200); +  T(xhr.responseText == "This is a string"); + +}; diff --git a/share/www/script/test/basics.js b/share/www/script/test/basics.js new file mode 100644 index 00000000..89e99a64 --- /dev/null +++ b/share/www/script/test/basics.js @@ -0,0 +1,144 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +// Do some basic tests. +couchTests.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");     +  }; diff --git a/share/www/script/test/bulk_docs.js b/share/www/script/test/bulk_docs.js new file mode 100644 index 00000000..d781b2c6 --- /dev/null +++ b/share/www/script/test/bulk_docs.js @@ -0,0 +1,56 @@ +// 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.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 != ""); +}; diff --git a/share/www/script/test/compact.js b/share/www/script/test/compact.js new file mode 100644 index 00000000..4612621e --- /dev/null +++ b/share/www/script/test/compact.js @@ -0,0 +1,56 @@ +// 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.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); +   +}; diff --git a/share/www/script/test/config.js b/share/www/script/test/config.js new file mode 100644 index 00000000..a156be37 --- /dev/null +++ b/share/www/script/test/config.js @@ -0,0 +1,66 @@ +// 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.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"'); +}; diff --git a/share/www/script/test/conflicts.js b/share/www/script/test/conflicts.js new file mode 100644 index 00000000..02e50035 --- /dev/null +++ b/share/www/script/test/conflicts.js @@ -0,0 +1,60 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +// Do some edit conflict detection tests +couchTests.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. +}; diff --git a/share/www/script/test/content_negotiation.js b/share/www/script/test/content_negotiation.js new file mode 100644 index 00000000..757105ff --- /dev/null +++ b/share/www/script/test/content_negotiation.js @@ -0,0 +1,32 @@ +// 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.content_negotiation = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; +  var xhr; + +  xhr = CouchDB.request("GET", "/test_suite_db/"); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); + +  xhr = CouchDB.request("GET", "/test_suite_db/", { +    headers: {"Accept": "text/html;text/plain;*/*"} +  }); +  T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8"); + +  xhr = CouchDB.request("GET", "/test_suite_db/", { +    headers: {"Accept": "application/json"} +  }); +  T(xhr.getResponseHeader("Content-Type") == "application/json"); +}; diff --git a/share/www/script/test/copy_move_doc.js b/share/www/script/test/copy_move_doc.js new file mode 100644 index 00000000..050f8113 --- /dev/null +++ b/share/www/script/test/copy_move_doc.js @@ -0,0 +1,65 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.copy_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); +}; diff --git a/share/www/script/test/delayed_commits.js b/share/www/script/test/delayed_commits.js new file mode 100644 index 00000000..daebdb8d --- /dev/null +++ b/share/www/script/test/delayed_commits.js @@ -0,0 +1,93 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.delayed_commits = function(debug) { +  var db = new CouchDB("test_suite_db"); +  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); +   +}; diff --git a/share/www/script/test/design_docs.js b/share/www/script/test/design_docs.js new file mode 100644 index 00000000..aedf741a --- /dev/null +++ b/share/www/script/test/design_docs.js @@ -0,0 +1,107 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.design_docs = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  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", +    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<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); +}; diff --git a/share/www/script/test/design_options.js b/share/www/script/test/design_options.js new file mode 100644 index 00000000..c2764b91 --- /dev/null +++ b/share/www/script/test/design_options.js @@ -0,0 +1,63 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.design_options = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  //// test the includes_design option +  var map = "function (doc) {emit(null, doc._id);}"; + +  // we need a design doc even to test temp views with it +  var designDoc = { +    _id:"_design/fu", +    language: "javascript", +    options: { +      include_design: true         +    }, +    views: { +      data: {"map": map} +    } +  }; +  T(db.save(designDoc).ok); + +  // should work for temp views +  var rows = db.query(map, null, {options:{include_design: true}}).rows; +  T(rows.length == 1); +  T(rows[0].value == "_design/fu"); + +  rows = db.query(map).rows; +  T(rows.length == 0); + +  // when true, should include design docs in views +  rows = db.view("fu/data").rows; +  T(rows.length == 1); +  T(rows[0].value == "_design/fu"); + +  // when false, should not +  designDoc.options.include_design = false; +  delete designDoc._rev; +  designDoc._id = "_design/bingo"; +  T(db.save(designDoc).ok); +  rows = db.view("bingo/data").rows; +  T(rows.length == 0); + +  // should default to false +  delete designDoc.options; +  delete designDoc._rev; +  designDoc._id = "_design/bango"; +  T(db.save(designDoc).ok); +  rows = db.view("bango/data").rows; +  T(rows.length == 0); +}; diff --git a/share/www/script/test/design_paths.js b/share/www/script/test/design_paths.js new file mode 100644 index 00000000..7722a188 --- /dev/null +++ b/share/www/script/test/design_paths.js @@ -0,0 +1,72 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.design_paths = function(debug) { +  if (debug) debugger; +  var dbNames = ["test_suite_db", "test_suite_db/with_slashes"]; +  for (var i=0; i < dbNames.length; i++) { +    var db = new CouchDB(dbNames[i]); +    var dbName = encodeURIComponent(dbNames[i]); +    db.deleteDb(); +    db.createDb(); +     +    // create a ddoc w bulk_docs +    db.bulkSave([{ +      _id : "_design/test", +      views : { +        "testing" : { +          "map" : "function(){emit(1,1)}" +        } +      } +    }]); + +    // ddoc is getable +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test"); +    var resp = JSON.parse(xhr.responseText); +    T(resp._id == "_design/test"); + +    // it's at 2 urls... +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest"); +    var resp = JSON.parse(xhr.responseText); +    T(resp._id == "_design/test"); + +    // ensure that views are addressable +    resp = db.view("test/testing") +    T(resp.total_rows == 0) + +    // create a ddoc by putting to url with raw slash +    var xhr = CouchDB.request("PUT", "/"+dbName+"/_design/test2",{ +      body : JSON.stringify({ +        _id : "_design/test2", +        views : { +          "testing" : { +            "map" : "function(){emit(1,1)}" +          } +        } +      }) +    }); + +    // ddoc is getable +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design/test2"); +    var resp = JSON.parse(xhr.responseText); +    T(resp._id == "_design/test2"); + +    // it's at 2 urls... +    var xhr = CouchDB.request("GET", "/"+dbName+"/_design%2Ftest2"); +    var resp = JSON.parse(xhr.responseText); +    T(resp._id == "_design/test2"); + +    // ensure that views are addressable +    resp = db.view("test2/testing"); +    T(resp.total_rows == 0); +  }; +}; diff --git a/share/www/script/test/etags_head.js b/share/www/script/test/etags_head.js new file mode 100644 index 00000000..097e6f9a --- /dev/null +++ b/share/www/script/test/etags_head.js @@ -0,0 +1,78 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.etags_head = function(debug) { +  var db = new CouchDB("test_suite_db"); +  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); +}; diff --git a/share/www/script/test/etags_views.js b/share/www/script/test/etags_views.js new file mode 100644 index 00000000..808a6829 --- /dev/null +++ b/share/www/script/test/etags_views.js @@ -0,0 +1,87 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.etags_views = function(debug) { +  var db = new CouchDB("test_suite_db"); +  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 +}; diff --git a/share/www/script/test/invalid_docids.js b/share/www/script/test/invalid_docids.js new file mode 100644 index 00000000..6ed01f0b --- /dev/null +++ b/share/www/script/test/invalid_docids.js @@ -0,0 +1,54 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.invalid_docids = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  // Test _local explicitly first. +  T(db.save({"_id": "_local/foo"}).ok); +  T(db.open("_local/foo")._id == "_local/foo"); +   +  //Test non-string +  try { +    db.save({"_id": 1}); +    T(1 == 0); +  } catch(e) { +      T(db.last_req.status == 400); +      T(e.error == "invalid_doc"); +  } + +  // Test invalid _prefix +  try { +    db.save({"_id": "_invalid"}); +    T(1 == 0); +  } catch(e) { +      T(db.last_req.status == 400); +      T(e.error == "invalid_doc"); +  } + +  // Test _bulk_docs explicitly. +  var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}]; +  T(db.bulkSave(docs).ok); +  docs.forEach(function(d) {T(db.open(d._id)._id == d._id);}); + +  docs = [{"_id": "_invalid"}]; +  try { +    db.bulkSave(docs); +    T(1 == 0); +  } catch(e) { +      T(db.last_req.status == 400); +      T(e.error == "invalid_doc"); +  } +}; diff --git a/share/www/script/test/large_docs.js b/share/www/script/test/large_docs.js new file mode 100644 index 00000000..4f0b343d --- /dev/null +++ b/share/www/script/test/large_docs.js @@ -0,0 +1,33 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.large_docs = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var longtext = "0123456789\n"; + +  for (var i=0; i<10; i++) { +    longtext = longtext + longtext +  } +  T(db.save({"longtest":longtext}).ok); +  T(db.save({"longtest":longtext}).ok); +  T(db.save({"longtest":longtext}).ok); +  T(db.save({"longtest":longtext}).ok); + +  // query all documents, and return the doc.foo member as a key. +  results = db.query(function(doc){ +      emit(null, doc.longtest); +  }); +}; diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js new file mode 100644 index 00000000..de593035 --- /dev/null +++ b/share/www/script/test/list_views.js @@ -0,0 +1,216 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.list_views = function(debug) { +  var db = new CouchDB("test_suite_db"); +  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<li>Key: '+row.key +                  +' Value: '+row.value +                  +' LineNo: '+row_info.row_number+'</li>' +          }; +        } else if (head) { +          // we return an object (like those used by external and show) +          // so that we can specify headers +          return { +            body : '<h1>Total Rows: ' +              + head.total_rows +              + ' Offset: ' + head.offset +              + '</h1><ul>' +          }; +        } else { +          // tail +          return {body : '</ul>'+ +              '<p>FirstKey: '+row_info.first_key+  +              ' LastKey: '+row_info.prev_key+'</p>'}; +        } +      }), +      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 <ul>"; +            } else if (row) { +              return '\n<li>Key: ' +                +row.key+' Value: '+row.value +                +' LineNo: '+row_info.row_number+'</li>'; +            } else { // tail +              return '</ul>'; + +            } +          }, +          xml : function() { +            if (head) { +              return '<feed xmlns="http://www.w3.org/2005/Atom">' +                +'<title>Test XML Feed</title>'; +            } 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/>'); +              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 "</feed>"; +            } +          } +        }) +      }), +      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$/)); + +}; diff --git a/share/www/script/test/lots_of_docs.js b/share/www/script/test/lots_of_docs.js new file mode 100644 index 00000000..702317e5 --- /dev/null +++ b/share/www/script/test/lots_of_docs.js @@ -0,0 +1,56 @@ +// 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. + + +// test saving a semi-large quanitity of documents and do some view queries. +couchTests.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<numDocsToCreate; i++) { +    T(results.rows[i].key==i); +  } + +  // do the query again, but with descending output +  results = db.query(function(doc){ emit(doc.integer, null) }, null, { +    descending: true +  }); + +  T(results.total_rows == numDocsToCreate); + +  // validate the keys are ordered descending +  for(var i=0; i<numDocsToCreate; i++) { +    T(results.rows[numDocsToCreate-1-i].key==i); +  } + +  // Check _all_docs with descending=true again (now that there are many docs) +  var desc = db.allDocs({descending:true}); +  T(desc.total_rows == desc.rows.length); +}; diff --git a/share/www/script/test/max_dbs_open.js b/share/www/script/test/max_dbs_open.js new file mode 100644 index 00000000..44c4fa38 --- /dev/null +++ b/share/www/script/test/max_dbs_open.js @@ -0,0 +1,41 @@ +// 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.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<max*2; i++) { +        var db = new CouchDB("test_suite_db"+ i); +        db.deleteDb(); +        db.createDb(); +      } +       +      var stats = JSON.parse(CouchDB.request("GET", "/_stats").responseText); +      T(stats.dbs_open == max); +       +       +      for(var i=0; i<max*2; i++) { +        var db = new CouchDB("test_suite_db"+ i); +        db.deleteDb(); +      } +       +      var stats = JSON.parse(CouchDB.request("GET", "/_stats").responseText); +      T(stats.dbs_open == 0); +    }) +}; diff --git a/share/www/script/test/multiple_rows.js b/share/www/script/test/multiple_rows.js new file mode 100644 index 00000000..8db2702c --- /dev/null +++ b/share/www/script/test/multiple_rows.js @@ -0,0 +1,80 @@ +// 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.multiple_rows = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var nc = {_id:"NC", cities:["Charlotte", "Raleigh"]}; +  var ma = {_id:"MA", cities:["Boston", "Lowell", "Worcester", "Cambridge", "Springfield"]}; +  var fl = {_id:"FL", cities:["Miami", "Tampa", "Orlando", "Springfield"]}; + +  T(db.save(nc).ok); +  T(db.save(ma).ok); +  T(db.save(fl).ok); + +  var generateListOfCitiesAndState = "function(doc) {" + +  " for (var i = 0; i < doc.cities.length; i++)" + +  "  emit(doc.cities[i] + \", \" + doc._id, null);" + +  "}"; + +  var results = db.query(generateListOfCitiesAndState); +  var rows = results.rows; + +  T(rows[0].key == "Boston, MA"); +  T(rows[1].key == "Cambridge, MA"); +  T(rows[2].key == "Charlotte, NC"); +  T(rows[3].key == "Lowell, MA"); +  T(rows[4].key == "Miami, FL"); +  T(rows[5].key == "Orlando, FL"); +  T(rows[6].key == "Raleigh, NC"); +  T(rows[7].key == "Springfield, FL"); +  T(rows[8].key == "Springfield, MA"); +  T(rows[9].key == "Tampa, FL"); +  T(rows[10].key == "Worcester, MA"); + +  // add another city to NC +  nc.cities.push("Wilmington"); +  T(db.save(nc).ok); + +  var results = db.query(generateListOfCitiesAndState); +  var rows = results.rows; + +  T(rows[0].key == "Boston, MA"); +  T(rows[1].key == "Cambridge, MA"); +  T(rows[2].key == "Charlotte, NC"); +  T(rows[3].key == "Lowell, MA"); +  T(rows[4].key == "Miami, FL"); +  T(rows[5].key == "Orlando, FL"); +  T(rows[6].key == "Raleigh, NC"); +  T(rows[7].key == "Springfield, FL"); +  T(rows[8].key == "Springfield, MA"); +  T(rows[9].key == "Tampa, FL"); +  T(rows[10].key == "Wilmington, NC"); +  T(rows[11].key == "Worcester, MA"); + +  // now delete MA +  T(db.deleteDoc(ma).ok); + +  var results = db.query(generateListOfCitiesAndState); +  var rows = results.rows; + +  T(rows[0].key == "Charlotte, NC"); +  T(rows[1].key == "Miami, FL"); +  T(rows[2].key == "Orlando, FL"); +  T(rows[3].key == "Raleigh, NC"); +  T(rows[4].key == "Springfield, FL"); +  T(rows[5].key == "Tampa, FL"); +  T(rows[6].key == "Wilmington, NC"); +}; diff --git a/share/www/script/test/purge.js b/share/www/script/test/purge.js new file mode 100644 index 00000000..e05ac024 --- /dev/null +++ b/share/www/script/test/purge.js @@ -0,0 +1,101 @@ +// 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.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); +}; diff --git a/share/www/script/test/recreate_doc.js b/share/www/script/test/recreate_doc.js new file mode 100644 index 00000000..d730c2bd --- /dev/null +++ b/share/www/script/test/recreate_doc.js @@ -0,0 +1,39 @@ +// 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.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); +    } +  } +}; diff --git a/share/www/script/test/reduce.js b/share/www/script/test/reduce.js new file mode 100644 index 00000000..d3245f98 --- /dev/null +++ b/share/www/script/test/reduce.js @@ -0,0 +1,169 @@ +// 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.reduce = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; +  var numDocs = 500 +  var docs = makeDocs(1,numDocs + 1); +  T(db.bulkSave(docs).ok); +  var summate = function(N) {return (N+1)*N/2;}; + +  var map = function (doc) { +      emit(doc.integer, doc.integer); +      emit(doc.integer, doc.integer)}; +  var reduce = function (keys, values) { return sum(values); }; +  var result = db.query(map, reduce); +  T(result.rows[0].value == 2*summate(numDocs)); + +  result = db.query(map, reduce, {startkey: 4, endkey: 4}); +  T(result.rows[0].value == 8); + +  result = db.query(map, reduce, {startkey: 4, endkey: 5}); +  T(result.rows[0].value == 18); + +  result = db.query(map, reduce, {startkey: 4, endkey: 6}); +  T(result.rows[0].value == 30); + +  result = db.query(map, reduce, {group:true, limit:3}); +  T(result.rows[0].value == 2); +  T(result.rows[1].value == 4); +  T(result.rows[2].value == 6); + +  for(var i=1; i<numDocs/2; i+=30) { +    result = db.query(map, reduce, {startkey: i, endkey: numDocs - i}); +    T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1))); +  } + +  db.deleteDb(); +  db.createDb(); + +  for(var i=1; i <= 5; i++) { + +    for(var j=0; j < 10; j++) { +      // these docs are in the order of the keys collation, for clarity +      var docs = []; +      docs.push({keys:["a"]}); +      docs.push({keys:["a"]}); +      docs.push({keys:["a", "b"]}); +      docs.push({keys:["a", "b"]}); +      docs.push({keys:["a", "b", "c"]}); +      docs.push({keys:["a", "b", "d"]}); +      docs.push({keys:["a", "c", "d"]}); +      docs.push({keys:["d"]}); +      docs.push({keys:["d", "a"]}); +      docs.push({keys:["d", "b"]}); +      docs.push({keys:["d", "c"]}); +      T(db.bulkSave(docs).ok); +      T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11)); +    } + +    map = function (doc) {emit(doc.keys, 1)}; +    reduce = function (keys, values) { return sum(values); }; + +    var results = db.query(map, reduce, {group:true}); + +    //group by exact key match +    T(equals(results.rows[0], {key:["a"],value:20*i})); +    T(equals(results.rows[1], {key:["a","b"],value:20*i})); +    T(equals(results.rows[2], {key:["a", "b", "c"],value:10*i})); +    T(equals(results.rows[3], {key:["a", "b", "d"],value:10*i})); + +    // test to make sure group reduce and limit params provide valid json +    var results = db.query(map, reduce, {group: true, limit: 2}); +    T(equals(results.rows[0], {key: ["a"], value: 20*i})); +    T(equals(results.rows.length, 2)); + +    //group by the first element in the key array +    var results = db.query(map, reduce, {group_level:1}); +    T(equals(results.rows[0], {key:["a"],value:70*i})); +    T(equals(results.rows[1], {key:["d"],value:40*i})); + +    //group by the first 2 elements in the key array +    var results = db.query(map, reduce, {group_level:2}); +    T(equals(results.rows[0], {key:["a"],value:20*i})); +    T(equals(results.rows[1], {key:["a","b"],value:40*i})); +    T(equals(results.rows[2], {key:["a","c"],value:10*i})); +    T(equals(results.rows[3], {key:["d"],value:10*i})); +    T(equals(results.rows[4], {key:["d","a"],value:10*i})); +    T(equals(results.rows[5], {key:["d","b"],value:10*i})); +    T(equals(results.rows[6], {key:["d","c"],value:10*i})); +  } + +  // now test out more complex reductions that need to use the combine option. + +  db.deleteDb(); +  db.createDb(); + + +  var map = function (doc) {emit(doc.val, doc.val)}; +  var reduceCombine = function (keys, values, rereduce) { +      // This computes the standard deviation of the mapped results +      var stdDeviation=0.0; +      var count=0; +      var total=0.0; +      var sqrTotal=0.0; + +      if (!rereduce) { +        // This is the reduce phase, we are reducing over emitted values from +        // the map functions. +        for(var i in values) { +          total = total + values[i]; +          sqrTotal = sqrTotal + (values[i] * values[i]); +        } +        count = values.length; +      } +      else { +        // This is the rereduce phase, we are re-reducing previosuly +        // reduced values. +        for(var i in values) { +          count = count + values[i].count; +          total = total + values[i].total; +          sqrTotal = sqrTotal + values[i].sqrTotal; +        } +      } + +      var variance =  (sqrTotal - ((total * total)/count)) / count; +      stdDeviation = Math.sqrt(variance); + +      // the reduce result. It contains enough information to be rereduced +      // with other reduce results. +      return {"stdDeviation":stdDeviation,"count":count, +          "total":total,"sqrTotal":sqrTotal}; +    }; + +    // Save a bunch a docs. + +  for(var i=0; i < 10; i++) { +    var docs = []; +    docs.push({val:10}); +    docs.push({val:20}); +    docs.push({val:30}); +    docs.push({val:40}); +    docs.push({val:50}); +    docs.push({val:60}); +    docs.push({val:70}); +    docs.push({val:80}); +    docs.push({val:90}); +    docs.push({val:100}); +    T(db.bulkSave(docs).ok); +  } +   +  var results = db.query(map, reduceCombine); +   +  var difference = results.rows[0].value.stdDeviation - 28.722813232690143; +  // account for floating point rounding error +  T(Math.abs(difference) < 0.0000000001); +   +}; diff --git a/share/www/script/test/reduce_false.js b/share/www/script/test/reduce_false.js new file mode 100644 index 00000000..41f0c669 --- /dev/null +++ b/share/www/script/test/reduce_false.js @@ -0,0 +1,45 @@ +// 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.reduce_false = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var numDocs = 5; +  var docs = makeDocs(1,numDocs + 1); +  T(db.bulkSave(docs).ok); +  var summate = function(N) {return (N+1)*N/2;}; + +  var designDoc = { +    _id:"_design/test", +    language: "javascript", +    views: { +      summate: {map:"function (doc) {emit(doc.integer, doc.integer)};", +                reduce:"function (keys, values) { return sum(values); };"}, +    } +  }; +  T(db.save(designDoc).ok); + +  // Test that the reduce works +  var res = db.view('test/summate'); +  T(res.rows.length == 1 && res.rows[0].value == summate(5)); +   +  //Test that we get our docs back +  res = db.view('test/summate', {reduce: false}); +  T(res.rows.length == 5); +  for(var i=0; i<5; i++) +  { +    T(res.rows[i].value == i+1); +  } +}; diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js new file mode 100644 index 00000000..59dc4700 --- /dev/null +++ b/share/www/script/test/replication.js @@ -0,0 +1,207 @@ +// 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.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); +    } + +  } +}; diff --git a/share/www/script/test/security_validation.js b/share/www/script/test/security_validation.js new file mode 100644 index 00000000..61f56b39 --- /dev/null +++ b/share/www/script/test/security_validation.js @@ -0,0 +1,154 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License.  You may obtain a copy +// of the License at +// +//   http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the +// License for the specific language governing permissions and limitations under +// the License. + +couchTests.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); +    }); +}; diff --git a/share/www/script/test/show_documents.js b/share/www/script/test/show_documents.js new file mode 100644 index 00000000..4837c18d --- /dev/null +++ b/share/www/script/test/show_documents.js @@ -0,0 +1,282 @@ +// 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.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('<xml><node foo="bar"/></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('<xml><node/></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/)); +}; diff --git a/share/www/script/test/utf8.js b/share/www/script/test/utf8.js new file mode 100644 index 00000000..5ba29496 --- /dev/null +++ b/share/www/script/test/utf8.js @@ -0,0 +1,41 @@ +// 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.utf8 = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var texts = []; + +  texts[0] = "1. Ascii: hello" +  texts[1] = "2. Russian: На берегу пустынных волн" +  texts[2] = "3. Math: ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i)," +  texts[3] = "4. Geek: STARGΛ̊TE SG-1" +  texts[4] = "5. Braille: ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌" + +  // check that we can save a reload with full fidelity +  for (var i=0; i<texts.length; i++) { +    T(db.save({_id:i.toString(), text:texts[i]}).ok); +  } + +  for (var i=0; i<texts.length; i++) { +    T(db.open(i.toString()).text == texts[i]); +  } + +  // check that views and key collation don't blow up +  var rows = db.query(function(doc) { emit(null, doc.text) }).rows; +  for (var i=0; i<texts.length; i++) { +    T(rows[i].value == texts[i]); +  } +}; diff --git a/share/www/script/test/uuids.js b/share/www/script/test/uuids.js new file mode 100644 index 00000000..6f701884 --- /dev/null +++ b/share/www/script/test/uuids.js @@ -0,0 +1,62 @@ +// 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.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); +}; diff --git a/share/www/script/test/view_collation.js b/share/www/script/test/view_collation.js new file mode 100644 index 00000000..bcb63404 --- /dev/null +++ b/share/www/script/test/view_collation.js @@ -0,0 +1,88 @@ +// 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.view_collation = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  // NOTE, the values are already in their correct sort order. Consider this +  // a specification of collation of json types. + +  var values = []; + +  // special values sort before all other types +  values.push(null); +  values.push(false); +  values.push(true); + +  // then numbers +  values.push(1); +  values.push(2); +  values.push(3.0); +  values.push(4); + +  // then text, case sensitive +  values.push("a"); +  values.push("A"); +  values.push("aa"); +  values.push("b"); +  values.push("B"); +  values.push("ba"); +  values.push("bb"); + +  // then arrays. compared element by element until different. +  // Longer arrays sort after their prefixes +  values.push(["a"]); +  values.push(["b"]); +  values.push(["b","c"]); +  values.push(["b","c", "a"]); +  values.push(["b","d"]); +  values.push(["b","d", "e"]); + +  // then object, compares each key value in the list until different. +  // larger objects sort after their subset objects. +  values.push({a:1}); +  values.push({a:2}); +  values.push({b:1}); +  values.push({b:2}); +  values.push({b:2, a:1}); // Member order does matter for collation. +                           // CouchDB preserves member order +                           // but doesn't require that clients will. +                           // (this test might fail if used with a js engine +                           // that doesn't preserve order) +  values.push({b:2, c:2}); + +  for (var i=0; i<values.length; i++) { +    db.save({_id:(i).toString(), foo:values[i]}); +  } + +  var queryFun = function(doc) { emit(doc.foo, null); }; +  var rows = db.query(queryFun).rows; +  for (i=0; i<values.length; i++) { +    T(equals(rows[i].key, values[i])); +  } + +  // everything has collated correctly. Now to check the descending output +  rows = db.query(queryFun, null, {descending: true}).rows; +  for (i=0; i<values.length; i++) { +    T(equals(rows[i].key, values[values.length - 1 -i])); +  } + +  // now check the key query args +  for (i=1; i<values.length; i++) { +    var queryOptions = {key:values[i]}; +    rows = db.query(queryFun, null, queryOptions).rows; +    T(rows.length == 1 && equals(rows[0].key, values[i])); +  } +}; diff --git a/share/www/script/test/view_conflicts.js b/share/www/script/test/view_conflicts.js new file mode 100644 index 00000000..959471c2 --- /dev/null +++ b/share/www/script/test/view_conflicts.js @@ -0,0 +1,49 @@ +// 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.view_conflicts = function(debug) { +  var dbA = new CouchDB("test_suite_db_a"); +  dbA.deleteDb(); +  dbA.createDb(); +  var dbB = new CouchDB("test_suite_db_b"); +  dbB.deleteDb(); +  dbB.createDb(); +  if (debug) debugger; + +  var docA = {_id: "foo", bar: 42}; +  T(dbA.save(docA).ok); +  CouchDB.replicate(dbA.name, dbB.name); + +  var docB = dbB.open("foo"); +  docB.bar = 43; +  dbB.save(docB); +  docA.bar = 41; +  dbA.save(docA); +  CouchDB.replicate(dbA.name, dbB.name); + +  var doc = dbB.open("foo", {conflicts: true}); +  T(doc._conflicts.length == 1); +  var conflictRev = doc._conflicts[0]; +  if (doc.bar == 41) { // A won +    T(conflictRev == docB._rev); +  } else { // B won +    T(doc.bar == 43); +    T(conflictRev == docA._rev); +  } + +  var results = dbB.query(function(doc) { +    if (doc._conflicts) { +      emit(doc._id, doc._conflicts); +    } +  }); +  T(results.rows[0].value[0] == conflictRev); +}; diff --git a/share/www/script/test/view_errors.js b/share/www/script/test/view_errors.js new file mode 100644 index 00000000..a8a9be7d --- /dev/null +++ b/share/www/script/test/view_errors.js @@ -0,0 +1,43 @@ +// 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.view_errors = 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); + +  // emitting a key value that is undefined should result in that row not +  // being included in the view results +  var results = db.query(function(doc) { +    emit(doc.undef, null); +  }); +  T(results.total_rows == 0); + +  // if a view function throws an exception, its results are not included in +  // the view index, but the view does not itself raise an error +  var results = db.query(function(doc) { +    doc.undef(); // throws an error +  }); +  T(results.total_rows == 0); + +  // if a view function includes an undefined value in the emitted key or +  // value, an error is logged and the result is not included in the view +  // index, and the view itself does not raise an error +  var results = db.query(function(doc) { +    emit([doc._id, doc.undef], null); +  }); +  T(results.total_rows == 0); +}; diff --git a/share/www/script/test/view_include_docs.js b/share/www/script/test/view_include_docs.js new file mode 100644 index 00000000..dd8ebb44 --- /dev/null +++ b/share/www/script/test/view_include_docs.js @@ -0,0 +1,111 @@ +// 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.view_include_docs = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var docs = makeDocs(0, 100); +  T(db.bulkSave(docs).ok); + +  var designDoc = { +    _id:"_design/test", +    language: "javascript", +    views: { +      all_docs: { +        map: "function(doc) { emit(doc.integer, doc.string) }" +      }, +      with_prev: { +        map: "function(doc){if(doc.prev) emit(doc._id,{'_rev':doc.prev}); else emit(doc._id,{'_rev':doc._rev});}" +      }, +      summate: { +        map:"function (doc) {emit(doc.integer, doc.integer)};", +        reduce:"function (keys, values) { return sum(values); };" +      } +    } +  } +  T(db.save(designDoc).ok); + +  var resp = db.view('test/all_docs', {include_docs: true, limit: 2}); +  T(resp.rows.length == 2); +  T(resp.rows[0].id == "0"); +  T(resp.rows[0].doc._id == "0"); +  T(resp.rows[1].id == "1"); +  T(resp.rows[1].doc._id == "1"); + +  resp = db.view('test/all_docs', {include_docs: true}, [29, 74]); +  T(resp.rows.length == 2); +  T(resp.rows[0].doc._id == "29"); +  T(resp.rows[1].doc.integer == 74); + +  resp = db.allDocs({limit: 2, skip: 1, include_docs: true}); +  T(resp.rows.length == 2); +  T(resp.rows[0].doc.integer == 1); +  T(resp.rows[1].doc.integer == 10); + +  resp = db.allDocs({include_docs: true}, ['not_a_doc']); +  T(resp.rows.length == 1); +  T(!resp.rows[0].doc); + +  resp = db.allDocs({include_docs: true}, ["1", "foo"]); +  T(resp.rows.length == 2); +  T(resp.rows[0].doc.integer == 1); +  T(!resp.rows[1].doc); + +  resp = db.allDocs({include_docs: true, limit: 0}); +  T(resp.rows.length == 0); + +  // No reduce support +  try { +      resp = db.view('test/summate', {include_docs: true}); +      alert(JSON.stringify(resp)); +      T(0==1); +  } catch (e) { +      T(e.error == 'query_parse_error'); +  } + +  // Reduce support when reduce=false +  resp = db.view('test/summate', {reduce: false, include_docs: true}); +  T(resp.rows.length == 100); + +  // Check emitted _rev controls things +  resp = db.allDocs({include_docs: true}, ["0"]); +  var before = resp.rows[0].doc; +  var after = db.open("0"); +  after.integer = 100 +  after.prev = after._rev; +  db.save(after); +  after = db.open("0"); +  T(after._rev != after.prev); +  T(after.integer == 100); + +  // should emit the previous revision +  resp = db.view("test/with_prev", {include_docs: true}, ["0"]); +  T(resp.rows[0].doc._id == "0"); +  T(resp.rows[0].doc._rev == before._rev); +  T(!resp.rows[0].doc.prev); +  T(resp.rows[0].doc.integer == 0); + +  var xhr = CouchDB.request("POST", "/test_suite_db/_compact"); +  T(xhr.status == 202) +  while (db.info().compact_running) {} + +  resp = db.view("test/with_prev", {include_docs: true}, ["0", "23"]); +  T(resp.rows.length == 2); +  T(resp.rows[0].key == "0"); +  T(resp.rows[0].id == "0"); +  T(!resp.rows[0].doc); +  T(resp.rows[0].error == "missing"); +  T(resp.rows[1].doc.integer == 23); +}; diff --git a/share/www/script/test/view_multi_key_all_docs.js b/share/www/script/test/view_multi_key_all_docs.js new file mode 100644 index 00000000..f036db11 --- /dev/null +++ b/share/www/script/test/view_multi_key_all_docs.js @@ -0,0 +1,54 @@ +// 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.view_multi_key_all_docs = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var docs = makeDocs(0, 100); +  T(db.bulkSave(docs).ok); + +  var keys = ["10","15","30","37","50"]; +  var rows = db.allDocs({},keys).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]); + +  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]); + +  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]); + +  rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).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); +  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/share/www/script/test/view_multi_key_design.js b/share/www/script/test/view_multi_key_design.js new file mode 100644 index 00000000..2464ff1c --- /dev/null +++ b/share/www/script/test/view_multi_key_design.js @@ -0,0 +1,138 @@ +// 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.view_multi_key_design = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var docs = makeDocs(0, 100); +  T(db.bulkSave(docs).ok); + +  var designDoc = { +    _id:"_design/test", +    language: "javascript", +    views: { +      all_docs: { +        map: "function(doc) { emit(doc.integer, doc.string) }" +      }, +      multi_emit: { +        map: "function(doc) {for(var i = 0 ; i < 3 ; i++) { emit(i, doc.integer) ; } }" +      }, +      summate: { +        map:"function (doc) {emit(doc.integer, doc.integer)};", +        reduce:"function (keys, values) { return sum(values); };" +      } +    } +  } +  T(db.save(designDoc).ok); + +  // Test that missing keys work too +  var keys = [101,30,15,37,50] +  var reduce = db.view("test/summate",{group:true},keys).rows; +  T(reduce.length == keys.length-1); // 101 is missing +  for(var i=0; i<reduce.length; i++) { +    T(keys.indexOf(reduce[i].key) != -1); +    T(reduce[i].key == reduce[i].value); +  } + +  // First, the goods: +  var keys = [10,15,30,37,50]; +  var rows = db.view("test/all_docs",{},keys).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++) { +    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}]; +  for(var i in badargs) +  { +      try { +          db.view("test/all_docs",badargs[i],keys); +          T(0==1); +      } catch (e) { +          T(e.error == "query_parse_error"); +      } +  } + +  try { +      db.view("test/summate",{},keys); +      T(0==1); +  } catch (e) { +      T(e.error == "query_parse_error"); +  } + +  // Test that a map & reduce containing func support keys when reduce=false +  resp = db.view("test/summate", {reduce: false}, keys); +  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; +  var exp_key = [ 0,  0,  0,  2,  2,  2] ; +  var exp_val = [21, 22, 23, 21, 22, 23] ; +  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); + +  // Check offset works +  curr = db.view("test/multi_emit", {skip: 1}, [0]).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); + +  // 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", {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: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).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); +}; diff --git a/share/www/script/test/view_multi_key_temp.js b/share/www/script/test/view_multi_key_temp.js new file mode 100644 index 00000000..28cd42b4 --- /dev/null +++ b/share/www/script/test/view_multi_key_temp.js @@ -0,0 +1,37 @@ +// 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.view_multi_key_temp = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  var docs = makeDocs(0, 100); +  T(db.bulkSave(docs).ok); + +  var queryFun = function(doc) { emit(doc.integer, doc.integer) }; +  var reduceFun = function (keys, values) { return sum(values); }; + +  var keys = [10,15,30,37,50]; +  var rows = db.query(queryFun, null, {}, keys).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.query(queryFun, reduceFun, {group:true}, keys).rows; +  for(var i=0; i<reduce.length; i++) { +    T(keys.indexOf(reduce[i].key) != -1); +    T(reduce[i].key == reduce[i].value); +  } +}; diff --git a/share/www/script/test/view_pagination.js b/share/www/script/test/view_pagination.js new file mode 100644 index 00000000..7d466f19 --- /dev/null +++ b/share/www/script/test/view_pagination.js @@ -0,0 +1,122 @@ +// 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.view_pagination = function(debug) { +    var db = new CouchDB("test_suite_db"); +    db.deleteDb(); +    db.createDb(); +    if (debug) debugger; + +    var docs = makeDocs(0, 100); +    T(db.bulkSave(docs).ok); + +    var queryFun = function(doc) { emit(doc.integer, null) }; +    var i; + +    // page through the view ascending +    for (i = 0; i < docs.length; i += 10) { +      var queryResults = db.query(queryFun, null, { +        startkey: i, +        startkey_docid: i, +        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); +      } +    } + +    // page through the view descending +    for (i = docs.length - 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"); + +  }; diff --git a/share/www/script/test/view_sandboxing.js b/share/www/script/test/view_sandboxing.js new file mode 100644 index 00000000..779b1d9d --- /dev/null +++ b/share/www/script/test/view_sandboxing.js @@ -0,0 +1,52 @@ +// 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.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); +}; diff --git a/share/www/script/test/view_xml.js b/share/www/script/test/view_xml.js new file mode 100644 index 00000000..c7a0472c --- /dev/null +++ b/share/www/script/test/view_xml.js @@ -0,0 +1,39 @@ +// 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.view_xml = function(debug) { +  var db = new CouchDB("test_suite_db"); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  db.save({content: "<doc><title id='xml'>Testing XML</title></doc>"}); +  db.save({content: "<doc><title id='e4x'>Testing E4X</title></doc>"}); + +  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"); +}; diff --git a/test/runner.sh b/test/runner.sh index 561110a8..6715f5f9 100755 --- a/test/runner.sh +++ b/test/runner.sh @@ -4,4 +4,4 @@ erlc runner.erl  erl -noshell -pa ../src/couchdb -pa ../src/mochiweb -eval "runner:run()" -cat ../share/www/script/couch.js ../share/www/script/couch_test_runner.js ../share/www/script/couch_tests.js test.js  | ../src/couchdb/couchjs - +cat ../share/www/script/couch.js ../share/www/script/couch_test_runner.js ../share/www/script/couch_tests.js ../share/www/script/test/* test.js  | ../src/couchdb/couchjs - diff --git a/test/test.js b/test/test.js index a4aa32e1..f3990966 100644 --- a/test/test.js +++ b/test/test.js @@ -194,14 +194,14 @@ var numFailures = 0;  function runAllTestsConsole() {    var numTests = 0;    var debug = false; -  for (var t in tests) { +  for (var t in couchTests) {      p(t);      if (t == "utf8") {        p("We skip the utf8 test because it fails due to problems in couch_js.c");        p("Run the in-browser tests to verify utf8.\n");      } else {        numTests += 1; -      var testFun = tests[t]; +      var testFun = couchTests[t];        runTestConsole(testFun, debug);            }    } | 
