You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by jc...@apache.org on 2009/02/16 00:59:39 UTC

svn commit: r744782 [4/4] - in /couchdb/trunk: share/www/ share/www/script/ share/www/script/test/ test/

Added: couchdb/trunk/share/www/script/test/reduce_false.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/reduce_false.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/reduce_false.js (added)
+++ couchdb/trunk/share/www/script/test/reduce_false.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,45 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.reduce_false = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var numDocs = 5;
+  var docs = makeDocs(1,numDocs + 1);
+  T(db.bulkSave(docs).ok);
+  var summate = function(N) {return (N+1)*N/2;};
+
+  var designDoc = {
+    _id:"_design/test",
+    language: "javascript",
+    views: {
+      summate: {map:"function (doc) {emit(doc.integer, doc.integer)};",
+                reduce:"function (keys, values) { return sum(values); };"},
+    }
+  };
+  T(db.save(designDoc).ok);
+
+  // Test that the reduce works
+  var res = db.view('test/summate');
+  T(res.rows.length == 1 && res.rows[0].value == summate(5));
+  
+  //Test that we get our docs back
+  res = db.view('test/summate', {reduce: false});
+  T(res.rows.length == 5);
+  for(var i=0; i<5; i++)
+  {
+    T(res.rows[i].value == i+1);
+  }
+};

Propchange: couchdb/trunk/share/www/script/test/reduce_false.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/replication.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/replication.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/replication.js (added)
+++ couchdb/trunk/share/www/script/test/replication.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,207 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.replication = function(debug) {
+  if (debug) debugger;
+  var host = CouchDB.host;
+  var dbPairs = [
+    {source:"test_suite_db_a",
+      target:"test_suite_db_b"},
+    {source:"test_suite_db_a",
+      target:"http://" + host + "/test_suite_db_b"},
+    {source:"http://" + host + "/test_suite_db_a",
+      target:"test_suite_db_b"},
+    {source:"http://" + host + "/test_suite_db_a",
+      target:"http://" + host + "/test_suite_db_b"}
+  ]
+  var dbA = new CouchDB("test_suite_db_a");
+  var dbB = new CouchDB("test_suite_db_b");
+  var numDocs = 10;
+  var xhr;
+  for (var testPair = 0; testPair < dbPairs.length; testPair++) {
+    var A = dbPairs[testPair].source
+    var B = dbPairs[testPair].target
+
+    dbA.deleteDb();
+    dbA.createDb();
+    dbB.deleteDb();
+    dbB.createDb();
+    
+    var repTests = {
+      // copy and paste and put your code in. delete unused steps.
+      test_template: new function () {
+        this.init = function(dbA, dbB) {
+          // before anything has happened
+        }
+        this.afterAB1 = function(dbA, dbB) {
+          // called after replicating src=A  tgt=B first time.
+        };
+        this.afterBA1 = function(dbA, dbB) {
+          // called after replicating src=B  tgt=A first time.
+        };
+        this.afterAB2 = function(dbA, dbB) {
+          // called after replicating src=A  tgt=B second time. 
+        };
+        this.afterBA2 = function(dbA, dbB) {
+          // etc...
+        };
+      },
+      
+      simple_test: new function () {
+        this.init = function(dbA, dbB) {
+          var docs = makeDocs(0, numDocs);
+          T(dbA.bulkSave(docs).ok);
+        };
+      
+        this.afterAB1 = function(dbA, dbB) {          
+          for (var j = 0; j < numDocs; j++) {
+            var docA = dbA.open("" + j);
+            var docB = dbB.open("" + j);
+            T(docA._rev == docB._rev);
+          }
+        };
+      },
+    
+     deletes_test: new function () {
+        this.init = function(dbA, dbB) {
+          T(dbA.save({_id:"foo1",value:"a"}).ok);
+        };
+        
+        this.afterAB1 = function(dbA, dbB) {
+          var docA = dbA.open("foo1");
+          var docB = dbB.open("foo1");
+          T(docA._rev == docB._rev);
+
+          dbA.deleteDoc(docA);
+        };
+        
+        this.afterAB2 = function(dbA, dbB) {
+          T(dbA.open("foo1") == null);
+          T(dbB.open("foo1") == null);
+        };
+      },
+      
+      slashes_in_ids_test: new function () {
+        // make sure docs with slashes in id replicate properly
+        this.init = function(dbA, dbB) {
+          dbA.save({ _id:"abc/def", val:"one" });
+        };
+        
+        this.afterAB1 = function(dbA, dbB) {
+          var docA = dbA.open("abc/def");
+          var docB = dbB.open("abc/def");
+          T(docA._rev == docB._rev);
+        };
+      },
+
+      design_docs_test: new function() {
+        // make sure design docs replicate properly
+        this.init = function(dbA, dbB) {
+          dbA.save({ _id:"_design/test" });
+        };
+
+        this.afterAB1 = function() {
+          var docA = dbA.open("_design/test");
+          var docB = dbB.open("_design/test");
+          T(docA._rev == docB._rev);
+        };
+      },
+    
+      attachments_test: new function () {
+        // Test attachments
+        this.init = function(dbA, dbB) {
+          dbA.save({
+            _id:"bin_doc",
+            _attachments:{
+              "foo.txt": {
+                "type":"base64",
+                "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+              }
+            }
+          });
+        };
+        
+        this.afterAB1 = function(dbA, dbB) {
+          var xhr = CouchDB.request("GET", "/test_suite_db_a/bin_doc/foo.txt");
+          T(xhr.responseText == "This is a base64 encoded text")
+
+          xhr = CouchDB.request("GET", "/test_suite_db_b/bin_doc/foo.txt");
+          T(xhr.responseText == "This is a base64 encoded text")
+        };
+      },
+      
+      conflicts_test: new function () {
+        // test conflicts
+        this.init = function(dbA, dbB) {
+          dbA.save({_id:"foo",value:"a"});
+          dbB.save({_id:"foo",value:"b"});
+        };
+        
+        this.afterBA1 = function(dbA, dbB) {            
+          var docA = dbA.open("foo", {conflicts: true});
+          var docB = dbB.open("foo", {conflicts: true});
+
+          // make sure the same rev is in each db
+          T(docA._rev === docB._rev);
+
+          // make sure the conflicts are the same in each db
+          T(docA._conflicts[0] === docB._conflicts[0]);
+
+          // delete a conflict.
+          dbA.deleteDoc({_id:"foo", _rev:docA._conflicts[0]});
+        };
+        
+        this.afterBA2 = function(dbA, dbB) {            
+          // open documents and include the conflict meta data
+          var docA = dbA.open("foo", {conflicts: true});
+          var docB = dbB.open("foo", {conflicts: true});
+
+          // We should have no conflicts this time
+          T(docA._conflicts === undefined)
+          T(docB._conflicts === undefined);
+        };
+      }
+    };
+
+    var test;
+    for(test in repTests) {
+      if(repTests[test].init) {
+        repTests[test].init(dbA, dbB);
+      }
+    }
+
+    T(CouchDB.replicate(A, B).ok);
+
+    for(test in repTests) {
+      if(repTests[test].afterAB1) repTests[test].afterAB1(dbA, dbB);
+    }
+
+    T(CouchDB.replicate(B, A).ok);
+
+    for(test in repTests) {
+      if(repTests[test].afterBA1) repTests[test].afterBA1(dbA, dbB);
+    }
+
+    T(CouchDB.replicate(A, B).ok);
+
+    for(test in repTests) {
+      if(repTests[test].afterAB2) repTests[test].afterAB2(dbA, dbB);
+    }
+
+    T(CouchDB.replicate(B, A).ok);
+
+    for(test in repTests) {
+      if(repTests[test].afterBA2) repTests[test].afterBA2(dbA, dbB);
+    }
+
+  }
+};

Propchange: couchdb/trunk/share/www/script/test/replication.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/security_validation.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/security_validation.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/security_validation.js (added)
+++ couchdb/trunk/share/www/script/test/security_validation.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,154 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.security_validation = function(debug) {
+  // This tests couchdb's security and validation features. This does
+  // not test authentication, except to use test authentication code made
+  // specifically for this testing. It is a WWWW-Authenticate scheme named
+  // X-Couch-Test-Auth, and the user names and passwords are hard coded
+  // on the server-side.
+  // 
+  // We could have used Basic authentication, however the XMLHttpRequest
+  // implementation for Firefox and Safari, and probably other browsers are
+  // broken (Firefox always prompts the user on 401 failures, Safari gives
+  // odd security errors when using different name/passwords, perhaps due
+  // to cross site scripting prevention).  These problems essentially make Basic
+  // authentication testing in the browser impossible. But while hard to
+  // test automated in the browser, Basic auth may still useful for real
+  // world use where these bugs/behaviors don't matter.
+  //
+  // So for testing purposes we are using this custom X-Couch-Test-Auth.
+  // It's identical to Basic auth, except it doesn't even base64 encode
+  // the "username:password" string, it's sent completely plain text.
+  // Firefox and Safari both deal with this correctly (which is to say
+  // they correctly do nothing special).
+  
+  
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+  
+  run_on_modified_server(
+    [{section: "httpd",
+      key: "authentication_handler",
+      value: "{couch_httpd, special_test_authentication_handler}"},
+    {section:"httpd",
+      key: "WWW-Authenticate",
+      value:  "X-Couch-Test-Auth"}],
+      
+    function () {
+  
+      // try saving document usin the wrong credentials
+      var wrongPasswordDb = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
+      );
+  
+      try {
+        wrongPasswordDb.save({foo:1,author:"Damien Katz"});
+        T(false && "Can't get here. Should have thrown an error 1");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(wrongPasswordDb.last_req.status == 401);
+      }
+      
+      
+      // Create the design doc that will run custom validation code
+      var designDoc = {
+        _id:"_design/test",
+        language: "javascript",
+        validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
+          // docs should have an author field.
+          if (!newDoc._deleted && !newDoc.author) {
+            throw {forbidden:
+                "Documents must have an author field"};
+          }
+          if (oldDoc && oldDoc.author != userCtx.name) {
+              throw {unauthorized:
+                  "You are not the author of this document. You jerk."};
+          }
+        }).toString() + ")"
+      }
+
+      // Save a document normally
+      var userDb = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
+      );
+      
+      T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
+      
+      // Attempt to save the design as a non-admin
+      try {
+        userDb.save(designDoc);
+        T(false && "Can't get here. Should have thrown an error on design doc");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(userDb.last_req.status == 401);
+      }
+      
+      // add user as admin
+      db.setAdmins(["Damien Katz"]);
+      
+      T(userDb.save(designDoc).ok);
+  
+      // update the document
+      var doc = userDb.open("testdoc");
+      doc.foo=2;
+      T(userDb.save(doc).ok);
+      
+      // Save a document that's missing an author field.
+      try {
+        userDb.save({foo:1});
+        T(false && "Can't get here. Should have thrown an error 2");
+      } catch (e) {
+        T(e.error == "forbidden");
+        T(userDb.last_req.status == 403);
+      }
+  
+      // Now attempt to update the document as a different user, Jan 
+      var user2Db = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
+      );
+  
+      var doc = user2Db.open("testdoc");
+      doc.foo=3;
+      try {
+        user2Db.save(doc);
+        T(false && "Can't get here. Should have thrown an error 3");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(user2Db.last_req.status == 401);
+      }
+      
+      // Now have Damien change the author to Jan
+      doc = userDb.open("testdoc");
+      doc.author="Jan Lehnardt";
+      T(userDb.save(doc).ok);
+      
+      // Now update the document as Jan
+      doc = user2Db.open("testdoc");
+      doc.foo = 3;
+      T(user2Db.save(doc).ok);
+      
+      // Damien can't delete it
+      try {
+        userDb.deleteDoc(doc);
+        T(false && "Can't get here. Should have thrown an error 4");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(userDb.last_req.status == 401);
+      }
+      
+      // Now delete document
+      T(user2Db.deleteDoc(doc).ok);
+    });
+};

Propchange: couchdb/trunk/share/www/script/test/security_validation.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/show_documents.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/show_documents.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/show_documents.js (added)
+++ couchdb/trunk/share/www/script/test/show_documents.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,282 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+
+couchTests.show_documents = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+      
+  var designDoc = {
+    _id:"_design/template",
+    language: "javascript",
+    shows: {
+      "hello" : stringFun(function(doc) { 
+        if (doc) {
+          return "Hello World";             
+        } else {
+          return "Empty World";
+        }
+      }),
+      "just-name" : stringFun(function(doc, req) {
+        return {
+          body : "Just " + doc.name
+        };
+      }),
+      "req-info" : stringFun(function(doc, req) {
+        return {
+          json : req
+        }
+      }),
+      "xml-type" : stringFun(function(doc, req) {
+         return {
+           "headers" : {
+             "Content-Type" : "application/xml"
+           },
+           "body" : new XML('<xml><node foo="bar"/></xml>')
+         }
+       }),
+      "no-set-etag" : stringFun(function(doc, req) {
+        return {
+          headers : {
+            "Etag" : "skipped"
+          },
+          "body" : "something"
+        }
+      }),
+      "accept-switch" : stringFun(function(doc, req) {
+        if (req.headers["Accept"].match(/image/)) {
+          return {
+            // a 16x16 px version of the CouchDB logo
+            "base64" : 
+["iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAsV",
+"BMVEUAAAD////////////////////////5ur3rEBn////////////////wDBL/",
+"AADuBAe9EB3IEBz/7+//X1/qBQn2AgP/f3/ilpzsDxfpChDtDhXeCA76AQH/v7",
+"/84eLyWV/uc3bJPEf/Dw/uw8bRWmP1h4zxSlD6YGHuQ0f6g4XyQkXvCA36MDH6",
+"wMH/z8/yAwX64ODeh47BHiv/Ly/20dLQLTj98PDXWmP/Pz//39/wGyJ7Iy9JAA",
+"AADHRSTlMAbw8vf08/bz+Pv19jK/W3AAAAg0lEQVR4Xp3LRQ4DQRBD0QqTm4Y5",
+"zMxw/4OleiJlHeUtv2X6RbNO1Uqj9g0RMCuQO0vBIg4vMFeOpCWIWmDOw82fZx",
+"vaND1c8OG4vrdOqD8YwgpDYDxRgkSm5rwu0nQVBJuMg++pLXZyr5jnc1BaH4GT",
+"LvEliY253nA3pVhQqdPt0f/erJkMGMB8xucAAAAASUVORK5CYII="].join(''),
+            headers : {
+              "Content-Type" : "image/png",
+              "Vary" : "Accept" // we set this for proxy caches
+            }
+          };
+        } else {
+          return {
+            "body" : "accepting text requests",
+            headers : {
+              "Content-Type" : "text/html",
+              "Vary" : "Accept"
+            }
+          };
+        }
+      }),
+      "respondWith" : stringFun(function(doc, req) {
+        registerType("foo", "application/foo","application/x-foo");
+        return respondWith(req, {
+          html : function() {
+            return {
+              body:"Ha ha, you said \"" + doc.word + "\"."
+            };
+          },
+          xml : function() {
+            var xml = new XML('<xml><node/></xml>');
+            // Becase Safari can't stand to see that dastardly
+            // E4X outside of a string. Outside of tests you
+            // can just use E4X literals.
+            this.eval('xml.node.@foo = doc.word');
+            return {
+              body: xml
+            };
+          },
+          foo : function() {
+            return {
+              body: "foofoo"
+            };
+          },
+          fallback : "html"
+        });
+      })
+    }
+  };
+  T(db.save(designDoc).ok);
+  
+  var doc = {"word":"plankton", "name":"Rusty"}
+  var resp = db.save(doc);
+  T(resp.ok);
+  var docid = resp.id;
+
+  // show error
+  var xhr = CouchDB.request("GET", "/test_suite_db/_show/");
+  T(xhr.status == 404);
+  T(JSON.parse(xhr.responseText).reason == "Invalid path.");
+
+  // hello template world
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello/"+docid);
+  T(xhr.responseText == "Hello World");
+
+  // hello template world (no docid)
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/hello");
+  T(xhr.responseText == "Empty World");
+
+  
+  // show with doc
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
+  T(xhr.responseText == "Just Rusty");
+  
+  // show with missing doc
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/missingdoc");
+  T(xhr.status == 404);
+  var resp = JSON.parse(xhr.responseText);
+  T(resp.error == "not_found");
+  T(resp.reason == "missing");
+  
+  // show with missing func
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/missing/"+docid);
+  T(xhr.status == 404);
+  
+  // missing design doc
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/missingdoc/just-name/"+docid);
+  T(xhr.status == 404);
+  var resp = JSON.parse(xhr.responseText);
+  T(resp.error == "not_found");
+  
+  // query parameters
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/req-info/"+docid+"?foo=bar", {
+    headers: {
+      "Accept": "text/html;text/plain;*/*",
+      "X-Foo" : "bar"
+    }
+  });
+  var resp = JSON.parse(xhr.responseText);
+  T(equals(resp.headers["X-Foo"], "bar"));
+  T(equals(resp.query, {foo:"bar"}));
+  T(equals(resp.verb, "GET"));
+  T(equals(resp.path[4], docid));
+  T(equals(resp.info.db_name, "test_suite_db"));
+  
+  // returning a content-type
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/xml-type/"+docid);
+  T("application/xml" == xhr.getResponseHeader("Content-Type"));
+  T("Accept" == xhr.getResponseHeader("Vary"));
+
+  // accept header switching
+  // different mime has different etag
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
+    headers: {"Accept": "text/html;text/plain;*/*"}
+  });
+  T("text/html" == xhr.getResponseHeader("Content-Type"));
+  T("Accept" == xhr.getResponseHeader("Vary"));
+  var etag = xhr.getResponseHeader("etag");
+
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/accept-switch/"+docid, {
+    headers: {"Accept": "image/png;*/*"}
+  });
+  T(xhr.responseText.match(/PNG/))
+  T("image/png" == xhr.getResponseHeader("Content-Type"));
+  var etag2 = xhr.getResponseHeader("etag");
+  T(etag2 != etag);
+
+  // proper etags
+  // show with doc
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid);
+  // extract the ETag header values
+  etag = xhr.getResponseHeader("etag");
+  // get again with etag in request
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
+    headers: {"if-none-match": etag}
+  });
+  // should be 304
+  T(xhr.status == 304);    
+
+  // update the doc
+  doc.name = "Crusty";
+  resp = db.save(doc);
+  T(resp.ok);
+  // req with same etag
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
+    headers: {"if-none-match": etag}
+  });
+  // status is 200    
+  T(xhr.status == 200);
+
+  // get new etag and request again
+  etag = xhr.getResponseHeader("etag");
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
+    headers: {"if-none-match": etag}
+  });
+  // should be 304
+  T(xhr.status == 304);
+
+  // update design doc (but not function)
+  designDoc.isChanged = true;
+  T(db.save(designDoc).ok);
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
+    headers: {"if-none-match": etag}
+  });
+  // should be 304
+  T(xhr.status == 304);
+  
+  // update design doc function
+  designDoc.shows["just-name"] = (function(doc, req) {
+    return {
+      body : "Just old " + doc.name
+    };
+  }).toString();
+  T(db.save(designDoc).ok);
+
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/just-name/"+docid, {
+    headers: {"if-none-match": etag}
+  });
+  // status is 200    
+  T(xhr.status == 200);
+  
+  
+  // JS can't set etag
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/no-set-etag/"+docid);
+  // extract the ETag header values
+  etag = xhr.getResponseHeader("etag");
+  T(etag != "skipped")
+
+  // test the respondWith mime matcher
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
+    headers: {
+      "Accept": 'text/html,application/atom+xml; q=0.9'
+    }
+  });
+  T(xhr.getResponseHeader("Content-Type") == "text/html");
+  T(xhr.responseText == "Ha ha, you said \"plankton\".");
+
+  // now with xml
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
+    headers: {
+      "Accept": 'application/xml'
+    }
+  });
+  T(xhr.getResponseHeader("Content-Type") == "application/xml");
+  T(xhr.responseText.match(/node/));
+  T(xhr.responseText.match(/plankton/));
+  
+  // registering types works
+  xhr = CouchDB.request("GET", "/test_suite_db/_show/template/respondWith/"+docid, {
+    headers: {
+      "Accept": "application/x-foo"
+    }
+  });
+  T(xhr.getResponseHeader("Content-Type") == "application/x-foo");
+  T(xhr.responseText.match(/foofoo/));
+};

Propchange: couchdb/trunk/share/www/script/test/show_documents.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/utf8.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/utf8.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/utf8.js (added)
+++ couchdb/trunk/share/www/script/test/utf8.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,41 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.utf8 = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var texts = [];
+
+  texts[0] = "1. Ascii: hello"
+  texts[1] = "2. Russian: На берегу пустынных волн"
+  texts[2] = "3. Math: ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),"
+  texts[3] = "4. Geek: STARGΛ̊TE SG-1"
+  texts[4] = "5. Braille: ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌"
+
+  // check that we can save a reload with full fidelity
+  for (var i=0; i<texts.length; i++) {
+    T(db.save({_id:i.toString(), text:texts[i]}).ok);
+  }
+
+  for (var i=0; i<texts.length; i++) {
+    T(db.open(i.toString()).text == texts[i]);
+  }
+
+  // check that views and key collation don't blow up
+  var rows = db.query(function(doc) { emit(null, doc.text) }).rows;
+  for (var i=0; i<texts.length; i++) {
+    T(rows[i].value == texts[i]);
+  }
+};

Propchange: couchdb/trunk/share/www/script/test/utf8.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/uuids.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/uuids.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/uuids.js (added)
+++ couchdb/trunk/share/www/script/test/uuids.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,62 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.uuids = function(debug) {
+  var testHashBustingHeaders = function(xhr) {
+    T(xhr.getResponseHeader("Cache-Control").match(/no-cache/));
+    T(xhr.getResponseHeader("Pragma") == "no-cache");
+    
+    var currentTime = new Date();
+    var expiresHeader = Date.parse(xhr.getResponseHeader("Expires"));
+    var dateHeader = Date.parse(xhr.getResponseHeader("Date")); 
+    
+    T(expiresHeader < currentTime);
+    T(currentTime - dateHeader < 3000);
+  };
+    
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+  
+  // a single UUID without an explicit count
+  var xhr = CouchDB.request("GET", "/_uuids");
+  T(xhr.status == 200);
+  var result = JSON.parse(xhr.responseText);
+  T(result.uuids.length == 1);
+  var first = result.uuids[0];
+  testHashBustingHeaders(xhr);
+
+  // a single UUID with an explicit count
+  xhr = CouchDB.request("GET", "/_uuids?count=1");
+  T(xhr.status == 200);
+  result = JSON.parse(xhr.responseText);
+  T(result.uuids.length == 1);
+  var second = result.uuids[0];
+  T(first != second);
+
+  // no collisions with 1,000 UUIDs
+  xhr = CouchDB.request("GET", "/_uuids?count=1000");
+  T(xhr.status == 200);
+  result = JSON.parse(xhr.responseText);
+  T( result.uuids.length == 1000 );
+  var seen = {};
+  for(var i in result.uuids) {
+    var id = result.uuids[i];
+    T(seen[id] === undefined);
+    seen[id] = 1;
+  }
+  
+  // ensure we return a 405 on POST
+  xhr = CouchDB.request("POST", "/_uuids?count=1000");
+  T(xhr.status == 405);
+};

Propchange: couchdb/trunk/share/www/script/test/uuids.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_collation.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_collation.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_collation.js (added)
+++ couchdb/trunk/share/www/script/test/view_collation.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,88 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_collation = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  // NOTE, the values are already in their correct sort order. Consider this
+  // a specification of collation of json types.
+
+  var values = [];
+
+  // special values sort before all other types
+  values.push(null);
+  values.push(false);
+  values.push(true);
+
+  // then numbers
+  values.push(1);
+  values.push(2);
+  values.push(3.0);
+  values.push(4);
+
+  // then text, case sensitive
+  values.push("a");
+  values.push("A");
+  values.push("aa");
+  values.push("b");
+  values.push("B");
+  values.push("ba");
+  values.push("bb");
+
+  // then arrays. compared element by element until different.
+  // Longer arrays sort after their prefixes
+  values.push(["a"]);
+  values.push(["b"]);
+  values.push(["b","c"]);
+  values.push(["b","c", "a"]);
+  values.push(["b","d"]);
+  values.push(["b","d", "e"]);
+
+  // then object, compares each key value in the list until different.
+  // larger objects sort after their subset objects.
+  values.push({a:1});
+  values.push({a:2});
+  values.push({b:1});
+  values.push({b:2});
+  values.push({b:2, a:1}); // Member order does matter for collation.
+                           // CouchDB preserves member order
+                           // but doesn't require that clients will.
+                           // (this test might fail if used with a js engine
+                           // that doesn't preserve order)
+  values.push({b:2, c:2});
+
+  for (var i=0; i<values.length; i++) {
+    db.save({_id:(i).toString(), foo:values[i]});
+  }
+
+  var queryFun = function(doc) { emit(doc.foo, null); };
+  var rows = db.query(queryFun).rows;
+  for (i=0; i<values.length; i++) {
+    T(equals(rows[i].key, values[i]));
+  }
+
+  // everything has collated correctly. Now to check the descending output
+  rows = db.query(queryFun, null, {descending: true}).rows;
+  for (i=0; i<values.length; i++) {
+    T(equals(rows[i].key, values[values.length - 1 -i]));
+  }
+
+  // now check the key query args
+  for (i=1; i<values.length; i++) {
+    var queryOptions = {key:values[i]};
+    rows = db.query(queryFun, null, queryOptions).rows;
+    T(rows.length == 1 && equals(rows[0].key, values[i]));
+  }
+};

Propchange: couchdb/trunk/share/www/script/test/view_collation.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_conflicts.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_conflicts.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_conflicts.js (added)
+++ couchdb/trunk/share/www/script/test/view_conflicts.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,49 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_conflicts = function(debug) {
+  var dbA = new CouchDB("test_suite_db_a");
+  dbA.deleteDb();
+  dbA.createDb();
+  var dbB = new CouchDB("test_suite_db_b");
+  dbB.deleteDb();
+  dbB.createDb();
+  if (debug) debugger;
+
+  var docA = {_id: "foo", bar: 42};
+  T(dbA.save(docA).ok);
+  CouchDB.replicate(dbA.name, dbB.name);
+
+  var docB = dbB.open("foo");
+  docB.bar = 43;
+  dbB.save(docB);
+  docA.bar = 41;
+  dbA.save(docA);
+  CouchDB.replicate(dbA.name, dbB.name);
+
+  var doc = dbB.open("foo", {conflicts: true});
+  T(doc._conflicts.length == 1);
+  var conflictRev = doc._conflicts[0];
+  if (doc.bar == 41) { // A won
+    T(conflictRev == docB._rev);
+  } else { // B won
+    T(doc.bar == 43);
+    T(conflictRev == docA._rev);
+  }
+
+  var results = dbB.query(function(doc) {
+    if (doc._conflicts) {
+      emit(doc._id, doc._conflicts);
+    }
+  });
+  T(results.rows[0].value[0] == conflictRev);
+};

Propchange: couchdb/trunk/share/www/script/test/view_conflicts.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_errors.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_errors.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_errors.js (added)
+++ couchdb/trunk/share/www/script/test/view_errors.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,43 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_errors = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var doc = {integer: 1, string: "1", array: [1, 2, 3]};
+  T(db.save(doc).ok);
+
+  // emitting a key value that is undefined should result in that row not
+  // being included in the view results
+  var results = db.query(function(doc) {
+    emit(doc.undef, null);
+  });
+  T(results.total_rows == 0);
+
+  // if a view function throws an exception, its results are not included in
+  // the view index, but the view does not itself raise an error
+  var results = db.query(function(doc) {
+    doc.undef(); // throws an error
+  });
+  T(results.total_rows == 0);
+
+  // if a view function includes an undefined value in the emitted key or
+  // value, an error is logged and the result is not included in the view
+  // index, and the view itself does not raise an error
+  var results = db.query(function(doc) {
+    emit([doc._id, doc.undef], null);
+  });
+  T(results.total_rows == 0);
+};

Propchange: couchdb/trunk/share/www/script/test/view_errors.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_include_docs.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_include_docs.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_include_docs.js (added)
+++ couchdb/trunk/share/www/script/test/view_include_docs.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,111 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_include_docs = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var docs = makeDocs(0, 100);
+  T(db.bulkSave(docs).ok);
+
+  var designDoc = {
+    _id:"_design/test",
+    language: "javascript",
+    views: {
+      all_docs: {
+        map: "function(doc) { emit(doc.integer, doc.string) }"
+      },
+      with_prev: {
+        map: "function(doc){if(doc.prev) emit(doc._id,{'_rev':doc.prev}); else emit(doc._id,{'_rev':doc._rev});}"
+      },
+      summate: {
+        map:"function (doc) {emit(doc.integer, doc.integer)};",
+        reduce:"function (keys, values) { return sum(values); };"
+      }
+    }
+  }
+  T(db.save(designDoc).ok);
+
+  var resp = db.view('test/all_docs', {include_docs: true, limit: 2});
+  T(resp.rows.length == 2);
+  T(resp.rows[0].id == "0");
+  T(resp.rows[0].doc._id == "0");
+  T(resp.rows[1].id == "1");
+  T(resp.rows[1].doc._id == "1");
+
+  resp = db.view('test/all_docs', {include_docs: true}, [29, 74]);
+  T(resp.rows.length == 2);
+  T(resp.rows[0].doc._id == "29");
+  T(resp.rows[1].doc.integer == 74);
+
+  resp = db.allDocs({limit: 2, skip: 1, include_docs: true});
+  T(resp.rows.length == 2);
+  T(resp.rows[0].doc.integer == 1);
+  T(resp.rows[1].doc.integer == 10);
+
+  resp = db.allDocs({include_docs: true}, ['not_a_doc']);
+  T(resp.rows.length == 1);
+  T(!resp.rows[0].doc);
+
+  resp = db.allDocs({include_docs: true}, ["1", "foo"]);
+  T(resp.rows.length == 2);
+  T(resp.rows[0].doc.integer == 1);
+  T(!resp.rows[1].doc);
+
+  resp = db.allDocs({include_docs: true, limit: 0});
+  T(resp.rows.length == 0);
+
+  // No reduce support
+  try {
+      resp = db.view('test/summate', {include_docs: true});
+      alert(JSON.stringify(resp));
+      T(0==1);
+  } catch (e) {
+      T(e.error == 'query_parse_error');
+  }
+
+  // Reduce support when reduce=false
+  resp = db.view('test/summate', {reduce: false, include_docs: true});
+  T(resp.rows.length == 100);
+
+  // Check emitted _rev controls things
+  resp = db.allDocs({include_docs: true}, ["0"]);
+  var before = resp.rows[0].doc;
+  var after = db.open("0");
+  after.integer = 100
+  after.prev = after._rev;
+  db.save(after);
+  after = db.open("0");
+  T(after._rev != after.prev);
+  T(after.integer == 100);
+
+  // should emit the previous revision
+  resp = db.view("test/with_prev", {include_docs: true}, ["0"]);
+  T(resp.rows[0].doc._id == "0");
+  T(resp.rows[0].doc._rev == before._rev);
+  T(!resp.rows[0].doc.prev);
+  T(resp.rows[0].doc.integer == 0);
+
+  var xhr = CouchDB.request("POST", "/test_suite_db/_compact");
+  T(xhr.status == 202)
+  while (db.info().compact_running) {}
+
+  resp = db.view("test/with_prev", {include_docs: true}, ["0", "23"]);
+  T(resp.rows.length == 2);
+  T(resp.rows[0].key == "0");
+  T(resp.rows[0].id == "0");
+  T(!resp.rows[0].doc);
+  T(resp.rows[0].error == "missing");
+  T(resp.rows[1].doc.integer == 23);
+};

Propchange: couchdb/trunk/share/www/script/test/view_include_docs.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_multi_key_all_docs.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_multi_key_all_docs.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_multi_key_all_docs.js (added)
+++ couchdb/trunk/share/www/script/test/view_multi_key_all_docs.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,54 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_multi_key_all_docs = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var docs = makeDocs(0, 100);
+  T(db.bulkSave(docs).ok);
+
+  var keys = ["10","15","30","37","50"];
+  var rows = db.allDocs({},keys).rows;
+  T(rows.length == keys.length);
+  for(var i=0; i<rows.length; i++)
+    T(rows[i].id == keys[i]);
+
+  rows = db.allDocs({limit: 1}, keys).rows;
+  T(rows.length == 1);
+  T(rows[0].id == keys[0]);
+
+  rows = db.allDocs({skip: 2}, keys).rows;
+  T(rows.length == 3);
+  for(var i=0; i<rows.length; i++)
+      T(rows[i].id == keys[i+2]);
+
+  rows = db.allDocs({descending: "true"}, keys).rows;
+  T(rows.length == keys.length);
+  for(var i=0; i<rows.length; i++)
+      T(rows[i].id == keys[keys.length-i-1]);
+
+  rows = db.allDocs({descending: "true", skip: 3, limit:1}, keys).rows;
+  T(rows.length == 1);
+  T(rows[0].id == keys[1]);
+
+  // Check we get invalid rows when the key doesn't exist
+  rows = db.allDocs({}, [1, "i_dont_exist", "0"]).rows;
+  T(rows.length == 3);
+  T(rows[0].error == "not_found");
+  T(!rows[0].id);
+  T(rows[1].error == "not_found");
+  T(!rows[1].id);
+  T(rows[2].id == rows[2].key && rows[2].key == "0");
+};

Propchange: couchdb/trunk/share/www/script/test/view_multi_key_all_docs.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_multi_key_design.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_multi_key_design.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_multi_key_design.js (added)
+++ couchdb/trunk/share/www/script/test/view_multi_key_design.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,138 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_multi_key_design = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var docs = makeDocs(0, 100);
+  T(db.bulkSave(docs).ok);
+
+  var designDoc = {
+    _id:"_design/test",
+    language: "javascript",
+    views: {
+      all_docs: {
+        map: "function(doc) { emit(doc.integer, doc.string) }"
+      },
+      multi_emit: {
+        map: "function(doc) {for(var i = 0 ; i < 3 ; i++) { emit(i, doc.integer) ; } }"
+      },
+      summate: {
+        map:"function (doc) {emit(doc.integer, doc.integer)};",
+        reduce:"function (keys, values) { return sum(values); };"
+      }
+    }
+  }
+  T(db.save(designDoc).ok);
+
+  // Test that missing keys work too
+  var keys = [101,30,15,37,50]
+  var reduce = db.view("test/summate",{group:true},keys).rows;
+  T(reduce.length == keys.length-1); // 101 is missing
+  for(var i=0; i<reduce.length; i++) {
+    T(keys.indexOf(reduce[i].key) != -1);
+    T(reduce[i].key == reduce[i].value);
+  }
+
+  // First, the goods:
+  var keys = [10,15,30,37,50];
+  var rows = db.view("test/all_docs",{},keys).rows;
+  for(var i=0; i<rows.length; i++) {
+    T(keys.indexOf(rows[i].key) != -1);
+    T(rows[i].key == rows[i].value);
+  }
+  
+  var reduce = db.view("test/summate",{group:true},keys).rows;
+  T(reduce.length == keys.length);
+  for(var i=0; i<reduce.length; i++) {
+    T(keys.indexOf(reduce[i].key) != -1);
+    T(reduce[i].key == reduce[i].value);
+  }
+
+  // Test that invalid parameter combinations get rejected
+  var badargs = [{startkey:0}, {endkey:0}, {key: 0}, {group_level: 2}];
+  for(var i in badargs)
+  {
+      try {
+          db.view("test/all_docs",badargs[i],keys);
+          T(0==1);
+      } catch (e) {
+          T(e.error == "query_parse_error");
+      }
+  }
+
+  try {
+      db.view("test/summate",{},keys);
+      T(0==1);
+  } catch (e) {
+      T(e.error == "query_parse_error");
+  }
+
+  // Test that a map & reduce containing func support keys when reduce=false
+  resp = db.view("test/summate", {reduce: false}, keys);
+  T(resp.rows.length == 5);
+
+  // Check that limiting by startkey_docid and endkey_docid get applied
+  // as expected.
+  var curr = db.view("test/multi_emit", {startkey_docid: 21, endkey_docid: 23}, [0, 2]).rows;
+  var exp_key = [ 0,  0,  0,  2,  2,  2] ;
+  var exp_val = [21, 22, 23, 21, 22, 23] ;
+  T(curr.length == 6);
+  for( var i = 0 ; i < 6 ; i++)
+  {
+      T(curr[i].key == exp_key[i]);
+      T(curr[i].value == exp_val[i]);
+  }
+
+  // Check limit works
+  curr = db.view("test/all_docs", {limit: 1}, keys).rows;
+  T(curr.length == 1);
+  T(curr[0].key == 10);
+
+  // Check offset works
+  curr = db.view("test/multi_emit", {skip: 1}, [0]).rows;
+  T(curr.length == 99);
+  T(curr[0].value == 1);
+
+  // Check that dir works
+  curr = db.view("test/multi_emit", {descending: "true"}, [1]).rows;
+  T(curr.length == 100);
+  T(curr[0].value == 99);
+  T(curr[99].value == 0);
+
+  // Check a couple combinations
+  curr = db.view("test/multi_emit", {descending: "true", skip: 3, limit: 2}, [2]).rows;
+  T(curr.length, 2);
+  T(curr[0].value == 96);
+  T(curr[1].value == 95);
+
+  curr = db.view("test/multi_emit", {skip: 2, limit: 3, startkey_docid: "13"}, [0]).rows;
+  T(curr.length == 3);
+  T(curr[0].value == 15);
+  T(curr[1].value == 16);
+  T(curr[2].value == 17);
+
+  curr = db.view("test/multi_emit",
+          {skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"}, [1]).rows;
+  T(curr.length == 2);
+  T(curr[0].value == 26);
+  T(curr[1].value == 27);
+
+  curr = db.view("test/multi_emit",
+          {skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: "true"}, [1]).rows;
+  T(curr.length == 2);
+  T(curr[0].value == 27);
+  T(curr[1].value == 26);
+};

Propchange: couchdb/trunk/share/www/script/test/view_multi_key_design.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_multi_key_temp.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_multi_key_temp.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_multi_key_temp.js (added)
+++ couchdb/trunk/share/www/script/test/view_multi_key_temp.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,37 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_multi_key_temp = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  var docs = makeDocs(0, 100);
+  T(db.bulkSave(docs).ok);
+
+  var queryFun = function(doc) { emit(doc.integer, doc.integer) };
+  var reduceFun = function (keys, values) { return sum(values); };
+
+  var keys = [10,15,30,37,50];
+  var rows = db.query(queryFun, null, {}, keys).rows;
+  for(var i=0; i<rows.length; i++) {
+    T(keys.indexOf(rows[i].key) != -1);
+    T(rows[i].key == rows[i].value);
+  }
+  
+  var reduce = db.query(queryFun, reduceFun, {group:true}, keys).rows;
+  for(var i=0; i<reduce.length; i++) {
+    T(keys.indexOf(reduce[i].key) != -1);
+    T(reduce[i].key == reduce[i].value);
+  }
+};

Propchange: couchdb/trunk/share/www/script/test/view_multi_key_temp.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_pagination.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_pagination.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_pagination.js (added)
+++ couchdb/trunk/share/www/script/test/view_pagination.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,122 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_pagination = function(debug) {
+    var db = new CouchDB("test_suite_db");
+    db.deleteDb();
+    db.createDb();
+    if (debug) debugger;
+
+    var docs = makeDocs(0, 100);
+    T(db.bulkSave(docs).ok);
+
+    var queryFun = function(doc) { emit(doc.integer, null) };
+    var i;
+
+    // page through the view ascending
+    for (i = 0; i < docs.length; i += 10) {
+      var queryResults = db.query(queryFun, null, {
+        startkey: i,
+        startkey_docid: i,
+        limit: 10
+      });
+      T(queryResults.rows.length == 10)
+      T(queryResults.total_rows == docs.length)
+      T(queryResults.offset == i)
+      var j;
+      for (j = 0; j < 10;j++) {
+        T(queryResults.rows[j].key == i + j);
+      }
+    }
+
+    // page through the view descending
+    for (i = docs.length - 1; i >= 0; i -= 10) {
+      var queryResults = db.query(queryFun, null, {
+        startkey: i,
+        startkey_docid: i,
+        descending: true,
+        limit: 10
+      });
+      T(queryResults.rows.length == 10)
+      T(queryResults.total_rows == docs.length)
+      T(queryResults.offset == docs.length - i - 1)
+      var j;
+      for (j = 0; j < 10; j++) {
+        T(queryResults.rows[j].key == i - j);
+      }
+    }
+
+    // ignore decending=false. CouchDB should just ignore that.
+    for (i = 0; i < docs.length; i += 10) {
+      var queryResults = db.query(queryFun, null, {
+        startkey: i,
+        startkey_docid: i,
+        descending: false,
+        limit: 10
+      });
+      T(queryResults.rows.length == 10)
+      T(queryResults.total_rows == docs.length)
+      T(queryResults.offset == i)
+      var j;
+      for (j = 0; j < 10;j++) {
+        T(queryResults.rows[j].key == i + j);
+      }
+    }
+    
+    // test endkey_docid
+    var queryResults = db.query(function(doc) { emit(null, null);}, null, {
+      startkey: null,
+      startkey_docid: 1,
+      endkey: null,
+      endkey_docid: 40
+    });
+    
+    T(queryResults.rows.length == 35)
+    T(queryResults.total_rows == docs.length)
+    T(queryResults.offset == 1)
+    T(queryResults.rows[0].id == "1");
+    T(queryResults.rows[1].id == "10");
+    T(queryResults.rows[2].id == "11");
+    T(queryResults.rows[3].id == "12");
+    T(queryResults.rows[4].id == "13");
+    T(queryResults.rows[5].id == "14");
+    T(queryResults.rows[6].id == "15");
+    T(queryResults.rows[7].id == "16");
+    T(queryResults.rows[8].id == "17");
+    T(queryResults.rows[9].id == "18");
+    T(queryResults.rows[10].id == "19");
+    T(queryResults.rows[11].id == "2");
+    T(queryResults.rows[12].id == "20");
+    T(queryResults.rows[13].id == "21");
+    T(queryResults.rows[14].id == "22");
+    T(queryResults.rows[15].id == "23");
+    T(queryResults.rows[16].id == "24");
+    T(queryResults.rows[17].id == "25");
+    T(queryResults.rows[18].id == "26");
+    T(queryResults.rows[19].id == "27");
+    T(queryResults.rows[20].id == "28");
+    T(queryResults.rows[21].id == "29");
+    T(queryResults.rows[22].id == "3");
+    T(queryResults.rows[23].id == "30");
+    T(queryResults.rows[24].id == "31");
+    T(queryResults.rows[25].id == "32");
+    T(queryResults.rows[26].id == "33");
+    T(queryResults.rows[27].id == "34");
+    T(queryResults.rows[28].id == "35");
+    T(queryResults.rows[29].id == "36");
+    T(queryResults.rows[30].id == "37");
+    T(queryResults.rows[31].id == "38");
+    T(queryResults.rows[32].id == "39");
+    T(queryResults.rows[33].id == "4");
+    T(queryResults.rows[34].id == "40");
+
+  };

Propchange: couchdb/trunk/share/www/script/test/view_pagination.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_sandboxing.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_sandboxing.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_sandboxing.js (added)
+++ couchdb/trunk/share/www/script/test/view_sandboxing.js Sun Feb 15 23:59:38 2009
@@ -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);
+};

Propchange: couchdb/trunk/share/www/script/test/view_sandboxing.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: couchdb/trunk/share/www/script/test/view_xml.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_xml.js?rev=744782&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_xml.js (added)
+++ couchdb/trunk/share/www/script/test/view_xml.js Sun Feb 15 23:59:38 2009
@@ -0,0 +1,39 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy
+// of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.view_xml = function(debug) {
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  db.save({content: "<doc><title id='xml'>Testing XML</title></doc>"});
+  db.save({content: "<doc><title id='e4x'>Testing E4X</title></doc>"});
+
+  var results = db.query(
+    "function(doc) {\n" +
+    "  var xml = new XML(doc.content);\n" +
+    "  emit(xml.title.text(), null);\n" +
+    "}");
+  T(results.total_rows == 2);
+  T(results.rows[0].key == "Testing E4X");
+  T(results.rows[1].key == "Testing XML");
+
+  var results = db.query(
+    "function(doc) {\n" +
+    "  var xml = new XML(doc.content);\n" +
+    "  emit(xml.title.@id, null);\n" +
+    "}");
+  T(results.total_rows == 2);
+  T(results.rows[0].key == "e4x");
+  T(results.rows[1].key == "xml");
+};

Propchange: couchdb/trunk/share/www/script/test/view_xml.js
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: couchdb/trunk/test/runner.sh
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/runner.sh?rev=744782&r1=744781&r2=744782&view=diff
==============================================================================
--- couchdb/trunk/test/runner.sh (original)
+++ couchdb/trunk/test/runner.sh Sun Feb 15 23:59:38 2009
@@ -4,4 +4,4 @@
 
 erl -noshell -pa ../src/couchdb -pa ../src/mochiweb -eval "runner:run()"
 
-cat ../share/www/script/couch.js ../share/www/script/couch_test_runner.js ../share/www/script/couch_tests.js test.js  | ../src/couchdb/couchjs -
+cat ../share/www/script/couch.js ../share/www/script/couch_test_runner.js ../share/www/script/couch_tests.js ../share/www/script/test/* test.js  | ../src/couchdb/couchjs -

Modified: couchdb/trunk/test/test.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/test.js?rev=744782&r1=744781&r2=744782&view=diff
==============================================================================
--- couchdb/trunk/test/test.js (original)
+++ couchdb/trunk/test/test.js Sun Feb 15 23:59:38 2009
@@ -194,14 +194,14 @@
 function runAllTestsConsole() {
   var numTests = 0;
   var debug = false;
-  for (var t in tests) {
+  for (var t in couchTests) {
     p(t);
     if (t == "utf8") {
       p("We skip the utf8 test because it fails due to problems in couch_js.c");
       p("Run the in-browser tests to verify utf8.\n");
     } else {
       numTests += 1;
-      var testFun = tests[t];
+      var testFun = couchTests[t];
       runTestConsole(testFun, debug);      
     }
   }