You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@couchdb.apache.org by GitBox <gi...@apache.org> on 2020/07/23 06:44:22 UTC

[GitHub] [couchdb] jjrodrig opened a new pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

jjrodrig opened a new pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025


   <!-- Thank you for your contribution!
   
        Please file this form by replacing the Markdown comments
        with your text. If a section needs no action - remove it.
   
        Also remember, that CouchDB uses the Review-Then-Commit (RTC) model
        of code collaboration. Positive feedback is represented +1 from committers
        and negative is a -1. The -1 also means veto, and needs to be addressed
        to proceed. Once there are no objections, the PR can be merged by a
        CouchDB committer.
   
        See: http://couchdb.apache.org/bylaws.html#decisions for more info. -->
   
   ## Overview
   This PR ports following tests into Elixir test suite:
   - view_conflicts.js
   - view_errors.js
   - view_include_docs.js
   
   <!-- Please give a short brief for the pull request,
        what problem it solves or how it makes things better. -->
   
   ## Testing recommendations
   
   ```
    make elixir tests=test/elixir/test/view_conflicts_test.exs
    make elixir tests=test/elixir/test/view_errors_test.exs
    make elixir tests=test/elixir/test/view_include_docs_test.exs
   ```
   
   <!-- Describe how we can test your changes.
        Does it provides any behaviour that the end users
        could notice? -->
   
   ## Related Issues or Pull Requests
   N/A
   <!-- If your changes affects multiple components in different
        repositories please put links to those issues or pull requests here.  -->
   
   ## Checklist
   
   - [X] Code is written and works correctly
   - [X] Changes are covered by tests
   - [ ] Any new configurable parameters are documented in `rel/overlay/etc/default.ini`
   - [ ] A PR for documentation changes has been made in https://github.com/apache/couchdb-documentation
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig merged pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig merged pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025






----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#issuecomment-690896823


   Thanks @dottorblaster for the review. 
   I've added the @moduletag kind to these tests


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig merged pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig merged pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig merged pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig merged pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig merged pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig merged pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on a change in pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on a change in pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#discussion_r486793312



##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on a change in pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on a change in pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#discussion_r486793312



##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on a change in pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on a change in pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#discussion_r486793312



##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 

##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Yes, no :error is expected here. It will fail if we get something different. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] dottorblaster commented on a change in pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
dottorblaster commented on a change in pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#discussion_r481209418



##########
File path: test/elixir/test/view_errors_test.exs
##########
@@ -0,0 +1,298 @@
+defmodule ViewErrorsTest do
+  use CouchTestCase
+
+  @document %{integer: 1, string: "1", array: [1, 2, 3]}
+
+  @tag :with_db
+  test "emit undefined key results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit(doc.undef, null);
+    }
+    """
+
+    # emitting a key value that is undefined should result in that row
+    # being included in the view results as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+    assert Enum.at(results["rows"], 0)["key"] == :null
+  end
+
+  @tag :with_db
+  test "exception in map function", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      doc.undef(); // throws an error
+    }
+    """
+
+    # 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
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 0
+  end
+
+  @tag :with_db
+  test "emit undefined value results as null", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    map_fun = """
+    function(doc) {
+      emit([doc._id, doc.undef], null);
+    }
+    """
+
+    # if a view function includes an undefined value in the emitted key or
+    # value, it is treated as null
+    results = query(db_name, map_fun)
+    assert results["total_rows"] == 1
+
+    key =
+      results["rows"]
+      |> Enum.at(0)
+      |> Map.get("key")
+      |> Enum.at(1)
+
+    assert key == :null
+  end
+
+  @tag :with_db
+  test "query view with invalid params", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    body = %{
+      language: "javascript",
+      map: "function(doc){emit(doc.integer)}"
+    }
+
+    # querying a view with invalid params should give a resonable error message
+    resp =
+      Couch.post("/#{db_name}/_all_docs?startkey=foo",
+        headers: ["Content-Type": "application/json"],
+        body: body
+      )
+
+    assert resp.body["error"] == "bad_request"
+
+    resp =
+      Couch.post("/#{db_name}/_all_docs",
+        headers: ["Content-Type": "application/x-www-form-urlencoded"],
+        body: body
+      )
+
+    assert resp.status_code == 415
+  end
+
+  @tag :with_db
+  test "query parse error", context do
+    db_name = context[:db_name]
+
+    map_fun = """
+    function(doc) {
+      emit(doc.integer, doc.integer);
+    }
+    """
+
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp = Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    map_fun = "function() {emit(null, null)}"
+    ddoc_name = create_view(db_name, map_fun)
+
+    resp =
+      Couch.get("/#{db_name}/#{ddoc_name}/_view/view", query: [startkey: 2, endkey: 1])
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+    assert String.contains?(resp.body["reason"], "No rows can match")
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [group_level: 1])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: true])
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp = Couch.get("/#{db_name}/_design/test/_view/no_reduce", query: [reduce: false])
+    assert resp.status_code == 200
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group: true, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+
+    resp =
+      Couch.get("/#{db_name}/_design/test/_view/with_reduce",
+        query: [group_level: 1, reduce: false]
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "query_parse_error"
+  end
+
+  @tag :with_db
+  test "infinite loop", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc3 = %{
+      _id: "_design/infinite",
+      language: "javascript",
+      views: %{
+        infinite_loop: %{
+          map: "function(doc) {while(true){emit(doc,doc);}};"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc3)
+
+    resp = Couch.get("/#{db_name}/_design/infinite/_view/infinite_loop")
+    assert resp.status_code == 500
+    # This test has two different races. The first is whether
+    # the while loop exhausts the JavaScript RAM limits before
+    # timing. The second is a race between which of two timeouts
+    # fires first. The first timeout is the couch_os_process
+    # waiting for data back from couchjs. The second is the
+    # gen_server call to couch_os_process.
+    assert resp.body["error"] == "os_process_error" or resp.body["error"] == "timeout"
+  end
+
+  @tag :with_db
+  test "error responses for invalid multi-get bodies", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      views: %{
+        no_reduce: %{map: "function(doc) {emit(doc._id, null);}"},
+        with_reduce: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce: "function (keys, values) { return sum(values); };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: "[]"
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp =
+      Couch.post("/#{db_name}/_design/test/_view/no_reduce",
+        body: %{keys: 1}
+      )
+
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "`keys` member must be an array."
+  end
+
+  @tag :with_db
+  test "reduce overflow error", context do
+    db_name = context[:db_name]
+    {:ok, _} = create_doc(db_name, @document)
+
+    design_doc2 = %{
+      _id: "_design/testbig",
+      language: "javascript",
+      views: %{
+        reduce_too_big: %{
+          map: "function (doc) {emit(doc.integer, doc.integer)};",
+          reduce:
+            "function (keys, values) { var chars = []; for (var i=0; i < 1000; i++) {chars.push('wazzap');};return chars; };"
+        }
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc2)

Review comment:
       Is this because the test would fail on getting an `:error` tagged tuple?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#issuecomment-690896823






----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [couchdb] jjrodrig commented on pull request #3025: Port view_conflicts.js, view_errors.js and view_include_docs.js into elixir

Posted by GitBox <gi...@apache.org>.
jjrodrig commented on pull request #3025:
URL: https://github.com/apache/couchdb/pull/3025#issuecomment-690896823






----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org