From 59a60a319bead55aa188e8cb1516d5e5146c4492 Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Sun, 15 Feb 2009 23:59:38 +0000 Subject: Reorganize the tests into one file per test. No other changes. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@744782 13f79535-47bb-0310-9956-ffa450edef68 --- share/www/couch_tests.html | 2 +- share/www/script/couch_test_runner.js | 12 +- share/www/script/couch_tests.js | 3366 +--------------------- share/www/script/test/all_docs.js | 100 + share/www/script/test/attachment_paths.js | 147 + share/www/script/test/attachment_views.js | 98 + share/www/script/test/attachments.js | 146 + share/www/script/test/basics.js | 144 + share/www/script/test/bulk_docs.js | 56 + share/www/script/test/compact.js | 56 + share/www/script/test/config.js | 66 + share/www/script/test/conflicts.js | 60 + share/www/script/test/content_negotiation.js | 32 + share/www/script/test/copy_move_doc.js | 65 + share/www/script/test/delayed_commits.js | 93 + share/www/script/test/design_docs.js | 107 + share/www/script/test/design_options.js | 63 + share/www/script/test/design_paths.js | 72 + share/www/script/test/etags_head.js | 78 + share/www/script/test/etags_views.js | 87 + share/www/script/test/invalid_docids.js | 54 + share/www/script/test/large_docs.js | 33 + share/www/script/test/list_views.js | 216 ++ share/www/script/test/lots_of_docs.js | 56 + share/www/script/test/max_dbs_open.js | 41 + share/www/script/test/multiple_rows.js | 80 + share/www/script/test/purge.js | 101 + share/www/script/test/recreate_doc.js | 39 + share/www/script/test/reduce.js | 169 ++ share/www/script/test/reduce_false.js | 45 + share/www/script/test/replication.js | 207 ++ share/www/script/test/security_validation.js | 154 + share/www/script/test/show_documents.js | 282 ++ share/www/script/test/utf8.js | 41 + share/www/script/test/uuids.js | 62 + share/www/script/test/view_collation.js | 88 + share/www/script/test/view_conflicts.js | 49 + share/www/script/test/view_errors.js | 43 + share/www/script/test/view_include_docs.js | 111 + share/www/script/test/view_multi_key_all_docs.js | 54 + share/www/script/test/view_multi_key_design.js | 138 + share/www/script/test/view_multi_key_temp.js | 37 + share/www/script/test/view_pagination.js | 122 + share/www/script/test/view_sandboxing.js | 52 + share/www/script/test/view_xml.js | 39 + 45 files changed, 3836 insertions(+), 3327 deletions(-) create mode 100644 share/www/script/test/all_docs.js create mode 100644 share/www/script/test/attachment_paths.js create mode 100644 share/www/script/test/attachment_views.js create mode 100644 share/www/script/test/attachments.js create mode 100644 share/www/script/test/basics.js create mode 100644 share/www/script/test/bulk_docs.js create mode 100644 share/www/script/test/compact.js create mode 100644 share/www/script/test/config.js create mode 100644 share/www/script/test/conflicts.js create mode 100644 share/www/script/test/content_negotiation.js create mode 100644 share/www/script/test/copy_move_doc.js create mode 100644 share/www/script/test/delayed_commits.js create mode 100644 share/www/script/test/design_docs.js create mode 100644 share/www/script/test/design_options.js create mode 100644 share/www/script/test/design_paths.js create mode 100644 share/www/script/test/etags_head.js create mode 100644 share/www/script/test/etags_views.js create mode 100644 share/www/script/test/invalid_docids.js create mode 100644 share/www/script/test/large_docs.js create mode 100644 share/www/script/test/list_views.js create mode 100644 share/www/script/test/lots_of_docs.js create mode 100644 share/www/script/test/max_dbs_open.js create mode 100644 share/www/script/test/multiple_rows.js create mode 100644 share/www/script/test/purge.js create mode 100644 share/www/script/test/recreate_doc.js create mode 100644 share/www/script/test/reduce.js create mode 100644 share/www/script/test/reduce_false.js create mode 100644 share/www/script/test/replication.js create mode 100644 share/www/script/test/security_validation.js create mode 100644 share/www/script/test/show_documents.js create mode 100644 share/www/script/test/utf8.js create mode 100644 share/www/script/test/uuids.js create mode 100644 share/www/script/test/view_collation.js create mode 100644 share/www/script/test/view_conflicts.js create mode 100644 share/www/script/test/view_errors.js create mode 100644 share/www/script/test/view_include_docs.js create mode 100644 share/www/script/test/view_multi_key_all_docs.js create mode 100644 share/www/script/test/view_multi_key_design.js create mode 100644 share/www/script/test/view_multi_key_temp.js create mode 100644 share/www/script/test/view_pagination.js create mode 100644 share/www/script/test/view_sandboxing.js create mode 100644 share/www/script/test/view_xml.js (limited to 'share') 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")
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(''); +function loadScript(url) { + if (typeof document != "undefined") document.write(''); }; 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; - $("
").text(tests[name].toString()).appendTo(win.document.body).fadeIn();
+  $("
").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 = $("")
       .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 0) {
-        str = str + str;
-      }
-      return str;
-    }
-
-    var designDoc = {
-      _id:"_design/test", // turn off couch.js id escaping?
-      language: "javascript",
-      views: {
-        all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
-        no_docs: {map: "function(doc) {}"},
-        single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"},
-        summate: {map:"function (doc) {emit(doc.integer, doc.integer)};",
-                  reduce:"function (keys, values) { return sum(values); };"},
-        summate2: {map:"function (doc) {emit(doc.integer, doc.integer)};",
-                  reduce:"function (keys, values) { return sum(values); };"},
-        huge_src_and_results: {map: "function(doc) { if (doc._id == \"1\") { emit(\"" + makebigstring(16) + "\", null) }}",
-                  reduce:"function (keys, values) { return \"" + makebigstring(16) + "\"; };"}
-      }
-    }
-    T(db.save(designDoc).ok);
-
-    T(db.bulkSave(makeDocs(1, numDocs + 1)).ok);
-
-    // test that the _all_docs view returns correctly with keys
-    var results = db.allDocs({startkey:"_design", endkey:"_design0"});
-    T(results.rows.length == 1);
-
-    for (var loop = 0; loop < 2; loop++) {
-      var rows = db.view("test/all_docs_twice").rows;
-      for (var i = 0; i < numDocs; i++) {
-        T(rows[2*i].key == i+1);
-        T(rows[(2*i)+1].key == i+1);
-      }
-      T(db.view("test/no_docs").total_rows == 0)
-      T(db.view("test/single_doc").total_rows == 1)
-      T(db.ensureFullCommit().ok);
-      restartServer();
-    };
-    
-    // test when language not specified, Javascript is implied
-    var designDoc2 = {
-      _id:"_design/test2",
-      // language: "javascript", 
-      views: {
-        single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}
-      }
-    };
-    
-    T(db.save(designDoc2).ok);
-    T(db.view("test2/single_doc").total_rows == 1);
-
-    var summate = function(N) {return (N+1)*N/2;};
-    var result = db.view("test/summate");
-    T(result.rows[0].value == summate(numDocs));
-
-    result = db.view("test/summate", {startkey:4,endkey:4});
-    T(result.rows[0].value == 4);
-
-    result = db.view("test/summate", {startkey:4,endkey:5});
-    T(result.rows[0].value == 9);
-
-    result = db.view("test/summate", {startkey:4,endkey:6});
-    T(result.rows[0].value == 15);
-
-    // Verify that a shared index (view def is an exact copy of "summate")
-    // does not confuse the reduce stage
-    result = db.view("test/summate2", {startkey:4,endkey:6});
-    T(result.rows[0].value == 15);
-
-    for(var i=1; i= 0; i -= 10) {
-      var queryResults = db.query(queryFun, null, {
-        startkey: i,
-        startkey_docid: i,
-        descending: true,
-        limit: 10
-      });
-      T(queryResults.rows.length == 10)
-      T(queryResults.total_rows == docs.length)
-      T(queryResults.offset == docs.length - i - 1)
-      var j;
-      for (j = 0; j < 10; j++) {
-        T(queryResults.rows[j].key == i - j);
-      }
-    }
-
-    // ignore decending=false. CouchDB should just ignore that.
-    for (i = 0; i < docs.length; i += 10) {
-      var queryResults = db.query(queryFun, null, {
-        startkey: i,
-        startkey_docid: i,
-        descending: false,
-        limit: 10
-      });
-      T(queryResults.rows.length == 10)
-      T(queryResults.total_rows == docs.length)
-      T(queryResults.offset == i)
-      var j;
-      for (j = 0; j < 10;j++) {
-        T(queryResults.rows[j].key == i + j);
-      }
-    }
-    
-    // test endkey_docid
-    var queryResults = db.query(function(doc) { emit(null, null);}, null, {
-      startkey: null,
-      startkey_docid: 1,
-      endkey: null,
-      endkey_docid: 40
-    });
-    
-    T(queryResults.rows.length == 35)
-    T(queryResults.total_rows == docs.length)
-    T(queryResults.offset == 1)
-    T(queryResults.rows[0].id == "1");
-    T(queryResults.rows[1].id == "10");
-    T(queryResults.rows[2].id == "11");
-    T(queryResults.rows[3].id == "12");
-    T(queryResults.rows[4].id == "13");
-    T(queryResults.rows[5].id == "14");
-    T(queryResults.rows[6].id == "15");
-    T(queryResults.rows[7].id == "16");
-    T(queryResults.rows[8].id == "17");
-    T(queryResults.rows[9].id == "18");
-    T(queryResults.rows[10].id == "19");
-    T(queryResults.rows[11].id == "2");
-    T(queryResults.rows[12].id == "20");
-    T(queryResults.rows[13].id == "21");
-    T(queryResults.rows[14].id == "22");
-    T(queryResults.rows[15].id == "23");
-    T(queryResults.rows[16].id == "24");
-    T(queryResults.rows[17].id == "25");
-    T(queryResults.rows[18].id == "26");
-    T(queryResults.rows[19].id == "27");
-    T(queryResults.rows[20].id == "28");
-    T(queryResults.rows[21].id == "29");
-    T(queryResults.rows[22].id == "3");
-    T(queryResults.rows[23].id == "30");
-    T(queryResults.rows[24].id == "31");
-    T(queryResults.rows[25].id == "32");
-    T(queryResults.rows[26].id == "33");
-    T(queryResults.rows[27].id == "34");
-    T(queryResults.rows[28].id == "35");
-    T(queryResults.rows[29].id == "36");
-    T(queryResults.rows[30].id == "37");
-    T(queryResults.rows[31].id == "38");
-    T(queryResults.rows[32].id == "39");
-    T(queryResults.rows[33].id == "4");
-    T(queryResults.rows[34].id == "40");
-
-  },
-
-  view_sandboxing: function(debug) {
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-
-    var doc = {integer: 1, string: "1", array: [1, 2, 3]};
-    T(db.save(doc).ok);
-/*
-    // make sure that attempting to change the document throws an error
-    var results = db.query(function(doc) {
-      doc.integer = 2;
-      emit(null, doc);
-    });
-    T(results.total_rows == 0);
-
-    var results = db.query(function(doc) {
-      doc.array[0] = 0;
-      emit(null, doc);
-    });
-    T(results.total_rows == 0);
-*/
-    // make sure that a view cannot invoke interpreter internals such as the
-    // garbage collector
-    var results = db.query(function(doc) {
-      gc();
-      emit(null, doc);
-    });
-    T(results.total_rows == 0);
-
-    // make sure that a view cannot access the map_funs array defined used by
-    // the view server
-    var results = db.query(function(doc) { map_funs.push(1); emit(null, doc) });
-    T(results.total_rows == 0);
-
-    // make sure that a view cannot access the map_results array defined used by
-    // the view server
-    var results = db.query(function(doc) { map_results.push(1); emit(null, doc) });
-    T(results.total_rows == 0);
-  },
-
-  view_xml: function(debug) {
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-
-    db.save({content: "Testing XML"});
-    db.save({content: "Testing E4X"});
-
-    var results = db.query(
-      "function(doc) {\n" +
-      "  var xml = new XML(doc.content);\n" +
-      "  emit(xml.title.text(), null);\n" +
-      "}");
-    T(results.total_rows == 2);
-    T(results.rows[0].key == "Testing E4X");
-    T(results.rows[1].key == "Testing XML");
-
-    var results = db.query(
-      "function(doc) {\n" +
-      "  var xml = new XML(doc.content);\n" +
-      "  emit(xml.title.@id, null);\n" +
-      "}");
-    T(results.total_rows == 2);
-    T(results.rows[0].key == "e4x");
-    T(results.rows[1].key == "xml");
-  },
-
-  replication: function(debug) {
-    if (debug) debugger;
-    var host = CouchDB.host;
-    var dbPairs = [
-      {source:"test_suite_db_a",
-        target:"test_suite_db_b"},
-      {source:"test_suite_db_a",
-        target:"http://" + host + "/test_suite_db_b"},
-      {source:"http://" + host + "/test_suite_db_a",
-        target:"test_suite_db_b"},
-      {source:"http://" + host + "/test_suite_db_a",
-        target:"http://" + host + "/test_suite_db_b"}
-    ]
-    var dbA = new CouchDB("test_suite_db_a");
-    var dbB = new CouchDB("test_suite_db_b");
-    var numDocs = 10;
-    var xhr;
-    for (var testPair = 0; testPair < dbPairs.length; testPair++) {
-      var A = dbPairs[testPair].source
-      var B = dbPairs[testPair].target
-
-      dbA.deleteDb();
-      dbA.createDb();
-      dbB.deleteDb();
-      dbB.createDb();
-      
-      var repTests = {
-        // copy and paste and put your code in. delete unused steps.
-        test_template: new function () {
-          this.init = function(dbA, dbB) {
-            // before anything has happened
-          }
-          this.afterAB1 = function(dbA, dbB) {
-            // called after replicating src=A  tgt=B first time.
-          };
-          this.afterBA1 = function(dbA, dbB) {
-            // called after replicating src=B  tgt=A first time.
-          };
-          this.afterAB2 = function(dbA, dbB) {
-            // called after replicating src=A  tgt=B second time. 
-          };
-          this.afterBA2 = function(dbA, dbB) {
-            // etc...
-          };
-        },
-        
-        simple_test: new function () {
-          this.init = function(dbA, dbB) {
-            var docs = makeDocs(0, numDocs);
-            T(dbA.bulkSave(docs).ok);
-          };
-        
-          this.afterAB1 = function(dbA, dbB) {          
-            for (var j = 0; j < numDocs; j++) {
-              var docA = dbA.open("" + j);
-              var docB = dbB.open("" + j);
-              T(docA._rev == docB._rev);
-            }
-          };
-        },
-      
-       deletes_test: new function () {
-          this.init = function(dbA, dbB) {
-            T(dbA.save({_id:"foo1",value:"a"}).ok);
-          };
-          
-          this.afterAB1 = function(dbA, dbB) {
-            var docA = dbA.open("foo1");
-            var docB = dbB.open("foo1");
-            T(docA._rev == docB._rev);
-
-            dbA.deleteDoc(docA);
-          };
-          
-          this.afterAB2 = function(dbA, dbB) {
-            T(dbA.open("foo1") == null);
-            T(dbB.open("foo1") == null);
-          };
-        },
-        
-        slashes_in_ids_test: new function () {
-          // make sure docs with slashes in id replicate properly
-          this.init = function(dbA, dbB) {
-            dbA.save({ _id:"abc/def", val:"one" });
-          };
-          
-          this.afterAB1 = function(dbA, dbB) {
-            var docA = dbA.open("abc/def");
-            var docB = dbB.open("abc/def");
-            T(docA._rev == docB._rev);
-          };
-        },
-
-        design_docs_test: new function() {
-          // make sure design docs replicate properly
-          this.init = function(dbA, dbB) {
-            dbA.save({ _id:"_design/test" });
-          };
-
-          this.afterAB1 = function() {
-            var docA = dbA.open("_design/test");
-            var docB = dbB.open("_design/test");
-            T(docA._rev == docB._rev);
-          };
-        },
-      
-        attachments_test: new function () {
-          // Test attachments
-          this.init = function(dbA, dbB) {
-            dbA.save({
-              _id:"bin_doc",
-              _attachments:{
-                "foo.txt": {
-                  "type":"base64",
-                  "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
-                }
-              }
-            });
-          };
-          
-          this.afterAB1 = function(dbA, dbB) {
-            var xhr = CouchDB.request("GET", "/test_suite_db_a/bin_doc/foo.txt");
-            T(xhr.responseText == "This is a base64 encoded text")
-
-            xhr = CouchDB.request("GET", "/test_suite_db_b/bin_doc/foo.txt");
-            T(xhr.responseText == "This is a base64 encoded text")
-          };
-        },
-        
-        conflicts_test: new function () {
-          // test conflicts
-          this.init = function(dbA, dbB) {
-            dbA.save({_id:"foo",value:"a"});
-            dbB.save({_id:"foo",value:"b"});
-          };
-          
-          this.afterBA1 = function(dbA, dbB) {            
-            var docA = dbA.open("foo", {conflicts: true});
-            var docB = dbB.open("foo", {conflicts: true});
-
-            // make sure the same rev is in each db
-            T(docA._rev === docB._rev);
-
-            // make sure the conflicts are the same in each db
-            T(docA._conflicts[0] === docB._conflicts[0]);
-
-            // delete a conflict.
-            dbA.deleteDoc({_id:"foo", _rev:docA._conflicts[0]});
-          };
-          
-          this.afterBA2 = function(dbA, dbB) {            
-            // open documents and include the conflict meta data
-            var docA = dbA.open("foo", {conflicts: true});
-            var docB = dbB.open("foo", {conflicts: true});
-
-            // We should have no conflicts this time
-            T(docA._conflicts === undefined)
-            T(docB._conflicts === undefined);
-          };
-        }
-      };
-
-      var test;
-      for(test in repTests) {
-        if(repTests[test].init) {
-          repTests[test].init(dbA, dbB);
-        }
-      }
-
-      T(CouchDB.replicate(A, B).ok);
-
-      for(test in repTests) {
-        if(repTests[test].afterAB1) repTests[test].afterAB1(dbA, dbB);
-      }
-
-      T(CouchDB.replicate(B, A).ok);
-
-      for(test in repTests) {
-        if(repTests[test].afterBA1) repTests[test].afterBA1(dbA, dbB);
-      }
-
-      T(CouchDB.replicate(A, B).ok);
-
-      for(test in repTests) {
-        if(repTests[test].afterAB2) repTests[test].afterAB2(dbA, dbB);
-      }
-
-      T(CouchDB.replicate(B, A).ok);
-
-      for(test in repTests) {
-        if(repTests[test].afterBA2) repTests[test].afterBA2(dbA, dbB);
-      }
-
-    }
-  },
-
-  etags_head: function(debug) {
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-
-    var xhr;
-
-    // create a new doc
-    xhr = CouchDB.request("PUT", "/test_suite_db/1", {
-      body: "{}"
-    });
-    T(xhr.status == 201);
-
-    // extract the ETag header values
-    var etag = xhr.getResponseHeader("etag");
-
-    // get the doc and verify the headers match
-    xhr = CouchDB.request("GET", "/test_suite_db/1");
-    T(etag == xhr.getResponseHeader("etag"));
-
-    // 'head' the doc and verify the headers match
-    xhr = CouchDB.request("HEAD", "/test_suite_db/1", {
-      headers: {"if-none-match": "s"}
-    });
-    T(etag == xhr.getResponseHeader("etag"));
-
-    // replace a doc
-    xhr = CouchDB.request("PUT", "/test_suite_db/1", {
-      body: "{}",
-      headers: {"if-match": etag}
-    });
-    T(xhr.status == 201);
-
-    // extract the new ETag value
-    var etagOld= etag;
-    etag = xhr.getResponseHeader("etag");
-
-    // fail to replace a doc
-    xhr = CouchDB.request("PUT", "/test_suite_db/1", {
-      body: "{}"
-    });
-    T(xhr.status == 409);
-
-    // verify get w/Etag
-    xhr = CouchDB.request("GET", "/test_suite_db/1", {
-      headers: {"if-none-match": etagOld}
-    });
-    T(xhr.status == 200);
-    xhr = CouchDB.request("GET", "/test_suite_db/1", {
-      headers: {"if-none-match": etag}
-    });
-    T(xhr.status == 304);
-
-    // fail to delete a doc
-    xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
-      headers: {"if-match": etagOld}
-    });
-    T(xhr.status == 409);
-
-    //now do it for real
-    xhr = CouchDB.request("DELETE", "/test_suite_db/1", {
-      headers: {"if-match": etag}
-    });
-    T(xhr.status == 200);
-  },
-  etags_views: function(debug) {
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-    
-    var designDoc = {
-      _id:"_design/etags",
-      language: "javascript",
-      views : {
-        basicView : {
-          map : stringFun(function(doc) {
-            emit(doc.integer, doc.string);
-          })
-        },
-        withReduce : {
-          map : stringFun(function(doc) {
-            emit(doc.integer, doc.string);
-          }),
-          reduce : stringFun(function(keys, values, rereduce) {
-            if (rereduce) {
-              return sum(values);
-            } else {
-              return values.length;
-            }
-          })
-        }
-      }
-    }
-    T(db.save(designDoc).ok);
-    var xhr;
-    var docs = makeDocs(0, 10);
-    var saveResult = db.bulkSave(docs);
-    T(saveResult.ok);
-    
-    // verify get w/Etag on map view
-    xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView");
-    T(xhr.status == 200);
-    var etag = xhr.getResponseHeader("etag");
-    xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView", {
-      headers: {"if-none-match": etag}
-    });
-    T(xhr.status == 304);
-    // TODO GET with keys (when that is available)
-    
-    // reduce view
-    xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce");
-    T(xhr.status == 200);
-    var etag = xhr.getResponseHeader("etag");
-    xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/withReduce", {
-      headers: {"if-none-match": etag}
-    });
-    T(xhr.status == 304);
-    
-    // all docs
-    xhr = CouchDB.request("GET", "/test_suite_db/_all_docs");
-    T(xhr.status == 200);
-    var etag = xhr.getResponseHeader("etag");
-    xhr = CouchDB.request("GET", "/test_suite_db/_all_docs", {
-      headers: {"if-none-match": etag}
-    });
-    T(xhr.status == 304);
-
-    // by seq
-    xhr = CouchDB.request("GET", "/test_suite_db/_all_docs_by_seq");
-    T(xhr.status == 200);
-    var etag = xhr.getResponseHeader("etag");
-    xhr = CouchDB.request("GET", "/test_suite_db/_all_docs_by_seq", {
-      headers: {"if-none-match": etag}
-    });
-    T(xhr.status == 304);    
-
-    // list etag
-    // in the list test for now
-  },
-
-   show_documents: function(debug) {
-     var db = new CouchDB("test_suite_db");
-     db.deleteDb();
-     db.createDb();
-     if (debug) debugger;
-         
-     var designDoc = {
-       _id:"_design/template",
-       language: "javascript",
-       shows: {
-         "hello" : stringFun(function(doc) { 
-           if (doc) {
-             return "Hello World";             
-           } else {
-             return "Empty World";
-           }
-         }),
-         "just-name" : stringFun(function(doc, req) {
-           return {
-             body : "Just " + doc.name
-           };
-         }),
-         "req-info" : stringFun(function(doc, req) {
-           return {
-             json : req
-           }
-         }),
-         "xml-type" : stringFun(function(doc, req) {
-            return {
-              "headers" : {
-                "Content-Type" : "application/xml"
-              },
-              "body" : new XML('')
-            }
-          }),
-         "no-set-etag" : stringFun(function(doc, req) {
-           return {
-             headers : {
-               "Etag" : "skipped"
-             },
-             "body" : "something"
-           }
-         }),
-         "accept-switch" : stringFun(function(doc, req) {
-           if (req.headers["Accept"].match(/image/)) {
-             return {
-               // a 16x16 px version of the CouchDB logo
-               "base64" : 
-["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV",
-"BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/",
-"AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7",
-"/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6",
-"wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA",
-"AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5",
-"zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx",
-"vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT",
-"LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''),
-               headers : {
-                 "Content-Type" : "image/png",
-                 "Vary" : "Accept" // we set this for proxy caches
-               }
-             };
-           } else {
-             return {
-               "body" : "accepting text requests",
-               headers : {
-                 "Content-Type" : "text/html",
-                 "Vary" : "Accept"
-               }
-             };
-           }
-         }),
-         "respondWith" : stringFun(function(doc, req) {
-           registerType("foo", "application/foo","application/x-foo");
-           return respondWith(req, {
-             html : function() {
-               return {
-                 body:"Ha ha, you said \"" + doc.word + "\"."
-               };
-             },
-             xml : function() {
-               var xml = new XML('');
-               // Becase Safari can't stand to see that dastardly
-               // E4X outside of a string. Outside of tests you
-               // can just use E4X literals.
-               this.eval('xml.node.@foo = doc.word');
-               return {
-                 body: xml
-               };
-             },
-             foo : function() {
-               return {
-                 body: "foofoo"
-               };
-             },
-             fallback : "html"
-           });
-         })
-       }
-     };
-     T(db.save(designDoc).ok);
-     
-     var doc = {"word":"plankton", "name":"Rusty"}
-     var resp = db.save(doc);
-     T(resp.ok);
-     var docid = resp.id;
- 
-     // show error
-     var xhr = CouchDB.request("GET", "/test_suite_db/_show/");
-     T(xhr.status == 404);
-     T(JSON.parse(xhr.responseText).reason == "Invalid path.");
- 
-     // hello template world
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/"+docid);
-     T(xhr.responseText == "Hello World");
-
-     // hello template world (no docid)
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello");
-     T(xhr.responseText == "Empty World");
-
-     
-     // show with doc
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
-     T(xhr.responseText == "Just Rusty");
-     
-     // show with missing doc
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/missingdoc");
-     T(xhr.status == 404);
-     var resp = JSON.parse(xhr.responseText);
-     T(resp.error == "not_found");
-     T(resp.reason == "missing");
-     
-     // show with missing func
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid);
-     T(xhr.status == 404);
-     
-     // missing design doc
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/missingdoc/just-name/"+docid);
-     T(xhr.status == 404);
-     var resp = JSON.parse(xhr.responseText);
-     T(resp.error == "not_found");
-     
-     // query parameters
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/req-info/"+docid+"?foo=bar", {
-       headers: {
-         "Accept": "text/html;text/plain;*/*",
-         "X-Foo" : "bar"
-       }
-     });
-     var resp = JSON.parse(xhr.responseText);
-     T(equals(resp.headers["X-Foo"], "bar"));
-     T(equals(resp.query, {foo:"bar"}));
-     T(equals(resp.verb, "GET"));
-     T(equals(resp.path[4], docid));
-     T(equals(resp.info.db_name, "test_suite_db"));
-     
-     // returning a content-type
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/xml-type/"+docid);
-     T("application/xml" == xhr.getResponseHeader("Content-Type"));
-     T("Accept" == xhr.getResponseHeader("Vary"));
- 
-     // accept header switching
-     // different mime has different etag
-     
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
-       headers: {"Accept": "text/html;text/plain;*/*"}
-     });
-     T("text/html" == xhr.getResponseHeader("Content-Type"));
-     T("Accept" == xhr.getResponseHeader("Vary"));
-     var etag = xhr.getResponseHeader("etag");
-
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
-       headers: {"Accept": "image/png;*/*"}
-     });
-     T(xhr.responseText.match(/PNG/))
-     T("image/png" == xhr.getResponseHeader("Content-Type"));
-     var etag2 = xhr.getResponseHeader("etag");
-     T(etag2 != etag);
- 
-     // proper etags
-     // show with doc
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
-     // extract the ETag header values
-     etag = xhr.getResponseHeader("etag");
-     // get again with etag in request
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
-       headers: {"if-none-match": etag}
-     });
-     // should be 304
-     T(xhr.status == 304);    
- 
-     // update the doc
-     doc.name = "Crusty";
-     resp = db.save(doc);
-     T(resp.ok);
-     // req with same etag
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
-       headers: {"if-none-match": etag}
-     });
-     // status is 200    
-     T(xhr.status == 200);
- 
-     // get new etag and request again
-     etag = xhr.getResponseHeader("etag");
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
-       headers: {"if-none-match": etag}
-     });
-     // should be 304
-     T(xhr.status == 304);
- 
-     // update design doc (but not function)
-     designDoc.isChanged = true;
-     T(db.save(designDoc).ok);
-     
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
-       headers: {"if-none-match": etag}
-     });
-     // should be 304
-     T(xhr.status == 304);
-     
-     // update design doc function
-     designDoc.shows["just-name"] = (function(doc, req) {
-       return {
-         body : "Just old " + doc.name
-       };
-     }).toString();
-     T(db.save(designDoc).ok);
- 
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
-       headers: {"if-none-match": etag}
-     });
-     // status is 200    
-     T(xhr.status == 200);
-     
-     
-     // JS can't set etag
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/no-set-etag/"+docid);
-     // extract the ETag header values
-     etag = xhr.getResponseHeader("etag");
-     T(etag != "skipped")
- 
-     // test the respondWith mime matcher
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
-       headers: {
-         "Accept": 'text/html,application/atom+xml; q=0.9'
-       }
-     });
-     T(xhr.getResponseHeader("Content-Type") == "text/html");
-     T(xhr.responseText == "Ha ha, you said \"plankton\".");
- 
-     // now with xml
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
-       headers: {
-         "Accept": 'application/xml'
-       }
-     });
-     T(xhr.getResponseHeader("Content-Type") == "application/xml");
-     T(xhr.responseText.match(/node/));
-     T(xhr.responseText.match(/plankton/));
-     
-     // registering types works
-     xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
-       headers: {
-         "Accept": "application/x-foo"
-       }
-     });
-     T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
-     T(xhr.responseText.match(/foofoo/));
-   },
-
-  list_views : function(debug) {
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-        
-    var designDoc = {
-      _id:"_design/lists",
-      language: "javascript",
-      views : {
-        basicView : {
-          map : stringFun(function(doc) {
-            emit(doc.integer, doc.string);
-          })
-        },
-        withReduce : {
-          map : stringFun(function(doc) {
-            emit(doc.integer, doc.string);
-          }),
-          reduce : stringFun(function(keys, values, rereduce) {
-            if (rereduce) {
-              return sum(values);
-            } else {
-              return values.length;
-            }
-          })
-        }
-      },
-      lists: {
-        simpleForm: stringFun(function(head, row, req, row_info) {
-          if (row) {
-            // we ignore headers on rows and tail
-            return {
-                    body : '\n
  • Key: '+row.key - +' Value: '+row.value - +' LineNo: '+row_info.row_number+'
  • ' - }; - } else if (head) { - // we return an object (like those used by external and show) - // so that we can specify headers - return { - body : '

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

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

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

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

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

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

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

    '}; + } + }), + acceptSwitch: stringFun(function(head, row, req, row_info) { + return respondWith(req, { + html : function() { + // If you're outputting text and you're not setting + // any headers, you can just return a string. + if (head) { + return "HTML
      "; + } else if (row) { + return '\n
    • Key: ' + +row.key+' Value: '+row.value + +' LineNo: '+row_info.row_number+'
    • '; + } else { // tail + return '
    '; + + } + }, + xml : function() { + if (head) { + return '' + +'Test XML Feed'; + } else if (row) { + // Becase Safari can't stand to see that dastardly + // E4X outside of a string. Outside of tests you + // can just use E4X literals. + var entry = new XML(''); + entry.id = row.id; + entry.title = row.key; + entry.content = row.value; + // We'll also let you return just an E4X object + // if you aren't setting headers. + return entry; + } else { + return ""; + } + } + }) + }), + qsParams: stringFun(function(head, row, req, row_info) { + if(head) return {body: req.query.foo}; + else return {body: "\n"}; + }), + stopIter: stringFun(function(head, row, req, row_info) { + if(head) { + return {body: "head"}; + } else if(row) { + if(row_info.row_number > 2) return {stop: true}; + return {body: " " + row_info.row_number}; + } else { + return {body: " tail"}; + } + }), + stopIter2: stringFun(function(head, row, req, row_info) { + return respondWith(req, { + html: function() { + if(head) { + return "head"; + } else if(row) { + if(row_info.row_number > 2) return {stop: true}; + return " " + row_info.row_number; + } else { + return " tail"; + } + } + }); + }) + } + }; + + T(db.save(designDoc).ok); + + var docs = makeDocs(0, 10); + var saveResult = db.bulkSave(docs); + T(saveResult.ok); + + var view = db.view('lists/basicView'); + T(view.total_rows == 10); + + // standard get + var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView"); + T(xhr.status == 200); + T(/Total Rows/.test(xhr.responseText)); + T(/Key: 1/.test(xhr.responseText)); + T(/LineNo: 0/.test(xhr.responseText)); + T(/LineNo: 5/.test(xhr.responseText)); + T(/FirstKey: 0/.test(xhr.responseText)); + T(/LastKey: 9/.test(xhr.responseText)); + + + var lines = xhr.responseText.split('\n'); + T(/LineNo: 5/.test(lines[6])); + + // test that etags are available + var etag = xhr.getResponseHeader("etag"); + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView", { + headers: {"if-none-match": etag} + }); + T(xhr.status == 304); + + // get with query params + var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=3"); + T(xhr.status == 200); + T(/Total Rows/.test(xhr.responseText)); + T(!(/Key: 1/.test(xhr.responseText))); + T(/FirstKey: 3/.test(xhr.responseText)); + T(/LastKey: 9/.test(xhr.responseText)); + + + // with 0 rows + var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/basicView?startkey=30"); + T(xhr.status == 200); + T(/Total Rows/.test(xhr.responseText)); + T(/Offset: null/.test(xhr.responseText)); + + // when there is a reduce present, but not used + var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?reduce=false"); + T(xhr.status == 200); + T(/Total Rows/.test(xhr.responseText)); + T(/Key: 1/.test(xhr.responseText)); + + // with accept headers for HTML + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", { + headers: { + "Accept": 'text/html' + } + }); + T(xhr.getResponseHeader("Content-Type") == "text/html"); + T(xhr.responseText.match(/HTML/)); + T(xhr.responseText.match(/Value/)); + + // now with xml + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", { + headers: { + "Accept": 'application/xml' + } + }); + T(xhr.getResponseHeader("Content-Type") == "application/xml"); + T(xhr.responseText.match(/XML/)); + T(xhr.responseText.match(/entry/)); + + // now with extra qs params + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/qsParams/basicView?foo=blam"); + T(xhr.responseText.match(/blam/)); + + + // aborting iteration + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter/basicView"); + T(xhr.responseText.match(/^head 0 1 2 tail$/)); + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/stopIter2/basicView"); + T(xhr.responseText.match(/^head 0 1 2 tail$/)); + +}; 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') + } + }), + "no-set-etag" : stringFun(function(doc, req) { + return { + headers : { + "Etag" : "skipped" + }, + "body" : "something" + } + }), + "accept-switch" : stringFun(function(doc, req) { + if (req.headers["Accept"].match(/image/)) { + return { + // a 16x16 px version of the CouchDB logo + "base64" : +["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV", +"BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/", +"AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7", +"/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6", +"wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA", +"AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5", +"zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx", +"vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT", +"LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''), + headers : { + "Content-Type" : "image/png", + "Vary" : "Accept" // we set this for proxy caches + } + }; + } else { + return { + "body" : "accepting text requests", + headers : { + "Content-Type" : "text/html", + "Vary" : "Accept" + } + }; + } + }), + "respondWith" : stringFun(function(doc, req) { + registerType("foo", "application/foo","application/x-foo"); + return respondWith(req, { + html : function() { + return { + body:"Ha ha, you said \"" + doc.word + "\"." + }; + }, + xml : function() { + var xml = new XML(''); + // Becase Safari can't stand to see that dastardly + // E4X outside of a string. Outside of tests you + // can just use E4X literals. + this.eval('xml.node.@foo = doc.word'); + return { + body: xml + }; + }, + foo : function() { + return { + body: "foofoo" + }; + }, + fallback : "html" + }); + }) + } + }; + T(db.save(designDoc).ok); + + var doc = {"word":"plankton", "name":"Rusty"} + var resp = db.save(doc); + T(resp.ok); + var docid = resp.id; + + // show error + var xhr = CouchDB.request("GET", "/test_suite_db/_show/"); + T(xhr.status == 404); + T(JSON.parse(xhr.responseText).reason == "Invalid path."); + + // hello template world + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/"+docid); + T(xhr.responseText == "Hello World"); + + // hello template world (no docid) + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello"); + T(xhr.responseText == "Empty World"); + + + // show with doc + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid); + T(xhr.responseText == "Just Rusty"); + + // show with missing doc + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/missingdoc"); + T(xhr.status == 404); + var resp = JSON.parse(xhr.responseText); + T(resp.error == "not_found"); + T(resp.reason == "missing"); + + // show with missing func + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid); + T(xhr.status == 404); + + // missing design doc + xhr = CouchDB.request("GET", "/test_suite_db/_show/missingdoc/just-name/"+docid); + T(xhr.status == 404); + var resp = JSON.parse(xhr.responseText); + T(resp.error == "not_found"); + + // query parameters + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/req-info/"+docid+"?foo=bar", { + headers: { + "Accept": "text/html;text/plain;*/*", + "X-Foo" : "bar" + } + }); + var resp = JSON.parse(xhr.responseText); + T(equals(resp.headers["X-Foo"], "bar")); + T(equals(resp.query, {foo:"bar"})); + T(equals(resp.verb, "GET")); + T(equals(resp.path[4], docid)); + T(equals(resp.info.db_name, "test_suite_db")); + + // returning a content-type + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/xml-type/"+docid); + T("application/xml" == xhr.getResponseHeader("Content-Type")); + T("Accept" == xhr.getResponseHeader("Vary")); + + // accept header switching + // different mime has different etag + + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, { + headers: {"Accept": "text/html;text/plain;*/*"} + }); + T("text/html" == xhr.getResponseHeader("Content-Type")); + T("Accept" == xhr.getResponseHeader("Vary")); + var etag = xhr.getResponseHeader("etag"); + + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, { + headers: {"Accept": "image/png;*/*"} + }); + T(xhr.responseText.match(/PNG/)) + T("image/png" == xhr.getResponseHeader("Content-Type")); + var etag2 = xhr.getResponseHeader("etag"); + T(etag2 != etag); + + // proper etags + // show with doc + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid); + // extract the ETag header values + etag = xhr.getResponseHeader("etag"); + // get again with etag in request + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { + headers: {"if-none-match": etag} + }); + // should be 304 + T(xhr.status == 304); + + // update the doc + doc.name = "Crusty"; + resp = db.save(doc); + T(resp.ok); + // req with same etag + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { + headers: {"if-none-match": etag} + }); + // status is 200 + T(xhr.status == 200); + + // get new etag and request again + etag = xhr.getResponseHeader("etag"); + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { + headers: {"if-none-match": etag} + }); + // should be 304 + T(xhr.status == 304); + + // update design doc (but not function) + designDoc.isChanged = true; + T(db.save(designDoc).ok); + + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { + headers: {"if-none-match": etag} + }); + // should be 304 + T(xhr.status == 304); + + // update design doc function + designDoc.shows["just-name"] = (function(doc, req) { + return { + body : "Just old " + doc.name + }; + }).toString(); + T(db.save(designDoc).ok); + + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, { + headers: {"if-none-match": etag} + }); + // status is 200 + T(xhr.status == 200); + + + // JS can't set etag + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/no-set-etag/"+docid); + // extract the ETag header values + etag = xhr.getResponseHeader("etag"); + T(etag != "skipped") + + // test the respondWith mime matcher + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { + headers: { + "Accept": 'text/html,application/atom+xml; q=0.9' + } + }); + T(xhr.getResponseHeader("Content-Type") == "text/html"); + T(xhr.responseText == "Ha ha, you said \"plankton\"."); + + // now with xml + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { + headers: { + "Accept": 'application/xml' + } + }); + T(xhr.getResponseHeader("Content-Type") == "application/xml"); + T(xhr.responseText.match(/node/)); + T(xhr.responseText.match(/plankton/)); + + // registering types works + xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, { + headers: { + "Accept": "application/x-foo" + } + }); + T(xhr.getResponseHeader("Content-Type") == "application/x-foo"); + T(xhr.responseText.match(/foofoo/)); +}; 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= 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: "Testing XML"}); + db.save({content: "Testing E4X"}); + + var results = db.query( + "function(doc) {\n" + + " var xml = new XML(doc.content);\n" + + " emit(xml.title.text(), null);\n" + + "}"); + T(results.total_rows == 2); + T(results.rows[0].key == "Testing E4X"); + T(results.rows[1].key == "Testing XML"); + + var results = db.query( + "function(doc) {\n" + + " var xml = new XML(doc.content);\n" + + " emit(xml.title.@id, null);\n" + + "}"); + T(results.total_rows == 2); + T(results.rows[0].key == "e4x"); + T(results.rows[1].key == "xml"); +}; -- cgit v1.2.3