summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuanjo Rodriguez <juanjo@apache.org>2020-09-29 09:49:06 +0200
committerJuanjo Rodriguez <jjrodrig@gmail.com>2020-10-01 14:57:36 +0200
commitfb36cc6cbf73d72308750c4d46f1ae51f5a51b9e (patch)
tree85b6cf9a2c30a6b610ddd97ce69e8b8abc156842
parent1719500d648cfeb396b9c503bbd86fb3dcdca35f (diff)
downloadcouchdb-fb36cc6cbf73d72308750c4d46f1ae51f5a51b9e.tar.gz
Port show_documents and list_views to Elixir
-rw-r--r--test/elixir/README.md4
-rw-r--r--test/elixir/test/list_views_test.exs581
-rw-r--r--test/elixir/test/show_documents_test.exs448
-rw-r--r--test/javascript/tests/list_views.js2
-rw-r--r--test/javascript/tests/show_documents.js2
5 files changed, 1033 insertions, 4 deletions
diff --git a/test/elixir/README.md b/test/elixir/README.md
index 7e19d3964..306322210 100644
--- a/test/elixir/README.md
+++ b/test/elixir/README.md
@@ -54,7 +54,7 @@ X means done, - means partially
- [X] Port invalid_docids.js
- [X] Port jsonp.js
- [X] Port large_docs.js
- - [ ] Port list_views.js
+ - [X] Port list_views.js
- [X] Port lorem_b64.txt
- [X] Port lorem.txt
- [X] Port lots_of_docs.js
@@ -91,7 +91,7 @@ X means done, - means partially
- [X] Port rewrite.js
- [X] Port rewrite_js.js
- [X] Port security_validation.js
- - [ ] Port show_documents.js
+ - [X] Port show_documents.js
- [ ] Port stats.js
- [X] Port update_documents.js
- [X] Port users_db.js
diff --git a/test/elixir/test/list_views_test.exs b/test/elixir/test/list_views_test.exs
new file mode 100644
index 000000000..8e6314dfb
--- /dev/null
+++ b/test/elixir/test/list_views_test.exs
@@ -0,0 +1,581 @@
+defmodule ListViewsTest do
+ use CouchTestCase
+
+ @moduletag kind: :single_node
+
+ @ddoc %{
+ _id: "_design/lists",
+ language: "javascript",
+ views: %{
+ basicView: %{
+ map: """
+ function(doc) {
+ emit(doc.integer, doc.string);
+ }
+ """
+ },
+ withReduce: %{
+ map: """
+ function(doc) {
+ emit(doc.integer, doc.string);
+ }
+ """,
+ reduce: """
+ function(keys, values, rereduce) {
+ if (rereduce) {
+ return sum(values);
+ } else {
+ return values.length;
+ }
+ }
+ """
+ }
+ },
+ lists: %{
+ basicBasic: """
+ function(head, req) {
+ send("head");
+ var row;
+ while(row = getRow()) {
+ send(row.key);
+ };
+ return "tail";
+ }
+ """,
+ basicJSON: """
+ function(head, req) {
+ start({"headers":{"Content-Type" : "application/json"}});
+ send('{"head":'+toJSON(head)+', ');
+ send('"req":'+toJSON(req)+', ');
+ send('"rows":[');
+ var row, sep = '';
+ while (row = getRow()) {
+ send(sep + toJSON(row));
+ sep = ', ';
+ }
+ return "]}";
+ }
+ """,
+ simpleForm: """
+ function(head, req) {
+ send('<ul>');
+ var row, row_number = 0, prevKey, firstKey = null;
+ while (row = getRow()) {
+ row_number += 1;
+ if (!firstKey) firstKey = row.key;
+ prevKey = row.key;
+ send('\\n<li>Key: '+row.key
+ +' Value: '+row.value
+ +' LineNo: '+row_number+'</li>');
+ }
+ return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
+ }
+ """,
+ acceptSwitch: """
+ function(head, req) {
+ // respondWith takes care of setting the proper headers
+ provides("html", function() {
+ send("HTML <ul>");
+
+ var row, num = 0;
+ while (row = getRow()) {
+ num ++;
+ send('\\n<li>Key: '
+ +row.key+' Value: '+row.value
+ +' LineNo: '+num+'</li>');
+ }
+
+ // tail
+ return '</ul>';
+ });
+ }
+ """,
+ qsParams: """
+ function(head, req) {
+ return toJSON(req.query) + "\\n";
+ }
+ """,
+ stopIter: """
+ function(req) {
+ send("head");
+ var row, row_number = 0;
+ while(row = getRow()) {
+ if(row_number > 2) break;
+ send(" " + row_number);
+ row_number += 1;
+ };
+ return " tail";
+ }
+ """,
+ stopIter2: """
+ function(head, req) {
+ provides("html", function() {
+ send("head");
+ var row, row_number = 0;
+ while(row = getRow()) {
+ if(row_number > 2) break;
+ send(" " + row_number);
+ row_number += 1;
+ };
+ return " tail";
+ });
+ }
+ """,
+ tooManyGetRows: """
+ function() {
+ send("head");
+ var row;
+ while(row = getRow()) {
+ send(row.key);
+ };
+ getRow();
+ getRow();
+ getRow();
+ row = getRow();
+ return "after row: "+toJSON(row);
+ }
+ """,
+ emptyList: """
+ function() {
+ return " ";
+ }
+ """,
+ rowError: """
+ function(head, req) {
+ send("head");
+ var row = getRow();
+ send(fooBarBam); // intentional error
+ return "tail";
+ }
+ """,
+ docReference: """
+ function(head, req) {
+ send("head");
+ var row = getRow();
+ send(row.doc.integer);
+ return "tail";
+ }
+ """,
+ secObj: """
+ function(head, req) {
+ return toJSON(req.secObj);
+ }
+ """,
+ setHeaderAfterGotRow: """
+ function(head, req) {
+ getRow();
+ start({
+ code: 400,
+ headers: {
+ "X-My-Header": "MyHeader"
+ }
+ });
+ send("bad request");
+ }
+ """,
+ allDocs: """
+ function(head, req){
+ start({'headers': {'Content-Type': 'application/json'}});
+ var resp = head;
+ var rows = [];
+ while(row=getRow()){
+ rows.push(row);
+ }
+ resp.rows = rows;
+ return toJSON(resp);
+ }
+ """
+ }
+ }
+
+ @view_only_design_doc %{
+ _id: "_design/views",
+ language: "javascript",
+ views: %{
+ basicView: %{
+ map: """
+ function(doc) {
+ emit(-doc.integer, doc.string);
+ }
+ """
+ }
+ }
+ }
+
+ @erl_list_doc %{
+ _id: "_design/erlang",
+ language: "erlang",
+ lists: %{
+ simple: """
+ fun(Head, {Req}) ->
+ Send(<<"[">>),
+ Fun = fun({Row}, Sep) ->
+ Val = couch_util:get_value(<<"key">>, Row, 23),
+ Send(list_to_binary(Sep ++ integer_to_list(Val))),
+ {ok, ","}
+ end,
+ {ok, _} = FoldRows(Fun, ""),
+ Send(<<"]">>)
+ end.
+ """
+ }
+ }
+
+ setup_all do
+ db_name = random_db_name()
+ {:ok, _} = create_db(db_name)
+ on_exit(fn -> delete_db(db_name) end)
+
+ {:ok, _} = create_doc(db_name, @ddoc)
+ bulk_save(db_name, make_docs(0..9))
+
+ # Check setup
+ resp = view(db_name, "lists/basicView")
+ assert resp.body["total_rows"] == 10
+
+ db_name_cross = "#{db_name}_cross"
+ {:ok, _} = create_db(db_name_cross)
+ on_exit(fn -> delete_db(db_name_cross) end)
+
+ {:ok, _} = create_doc(db_name_cross, @ddoc)
+ {:ok, _} = create_doc(db_name_cross, @view_only_design_doc)
+ bulk_save(db_name_cross, make_docs(0..9))
+
+ db_name_erlang = "#{db_name}_erlang"
+ {:ok, _} = create_db(db_name_erlang)
+ on_exit(fn -> delete_db(db_name_erlang) end)
+
+ {:ok, _} = create_doc(db_name_erlang, @erl_list_doc)
+ {:ok, _} = create_doc(db_name_erlang, @view_only_design_doc)
+ bulk_save(db_name_erlang, make_docs(0..9))
+
+ {:ok,
+ [db_name: db_name, db_name_cross: db_name_cross, db_name_erlang: db_name_erlang]}
+ end
+
+ test "standard GET", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/basicBasic/basicView")
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/head0123456789tail/)
+ end
+
+ test "standard OPTIONS", context do
+ db_name = context[:db_name]
+ resp = Rawresp.options("/#{db_name}/_design/lists/_list/basicBasic/basicView")
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/head0123456789tail/)
+ end
+
+ test "the richness of the arguments", context do
+ db_name = context[:db_name]
+
+ resp =
+ Couch.get("/#{db_name}/_design/lists/_list/basicJSON/basicView?update_seq=true")
+
+ assert resp.status_code == 200
+ assert resp.body["head"]["total_rows"] == 10
+ assert resp.body["head"]["offset"] == 0
+ assert length(resp.body["rows"]) == 10
+ assert Enum.at(resp.body["rows"], 0) == %{"id" => "0", "key" => 0, "value" => "0"}
+ assert resp.body["req"]["info"]["db_name"] == db_name
+ assert resp.body["req"]["method"] == "GET"
+
+ assert resp.body["req"]["path"] == [
+ db_name,
+ "_design",
+ "lists",
+ "_list",
+ "basicJSON",
+ "basicView"
+ ]
+
+ assert Map.has_key?(resp.body["req"]["headers"], "Host") == true
+ assert Map.has_key?(resp.body["req"]["headers"], "User-Agent") == true
+ assert Map.has_key?(resp.body["req"], "cookie")
+
+ assert resp.body["req"]["raw_path"] ==
+ "/#{db_name}/_design/lists/_list/basicJSON/basicView?update_seq=true"
+ end
+
+ test "get with query params", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get(
+ "/#{db_name}/_design/lists/_list/simpleForm/basicView?startkey=3&endkey=8"
+ )
+
+ assert resp.status_code == 200
+ assert not String.match?(resp.body, ~r/Key: 1/)
+ assert String.match?(resp.body, ~r/FirstKey: 3/)
+ assert String.match?(resp.body, ~r/LastKey: 8/)
+ end
+
+ test "with 0 rows", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/simpleForm/basicView?startkey=30")
+
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/<\/ul>/)
+ end
+
+ test "too many Get Rows", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/tooManyGetRows/basicView")
+
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/9after row: null/)
+ end
+
+ test "reduce with 0 rows", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/simpleForm/withReduce?startkey=30")
+
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/LastKey: undefined/)
+ end
+
+ test "when there is a reduce present, but not used", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/simpleForm/withReduce?reduce=false")
+
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/Key: 1/)
+ end
+
+ test "when there is a reduce present, and used", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/simpleForm/withReduce?group=true")
+
+ assert resp.status_code == 200
+ assert String.match?(resp.body, ~r/Key: 1/)
+ end
+
+ test "empty list", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/emptyList/basicView")
+ assert String.match?(resp.body, ~r/^ $/)
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/emptyList/withReduce?group=true")
+ assert String.match?(resp.body, ~r/^ $/)
+ end
+
+ test "multi-key fetch with POST", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.post("/#{db_name}/_design/lists/_list/simpleForm/basicView",
+ body: %{keys: [2, 4, 5, 7]}
+ )
+
+ assert resp.status_code == 200
+ assert not String.match?(resp.body, ~r/Key: 1/)
+ assert String.match?(resp.body, ~r/Key: 2/)
+ assert String.match?(resp.body, ~r/FirstKey: 2/)
+ assert String.match?(resp.body, ~r/LastKey: 7/)
+ end
+
+ test "multi-key fetch with GET", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/simpleForm/basicView?keys=[2,4,5,7]")
+
+ assert resp.status_code == 200
+ assert not String.match?(resp.body, ~r/Key: 1/)
+ assert String.match?(resp.body, ~r/Key: 2/)
+ assert String.match?(resp.body, ~r/FirstKey: 2/)
+ assert String.match?(resp.body, ~r/LastKey: 7/)
+ end
+
+ test "no multi-key fetch allowed when group=false", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.post("/#{db_name}/_design/lists/_list/simpleForm/withReduce?group=false",
+ body: %{keys: [2, 4, 5, 7]}
+ )
+
+ assert resp.status_code == 400
+ assert String.match?(resp.body, ~r/query_parse_error/)
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/rowError/basicView")
+ assert String.match?(resp.body, ~r/ReferenceError/)
+ end
+
+ test "with include_docs and a reference to the doc", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get(
+ "/#{db_name}/_design/lists/_list/docReference/basicView?include_docs=true"
+ )
+
+ assert String.match?(resp.body, ~r/head0tail/)
+ end
+
+ test "extra qs params", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/qsParams/basicView?foo=blam")
+ assert String.match?(resp.body, ~r/blam/)
+ end
+
+ test "stop iteration", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/stopIter/basicView")
+ assert String.match?(resp.body, ~r/^head 0 1 2 tail$/)
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/stopIter2/basicView",
+ headers: [Accept: "text/html"]
+ )
+
+ assert String.match?(resp.body, ~r/^head 0 1 2 tail$/)
+ end
+
+ test "abort iteration with reduce", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/stopIter/withReduce?group=true")
+ assert String.match?(resp.body, ~r/^head 0 1 2 tail$/)
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/stopIter2/withReduce?group=true",
+ headers: [Accept: "text/html"]
+ )
+
+ assert String.match?(resp.body, ~r/^head 0 1 2 tail$/)
+ end
+
+ test "with accept headers for HTML", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/lists/_list/acceptSwitch/basicView",
+ headers: [Accept: "text/html"]
+ )
+
+ assert resp.headers["Content-Type"] == "text/html; charset=utf-8"
+ assert String.match?(resp.body, ~r/HTML/)
+ assert String.match?(resp.body, ~r/Value/)
+ end
+
+ test "we can run lists and views from separate docs", context do
+ db_name = context[:db_name_cross]
+
+ resp =
+ Rawresp.get(
+ "/#{db_name}/_design/lists/_list/simpleForm/views/basicView?startkey=-3"
+ )
+
+ assert resp.status_code == 200
+ assert not String.match?(resp.body, ~r/Key: -4/)
+ assert String.match?(resp.body, ~r/FirstKey: -3/)
+ assert String.match?(resp.body, ~r/LastKey: 0/)
+ end
+
+ test "we do multi-key requests on lists and views in separate docs", context do
+ db_name = context[:db_name_cross]
+
+ resp =
+ Rawresp.post(
+ "/#{db_name}/_design/lists/_list/simpleForm/views/basicView",
+ body: %{keys: [-2, -4, -5, -7]}
+ )
+
+ assert resp.status_code == 200
+ assert not String.match?(resp.body, ~r/Key: -3/)
+ assert String.match?(resp.body, ~r/Key: -7/)
+ assert String.match?(resp.body, ~r/FirstKey: -2/)
+ assert String.match?(resp.body, ~r/LastKey: -7/)
+ end
+
+ test "secObj is available", context do
+ db_name = context[:db_name]
+
+ resp = Couch.get("/#{db_name}/_design/lists/_list/secObj/basicView")
+ assert resp.status_code == 200
+ assert is_map(resp.body)
+ end
+
+ test "multiple languages in design docs", context do
+ db_name = context[:db_name_erlang]
+
+ resp =
+ Couch.get("/#{db_name}/_design/erlang/_list/simple/views/basicView?startkey=-3")
+
+ assert resp.status_code == 200
+ assert length(resp.body) == 4
+
+ for i <- 0..3 do
+ assert Enum.at(resp.body, i) + 3 == i
+ end
+ end
+
+ @tag :with_db
+ test "COUCHDB-1113", context do
+ db_name = context[:db_name]
+
+ ddoc = %{
+ _id: "_design/test",
+ views: %{
+ me: %{
+ map: "function(doc) { emit(null,null)}"
+ }
+ },
+ lists: %{
+ you: """
+ function(head, req) {
+ var row;
+ while(row = getRow()) {
+ send(row);
+ }
+ }
+ """
+ }
+ }
+
+ {:ok, _} = create_doc(db_name, ddoc)
+
+ resp =
+ Couch.get("/#{db_name}/_design/test/_list/you/me",
+ headers: [
+ "Content-Type": "application/x-www-form-urlencoded"
+ ]
+ )
+
+ assert resp.status_code == 200
+ end
+
+ test "HTTP header response set after getRow() called in _list function", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/lists/_list/setHeaderAfterGotRow/basicView")
+ assert resp.status_code == 400
+ assert resp.headers["X-My-Header"] == "MyHeader"
+ assert String.match?(resp.body, ~r/^bad request$/)
+ end
+
+ test "handling _all_docs by _list functions. the result should be equal", context do
+ db_name = context[:db_name]
+
+ resp_list = Couch.get("/#{db_name}/_design/lists/_list/allDocs/_all_docs")
+ assert resp_list.status_code == 200
+
+ resp_alldocs = Couch.get("/#{db_name}/_all_docs")
+
+ assert resp_list.body["total_rows"] == resp_alldocs.body["total_rows"]
+ assert resp_list.body["offset"] == resp_alldocs.body["offset"]
+ assert length(resp_list.body["rows"]) == length(resp_alldocs.body["rows"])
+ assert resp_list.body["rows"] == resp_alldocs.body["rows"]
+ end
+end
diff --git a/test/elixir/test/show_documents_test.exs b/test/elixir/test/show_documents_test.exs
new file mode 100644
index 000000000..a574c72b1
--- /dev/null
+++ b/test/elixir/test/show_documents_test.exs
@@ -0,0 +1,448 @@
+defmodule ShowDocumentsTest do
+ use CouchTestCase
+
+ @moduletag kind: :single_node
+
+ @ddoc %{
+ _id: "_design/template",
+ language: "javascript",
+ shows: %{
+ hello: """
+ function(doc, req) {
+ if (doc) {
+ return "Hello World";
+ } else {
+ if(req.id) {
+ return "New World";
+ } else {
+ return "Empty World";
+ }
+ }
+ }
+ """,
+ "just-name": """
+ function(doc, req) {
+ if (doc) {
+ return {
+ body : "Just " + doc.name
+ };
+ } else {
+ return {
+ body : "No such doc",
+ code : 404
+ };
+ }
+ }
+ """,
+ json: """
+ function(doc, req) {
+ return {
+ json : doc
+ }
+ }
+ """,
+ "req-info": """
+ function(doc, req) {
+ return {
+ json : req
+ }
+ }
+ """,
+ "show-deleted": """
+ function(doc, req) {
+ if(doc) {
+ return doc._id;
+ } else {
+ return "No doc " + req.id;
+ }
+ }
+ """,
+ "render-error": """
+ function(doc, req) {
+ return noSuchVariable;
+ }
+ """,
+ empty: """
+ function(doc, req) {
+ return "";
+ }
+ """,
+ fail: """
+ function(doc, req) {
+ return doc._id;
+ }
+ """,
+ "no-set-etag": """
+ function(doc, req) {
+ return {
+ headers : {
+ "Etag" : "skipped"
+ },
+ "body" : "something"
+ }
+ }
+ """,
+ "list-api": """
+ function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey");
+ }
+ """,
+ "list-api-provides": """
+ function(doc, req) {
+ provides("text", function(){
+ send("foo, ");
+ send("bar, ");
+ send("baz!");
+ })
+ }
+ """,
+ "list-api-provides-and-return": """
+ function(doc, req) {
+ provides("text", function(){
+ send("4, ");
+ send("5, ");
+ send("6, ");
+ return "7!";
+ })
+ send("1, ");
+ send("2, ");
+ return "3, ";
+ }
+ """,
+ "list-api-mix": """
+ function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey ");
+ return "Dude";
+ }
+ """,
+ "list-api-mix-with-header": """
+ function(doc, req) {
+ start({"X-Couch-Test-Header": "Yeah"});
+ send("Hey ");
+ return {
+ headers: {
+ "X-Couch-Test-Header-Awesome": "Oh Yeah!"
+ },
+ body: "Dude"
+ };
+ }
+ """,
+ "accept-switch": """
+ 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: """
+ function(doc, req) {
+ registerType("foo", "application/foo","application/x-foo");
+
+ provides("html", function() {
+ return "Ha ha, you said \\"" + doc.word + "\\".";
+ });
+
+ provides("foo", function() {
+ return "foofoo";
+ });
+ }
+ """,
+ withSlash: """
+ function(doc, req) {
+ return { json: doc }
+ }
+ """,
+ secObj: """
+ function(doc, req) {
+ return { json: req.secObj };
+ }
+ """
+ }
+ }
+
+ setup_all do
+ db_name = random_db_name()
+ {:ok, _} = create_db(db_name)
+ on_exit(fn -> delete_db(db_name) end)
+
+ {:ok, _} = create_doc(db_name, @ddoc)
+
+ create_doc(db_name, %{_id: "test-doc-id", word: "plankton", name: "Rusty"})
+
+ {:ok, [db_name: db_name]}
+ end
+
+ test "show error", context do
+ db_name = context[:db_name]
+
+ resp = Couch.get("/#{db_name}/_design/template/_show/")
+ assert resp.status_code == 404
+ assert resp.body["reason"] == "Invalid path."
+ end
+
+ test "show with existing doc", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/hello/test-doc-id")
+ assert resp.body == "Hello World"
+ assert String.match?(resp.headers["Content-Type"], ~r/charset=utf-8/)
+
+ # Fix for COUCHDB-379
+ assert String.match?(resp.headers["Server"], ~r/^CouchDB/)
+ end
+
+ test "show without docid", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/hello")
+ assert resp.body == "Empty World"
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/empty")
+ assert resp.body == ""
+ end
+
+ test "show fail with non-existing docid", context do
+ db_name = context[:db_name]
+ resp = Couch.get("/#{db_name}/_design/template/_show/fail/nonExistingDoc")
+ assert resp.status_code == 404
+ assert resp.body["error"] == "not_found"
+ end
+
+ test "show with doc", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/just-name/test-doc-id")
+ assert resp.body == "Just Rusty"
+ end
+
+ test "show with missing doc", context do
+ db_name = context[:db_name]
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/just-name/missingdoc")
+ assert resp.status_code == 404
+ assert resp.body == "No such doc"
+ end
+
+ test "missing design doc", context do
+ db_name = context[:db_name]
+ resp = Couch.get("/#{db_name}/_design/missingddoc/_show/just-name/test-doc-id")
+ assert resp.status_code == 404
+ assert resp.body["error"] == "not_found"
+ end
+
+ test "show query parameters", context do
+ db_name = context[:db_name]
+
+ resp =
+ Couch.get("/#{db_name}/_design/template/_show/req-info/test-doc-id?foo=bar",
+ headers: [Accept: "text/html;text/plain;*/*", "X-Foo": "bar"]
+ )
+
+ assert resp.body["headers"]["X-Foo"] == "bar"
+ assert resp.body["query"] == %{"foo" => "bar"}
+ assert resp.body["method"] == "GET"
+ assert Enum.at(resp.body["path"], 5) == "test-doc-id"
+ assert resp.body["info"]["db_name"] == db_name
+ end
+
+ test "accept header switching - different mime has different etag", context do
+ db_name = context[:db_name]
+
+ resp =
+ Couch.get("/#{db_name}/_design/template/_show/accept-switch/test-doc-id",
+ headers: [Accept: "text/html;text/plain;*/*"]
+ )
+
+ assert String.match?(resp.headers["Content-Type"], ~r/text\/html/)
+ assert resp.headers["Vary"] == "Accept"
+
+ etag = resp.headers["etag"]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/template/_show/accept-switch/test-doc-id",
+ headers: [Accept: "image/png;*/*"]
+ )
+
+ assert String.match?(resp.body, ~r/PNG/)
+ assert resp.headers["Content-Type"] == "image/png"
+
+ etag2 = resp.headers["etag"]
+
+ assert etag != etag2
+ end
+
+ test "show with doc - etags", context do
+ db_name = context[:db_name]
+
+ doc = %{"_id" => "test-doc-id2", word: "plankton", name: "Rusty"}
+ doc = save(db_name, doc)
+
+ resp = Couch.get("/#{db_name}/_design/template/_show/just-name/test-doc-id2")
+
+ etag = resp.headers["etag"]
+
+ resp =
+ Couch.get("/#{db_name}/_design/template/_show/just-name/test-doc-id2",
+ headers: ["if-none-match": etag]
+ )
+
+ assert resp.status_code == 304
+
+ doc = Map.put(doc, "name", "Crusty")
+ save(db_name, doc)
+
+ resp =
+ Couch.get("/#{db_name}/_design/template/_show/just-name/test-doc-id2",
+ headers: ["if-none-match": etag]
+ )
+
+ assert resp.status_code == 200
+ end
+
+ test "JS can't set etag", context do
+ db_name = context[:db_name]
+
+ resp = Couch.get("/#{db_name}/_design/template/_show/no-set-etag/test-doc-id")
+ assert resp.headers["etag"] != "skipped"
+ end
+
+ test "the provides mime matcher", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/template/_show/provides/test-doc-id",
+ headers: [Accept: "text/html,application/atom+xml; q=0.9"]
+ )
+
+ assert String.match?(resp.headers["Content-Type"], ~r/text\/html/)
+ assert String.match?(resp.headers["Content-Type"], ~r/charset=utf-8/)
+ assert resp.body == "Ha ha, you said \"plankton\"."
+ end
+
+ test "registering types works", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/template/_show/provides/test-doc-id",
+ headers: [Accept: "application/x-foo"]
+ )
+
+ assert resp.headers["Content-Type"] == "application/x-foo"
+ assert String.match?(resp.body, ~r/foofoo/)
+ end
+
+ test "the provides mime matcher without a match", context do
+ db_name = context[:db_name]
+
+ resp =
+ Couch.get("/#{db_name}/_design/template/_show/provides/test-doc-id",
+ headers: [Accept: "text/monkeys"]
+ )
+
+ assert resp.body["error"] == "not_acceptable"
+ end
+
+ test "id with slash", context do
+ db_name = context[:db_name]
+
+ doc3 = %{"_id" => "a/b/c", "a" => 1}
+ save(db_name, doc3)
+ resp = Couch.get("/#{db_name}/_design/template/_show/withSlash/a/b/c")
+ assert resp.status_code == 200
+ end
+
+ test "show with non-existing docid", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/hello/nonExistingDoc")
+ assert resp.body == "New World"
+ end
+
+ test "list() compatible API", context do
+ db_name = context[:db_name]
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/list-api/foo")
+ assert resp.body == "Hey"
+ assert resp.headers["X-Couch-Test-Header"] == "Yeah"
+ end
+
+ test "list() compatible API with provides function", context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get("/#{db_name}/_design/template/_show/list-api-provides/foo?format=text")
+
+ assert resp.body == "foo, bar, baz!"
+ end
+
+ test "should keep next result order: chunks + return value + provided chunks + provided return value",
+ context do
+ db_name = context[:db_name]
+
+ resp =
+ Rawresp.get(
+ "/#{db_name}/_design/template/_show/list-api-provides-and-return/foo?format=text"
+ )
+
+ assert resp.body == "1, 2, 3, 4, 5, 6, 7!"
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/list-api-mix/foo")
+ assert resp.body == "Hey Dude"
+ assert resp.headers["X-Couch-Test-Header"] == "Yeah"
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/list-api-mix-with-header/foo")
+ assert resp.body == "Hey Dude"
+ assert resp.headers["X-Couch-Test-Header"] == "Yeah"
+ assert resp.headers["X-Couch-Test-Header-Awesome"] == "Oh Yeah!"
+ end
+
+ test "deleted docs", context do
+ db_name = context[:db_name]
+
+ doc = save(db_name, %{"_id" => "testdoc", "foo" => 1})
+
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/show-deleted/testdoc")
+ assert resp.body == "testdoc"
+
+ Couch.delete("/#{db_name}/testdoc?rev=#{doc["_rev"]}")
+ resp = Rawresp.get("/#{db_name}/_design/template/_show/show-deleted/testdoc")
+ assert resp.body == "No doc testdoc"
+ end
+
+ @tag :with_db
+ test "security object", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, @ddoc)
+ {:ok, _} = create_doc(db_name, %{_id: "testdoc", foo: 1})
+
+ Couch.put("/#{db_name}/_security", body: %{foo: true})
+
+ retry_until(fn ->
+ resp = Couch.get("/#{db_name}/_design/template/_show/secObj")
+ assert resp.body["foo"]
+ end)
+ end
+end
diff --git a/test/javascript/tests/list_views.js b/test/javascript/tests/list_views.js
index e255e1546..2d74586fe 100644
--- a/test/javascript/tests/list_views.js
+++ b/test/javascript/tests/list_views.js
@@ -9,7 +9,7 @@
// 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.elixir = true;
couchTests.list_views = function(debug) {
var db_name = get_random_db_name();
diff --git a/test/javascript/tests/show_documents.js b/test/javascript/tests/show_documents.js
index 172a79532..e604f3058 100644
--- a/test/javascript/tests/show_documents.js
+++ b/test/javascript/tests/show_documents.js
@@ -9,7 +9,7 @@
// 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.elixir = true
couchTests.show_documents = function(debug) {
var db_name = get_random_db_name();