// 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", {"X-Couch-Full-Commit":"false"}); db.deleteDb(); db.createDb(); if (debug) debugger; var designDoc = { _id:"_design/template", language: "javascript", shows: { "hello" : stringFun(function(doc, req) { log("hello fun"); if (doc) { return "Hello World"; } else { if(req.id) { return "New World"; } else { return "Empty World"; } } }), "just-name" : stringFun(function(doc, req) { if (doc) { return { body : "Just " + doc.name }; } else { return { body : "No such doc", code : 404 }; } }), "json" : stringFun(function(doc, req) { return { json : doc } }), "req-info" : stringFun(function(doc, req) { return { json : req } }), "show-deleted" : stringFun(function(doc, req) { if(doc) { return doc._id; } else { return "No doc " + req.id; } }), "render-error" : stringFun(function(doc, req) { return noSuchVariable; }), "empty" : stringFun(function(doc, req) { return ""; }), "fail" : stringFun(function(doc, req) { return doc._id; }), "xml-type" : stringFun(function(doc, req) { return { "headers" : { "Content-Type" : "application/xml" }, "body" : new XML('').toXMLString() } }), "no-set-etag" : stringFun(function(doc, req) { return { headers : { "Etag" : "skipped" }, "body" : "something" } }), "list-api" : stringFun(function(doc, req) { start({"X-Couch-Test-Header": "Yeah"}); send("Hey"); }), "list-api-provides" : stringFun(function(doc, req) { provides("text", function(){ send("foo, "); send("bar, "); send("baz!"); }) }), "list-api-provides-and-return" : stringFun(function(doc, req) { provides("text", function(){ send("4, "); send("5, "); send("6, "); return "7!"; }) send("1, "); send("2, "); return "3, "; }), "list-api-mix" : stringFun(function(doc, req) { start({"X-Couch-Test-Header": "Yeah"}); send("Hey "); return "Dude"; }), "list-api-mix-with-header" : stringFun(function(doc, req) { start({"X-Couch-Test-Header": "Yeah"}); send("Hey "); return { headers: { "X-Couch-Test-Header-Awesome": "Oh Yeah!" }, body: "Dude" }; }), "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" } }; } }), "provides" : stringFun(function(doc, req) { registerType("foo", "application/foo","application/x-foo"); provides("html", function() { return "Ha ha, you said \"" + doc.word + "\"."; }); provides("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. eval('xml.node.@foo = doc.word'); log('xml: '+xml.toSource()); return xml.toXMLString(); }); provides("foo", function() { return "foofoo"; }); }), "withSlash": stringFun(function(doc, req) { return { json: doc } }), "secObj": stringFun(function(doc, req) { return { json: req.secObj }; }) } }; 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/_design/template/_show/"); T(xhr.status == 404, 'Should be missing'); T(JSON.parse(xhr.responseText).reason == "Invalid path."); // hello template world xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/"+docid); T(xhr.responseText == "Hello World", "hello"); T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type"))); // Fix for COUCHDB-379 T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB")); // // error stacktraces // xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid); // T(JSON.parse(xhr.responseText).error == "render_error"); // hello template world (no docid) xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello"); T(xhr.responseText == "Empty World"); // hello template world (no docid) xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/empty"); T(xhr.responseText == ""); // // hello template world (non-existing docid) xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/fail/nonExistingDoc"); T(xhr.status == 404); var resp = JSON.parse(xhr.responseText); T(resp.error == "not_found"); // show with doc xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid); T(xhr.responseText == "Just Rusty"); // show with missing doc xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc"); T(xhr.status == 404); TEquals("No such doc", xhr.responseText); // show with missing func xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/missing/"+docid); T(xhr.status == 404, "function is missing"); // missing design doc xhr = CouchDB.request("GET", "/test_suite_db/_design/missingddoc/_show/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/_design/template/_show/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.method, "GET")); T(equals(resp.path[5], docid)); T(equals(resp.info.db_name, "test_suite_db")); // returning a content-type xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/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/_design/template/_show/accept-switch/"+docid, { headers: {"Accept": "text/html;text/plain;*/*"} }); var ct = xhr.getResponseHeader("Content-Type"); T(/text\/html/.test(ct)) T("Accept" == xhr.getResponseHeader("Vary")); var etag = xhr.getResponseHeader("etag"); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/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/_design/template/_show/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/_design/template/_show/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/_design/template/_show/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/_design/template/_show/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/_design/template/_show/just-name/"+docid, { headers: {"if-none-match": etag} }); // should not be 304 if we change the doc T(xhr.status != 304, "changed ddoc"); // 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/_design/template/_show/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/_design/template/_show/no-set-etag/"+docid); // extract the ETag header values etag = xhr.getResponseHeader("etag"); T(etag != "skipped") // test the provides mime matcher xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, { headers: { "Accept": 'text/html,application/atom+xml; q=0.9' } }); var ct = xhr.getResponseHeader("Content-Type"); T(/charset=utf-8/.test(ct)) T(/text\/html/.test(ct)) T(xhr.responseText == "Ha ha, you said \"plankton\"."); // now with xml xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+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/_design/template/_show/provides/"+docid, { headers: { "Accept": "application/x-foo" } }); T(xhr.getResponseHeader("Content-Type") == "application/x-foo"); T(xhr.responseText.match(/foofoo/)); // test the provides mime matcher without a match xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, { headers: { "Accept": 'text/monkeys' } }); var rs = JSON.parse(xhr.responseText); T(rs.error == "not_acceptable") // should fallback on the first one xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/provides/"+docid, { headers: { "Accept": 'application/x-foo, application/xml' } }); var ct = xhr.getResponseHeader("Content-Type"); T(/application\/xml/.test(ct)); // test inclusion of conflict state var doc1 = {_id:"foo", a:1}; var doc2 = {_id:"foo", a:2}; db.save(doc1); // create the conflict with an all_or_nothing bulk docs request var docs = [doc2]; db.bulkSave(docs, {all_or_nothing:true}); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/json/foo"); TEquals(1, JSON.parse(xhr.responseText)._conflicts.length); var doc3 = {_id:"a/b/c", a:1}; db.save(doc3); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/withSlash/a/b/c"); T(xhr.status == 200); // hello template world (non-existing docid) xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/nonExistingDoc"); T(xhr.responseText == "New World"); // test list() compatible API xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api/foo"); T(xhr.responseText == "Hey"); TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool"); // test list() compatible API with provides function xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-provides/foo?format=text"); TEquals(xhr.responseText, "foo, bar, baz!", "should join chunks to response body"); // should keep next result order: chunks + return value + provided chunks + provided return value xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-provides-and-return/foo?format=text"); TEquals(xhr.responseText, "1, 2, 3, 4, 5, 6, 7!", "should not break 1..7 range"); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix/foo"); T(xhr.responseText == "Hey Dude"); TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool"); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/list-api-mix-with-header/foo"); T(xhr.responseText == "Hey Dude"); TEquals("Yeah", xhr.getResponseHeader("X-Couch-Test-Header"), "header should be cool"); TEquals("Oh Yeah!", xhr.getResponseHeader("X-Couch-Test-Header-Awesome"), "header should be cool"); // test deleted docs var doc = {_id:"testdoc",foo:1}; db.save(doc); var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc"); TEquals("testdoc", xhr.responseText, "should return 'testdoc'"); db.deleteDoc(doc); var xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/show-deleted/testdoc"); TEquals("No doc testdoc", xhr.responseText, "should return 'no doc testdoc'"); run_on_modified_server( [{section: "httpd", key: "authentication_handlers", value: "{couch_httpd_auth, special_test_authentication_handler}"}, {section:"httpd", key: "WWW-Authenticate", value: "X-Couch-Test-Auth"}], function() { T(db.setDbProperty("_security", {foo: true}).ok); T(db.save(doc).ok); xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/secObj"); var resp = JSON.parse(xhr.responseText); T(resp.foo == true); } ); };