diff options
author | Juanjo Rodriguez <juanjo@apache.org> | 2020-09-29 09:49:06 +0200 |
---|---|---|
committer | Juanjo Rodriguez <jjrodrig@gmail.com> | 2020-10-07 09:13:30 +0200 |
commit | a4bfaaf346406f769588f1b583d6b5d80fcb0dd2 (patch) | |
tree | 0db0ba176cb3ab063eb6fb8bed9724fbc43615b1 | |
parent | 8bea5f257c00db3ddc7f224eefa08ef1da4bfb89 (diff) | |
download | couchdb-a4bfaaf346406f769588f1b583d6b5d80fcb0dd2.tar.gz |
Port show_documents and list_views to Elixir
-rw-r--r-- | test/elixir/README.md | 4 | ||||
-rw-r--r-- | test/elixir/test/list_views_test.exs | 581 | ||||
-rw-r--r-- | test/elixir/test/show_documents_test.exs | 448 | ||||
-rw-r--r-- | test/javascript/tests/list_views.js | 2 | ||||
-rw-r--r-- | test/javascript/tests/show_documents.js | 2 |
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(); |