You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ju...@apache.org on 2020/09/29 07:37:03 UTC

[couchdb] branch master updated: port rewrite and rewrite_js tests into elixir

This is an automated email from the ASF dual-hosted git repository.

juanjo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 796d714  port rewrite and rewrite_js tests into elixir
796d714 is described below

commit 796d714c3fb5a5fc1cc0ddcd8184ff304084407d
Author: Juanjo Rodriguez <ju...@apache.org>
AuthorDate: Thu Jul 23 08:26:36 2020 +0200

    port rewrite and rewrite_js tests into elixir
---
 test/elixir/README.md                   |   2 +-
 test/elixir/lib/couch_raw.ex            | 105 ++++++++
 test/elixir/test/changes_async_test.exs | 103 --------
 test/elixir/test/rewrite_js_test.exs    | 411 ++++++++++++++++++++++++++++++++
 test/elixir/test/rewrite_test.exs       | 181 +++++++++++++-
 test/javascript/tests/rewrite.js        |   2 +-
 test/javascript/tests/rewrite_js.js     |   3 +-
 7 files changed, 691 insertions(+), 116 deletions(-)

diff --git a/test/elixir/README.md b/test/elixir/README.md
index 52ce45a..7e19d39 100644
--- a/test/elixir/README.md
+++ b/test/elixir/README.md
@@ -89,7 +89,7 @@ X means done, - means partially
   - [ ] Port replicator_db_write_auth.js
   - [X] Port rev_stemming.js
   - [X] Port rewrite.js
-  - [ ] Port rewrite_js.js
+  - [X] Port rewrite_js.js
   - [X] Port security_validation.js
   - [ ] Port show_documents.js
   - [ ] Port stats.js
diff --git a/test/elixir/lib/couch_raw.ex b/test/elixir/lib/couch_raw.ex
new file mode 100644
index 0000000..62a0bbd
--- /dev/null
+++ b/test/elixir/lib/couch_raw.ex
@@ -0,0 +1,105 @@
+defmodule Rawresp do
+  use HTTPotion.Base
+
+  @moduledoc """
+  HTTP client that provides raw response as result
+  """
+  @request_timeout 60_000
+  @inactivity_timeout 55_000
+
+  def process_url("http://" <> _ = url) do
+    url
+  end
+
+  def process_url(url) do
+    base_url = System.get_env("EX_COUCH_URL") || "http://127.0.0.1:15984"
+    base_url <> url
+  end
+
+  def process_request_headers(headers, _body, options) do
+    headers =
+      headers
+      |> Keyword.put(:"User-Agent", "couch-potion")
+
+    headers =
+      if headers[:"Content-Type"] do
+        headers
+      else
+        Keyword.put(headers, :"Content-Type", "application/json")
+      end
+
+    case Keyword.get(options, :cookie) do
+      nil ->
+        headers
+
+      cookie ->
+        Keyword.put(headers, :Cookie, cookie)
+    end
+  end
+
+  def process_options(options) do
+    options
+    |> set_auth_options()
+    |> set_inactivity_timeout()
+    |> set_request_timeout()
+  end
+
+  def process_request_body(body) do
+    if is_map(body) do
+      :jiffy.encode(body)
+    else
+      body
+    end
+  end
+
+  def set_auth_options(options) do
+    if Keyword.get(options, :cookie) == nil do
+      headers = Keyword.get(options, :headers, [])
+
+      if headers[:basic_auth] != nil or headers[:authorization] != nil do
+        options
+      else
+        username = System.get_env("EX_USERNAME") || "adm"
+        password = System.get_env("EX_PASSWORD") || "pass"
+        Keyword.put(options, :basic_auth, {username, password})
+      end
+    else
+      options
+    end
+  end
+
+  def set_inactivity_timeout(options) do
+    Keyword.update(
+      options,
+      :ibrowse,
+      [{:inactivity_timeout, @inactivity_timeout}],
+      fn ibrowse ->
+        Keyword.put_new(ibrowse, :inactivity_timeout, @inactivity_timeout)
+      end
+    )
+  end
+
+  def set_request_timeout(options) do
+    timeout = Application.get_env(:httpotion, :default_timeout, @request_timeout)
+    Keyword.put_new(options, :timeout, timeout)
+  end
+
+  def login(userinfo) do
+    [user, pass] = String.split(userinfo, ":", parts: 2)
+    login(user, pass)
+  end
+
+  def login(user, pass, expect \\ :success) do
+    resp = Couch.post("/_session", body: %{:username => user, :password => pass})
+
+    if expect == :success do
+      true = resp.body["ok"]
+      cookie = resp.headers[:"set-cookie"]
+      [token | _] = String.split(cookie, ";")
+      %Couch.Session{cookie: token}
+    else
+      true = Map.has_key?(resp.body, "error")
+      %Couch.Session{error: resp.body["error"]}
+    end
+  end
+end
diff --git a/test/elixir/test/changes_async_test.exs b/test/elixir/test/changes_async_test.exs
index 36876ae..001c5d5 100644
--- a/test/elixir/test/changes_async_test.exs
+++ b/test/elixir/test/changes_async_test.exs
@@ -441,106 +441,3 @@ defmodule ChangesAsyncTest do
     create_doc(db_name, ddoc)
   end
 end
-
-defmodule Rawresp do
-  use HTTPotion.Base
-
-  @request_timeout 60_000
-  @inactivity_timeout 55_000
-
-  def process_url("http://" <> _ = url) do
-    url
-  end
-
-  def process_url(url) do
-    base_url = System.get_env("EX_COUCH_URL") || "http://127.0.0.1:15984"
-    base_url <> url
-  end
-
-  def process_request_headers(headers, _body, options) do
-    headers =
-      headers
-      |> Keyword.put(:"User-Agent", "couch-potion")
-
-    headers =
-      if headers[:"Content-Type"] do
-        headers
-      else
-        Keyword.put(headers, :"Content-Type", "application/json")
-      end
-
-    case Keyword.get(options, :cookie) do
-      nil ->
-        headers
-
-      cookie ->
-        Keyword.put(headers, :Cookie, cookie)
-    end
-  end
-
-  def process_options(options) do
-    options
-    |> set_auth_options()
-    |> set_inactivity_timeout()
-    |> set_request_timeout()
-  end
-
-  def process_request_body(body) do
-    if is_map(body) do
-      :jiffy.encode(body)
-    else
-      body
-    end
-  end
-
-  def set_auth_options(options) do
-    if Keyword.get(options, :cookie) == nil do
-      headers = Keyword.get(options, :headers, [])
-
-      if headers[:basic_auth] != nil or headers[:authorization] != nil do
-        options
-      else
-        username = System.get_env("EX_USERNAME") || "adm"
-        password = System.get_env("EX_PASSWORD") || "pass"
-        Keyword.put(options, :basic_auth, {username, password})
-      end
-    else
-      options
-    end
-  end
-
-  def set_inactivity_timeout(options) do
-    Keyword.update(
-      options,
-      :ibrowse,
-      [{:inactivity_timeout, @inactivity_timeout}],
-      fn ibrowse ->
-        Keyword.put_new(ibrowse, :inactivity_timeout, @inactivity_timeout)
-      end
-    )
-  end
-
-  def set_request_timeout(options) do
-    timeout = Application.get_env(:httpotion, :default_timeout, @request_timeout)
-    Keyword.put_new(options, :timeout, timeout)
-  end
-
-  def login(userinfo) do
-    [user, pass] = String.split(userinfo, ":", parts: 2)
-    login(user, pass)
-  end
-
-  def login(user, pass, expect \\ :success) do
-    resp = Couch.post("/_session", body: %{:username => user, :password => pass})
-
-    if expect == :success do
-      true = resp.body["ok"]
-      cookie = resp.headers[:"set-cookie"]
-      [token | _] = String.split(cookie, ";")
-      %Couch.Session{cookie: token}
-    else
-      true = Map.has_key?(resp.body, "error")
-      %Couch.Session{error: resp.body["error"]}
-    end
-  end
-end
diff --git a/test/elixir/test/rewrite_js_test.exs b/test/elixir/test/rewrite_js_test.exs
new file mode 100644
index 0000000..a3adb3e
--- /dev/null
+++ b/test/elixir/test/rewrite_js_test.exs
@@ -0,0 +1,411 @@
+defmodule RewriteJSTest do
+  use CouchTestCase
+
+  @moduletag :js_engine
+  @moduletag kind: :single_node
+
+  @moduledoc """
+  Test CouchDB rewrites JS
+  This is a port of the rewrite_js.js suite
+  """
+
+  @ddoc %{
+    _id: "_design/test",
+    language: "javascript",
+    _attachments: %{
+      "foo.txt": %{
+        content_type: "text/plain",
+        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+      }
+    },
+    rewrites: """
+      function(req) {
+      prefix = req.path[4];
+      if (prefix === 'foo') {
+          return 'foo.txt';
+      }
+      if (prefix === 'foo2') {
+          return {path: 'foo.txt', method: 'GET'};
+      }
+      if (prefix === 'hello') {
+          if (req.method != 'PUT') {
+              return
+          }
+          id = req.path[5];
+          return {path: '_update/hello/' + id};
+      }
+      if (prefix === 'welcome') {
+          if (req.path.length == 6){
+              name = req.path[5];
+              return {path: '_show/welcome', query: {'name': name}};
+          }
+          return '_show/welcome';
+      }
+      if (prefix === 'welcome2') {
+          return {path: '_show/welcome', query: {'name': 'user'}};
+      }
+      if (prefix === 'welcome3') {
+          name = req.path[5];
+          if (req.method == 'PUT') {
+              path = '_update/welcome2/' + name;
+          } else if (req.method == 'GET') {
+              path = '_show/welcome2/' + name;
+          } else {
+              return;
+          }
+          return path;
+      }
+      if (prefix === 'welcome4') {
+          return {path: '_show/welcome3',  query: {name: req.path[5]}};
+      }
+      if (prefix === 'welcome5') {
+          rest = req.path.slice(5).join('/');
+          return {path: '_show/' + rest,  query: {name: rest}};
+      }
+      if (prefix === 'basicView') {
+          rest = req.path.slice(5).join('/');
+          return {path: '_view/basicView'};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/basicView') {
+          return {path: '_list/simpleForm/basicView'};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/basicViewFixed') {
+          return {path: '_list/simpleForm/basicView',
+                  query: {startkey: '"3"', endkey: '"8"'}};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/complexView') {
+          return {path: '_list/simpleForm/complexView',
+                  query: {key: JSON.stringify([1,2])}};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/complexView2') {
+          return {path: '_list/simpleForm/complexView',
+                  query: {key: JSON.stringify(['test', {}])}};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/complexView3') {
+          return {path: '_list/simpleForm/complexView',
+                  query: {key: JSON.stringify(['test', ['test', 'essai']])}};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/complexView4') {
+          return {path: '_list/simpleForm/complexView2',
+                  query: {key: JSON.stringify({"c": 1})}};
+      }
+      if (req.path.slice(4).join('/') === 'simpleForm/sendBody1') {
+          return {path:   '_list/simpleForm/complexView2',
+                  method: 'POST',
+                  query:  {limit: '1'},
+                  headers:{'Content-type':'application/json'},
+                  body:  JSON.stringify( {keys: [{"c": 1}]} )};
+      }
+      if (req.path.slice(4).join('/') === '/') {
+          return {path: '_view/basicView'};
+      }
+      if (prefix === 'db') {
+          return {path: '../../' + req.path.slice(5).join('/')};
+      }
+    }
+    """,
+    lists: %{
+      simpleForm: """
+      function(head, req) {
+        send('<ul>');
+        var row, row_number = 0, prevKey, firstKey = null;
+        while (row = getRow()) {
+          row_number += 1;
+          if (!firstKey) firstKey = row.key;
+          prevKey = row.key;
+          send('\\n<li>Key: '+row.key
+               +' Value: '+row.value
+               +' LineNo: '+row_number+'</li>');
+        }
+        return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
+      }
+      """
+    },
+    shows: %{
+      welcome: """
+       function(doc,req) {
+        return "Welcome " + req.query["name"];
+      }
+      """,
+      welcome2: """
+       function(doc, req) {
+        return "Welcome " + doc.name;
+      }
+      """,
+      welcome3: """
+        function(doc,req) {
+        return "Welcome " + req.query["name"];
+      }
+      """
+    },
+    updates: %{
+      hello: """
+       function(doc, req) {
+        if (!doc) {
+          if (req.id) {
+            return [{
+              _id : req.id
+            }, "New World"]
+          }
+          return [null, "Empty World"];
+        }
+        doc.world = "hello";
+        doc.edited_by = req.userCtx;
+        return [doc, "hello doc"];
+      }
+      """,
+      welcome2: """
+       function(doc, req) {
+        if (!doc) {
+          if (req.id) {
+            return [{
+              _id: req.id,
+              name: req.id
+            }, "New World"]
+          }
+          return [null, "Empty World"];
+        }
+        return [doc, "hello doc"];
+      }
+      """
+    },
+    views: %{
+      basicView: %{
+        map: """
+         function(doc) {
+          if (doc.integer) {
+            emit(doc.integer, doc.string);
+          }
+        }
+        """
+      },
+      complexView: %{
+        map: """
+         function(doc) {
+          if (doc.type == "complex") {
+            emit([doc.a, doc.b], doc.string);
+          }
+        }
+        """
+      },
+      complexView2: %{
+        map: """
+         function(doc) {
+          if (doc.type == "complex") {
+            emit(doc.a, doc.string);
+          }
+        }
+        """
+      },
+      complexView3: %{
+        map: """
+         function(doc) {
+          if (doc.type == "complex") {
+            emit(doc.b, doc.string);
+          }
+        }
+        """
+      }
+    }
+  }
+
+  Enum.each(
+    ["test_rewrite_suite_db", "test_rewrite_suite_db%2Fwith_slashes"],
+    fn db_name ->
+      @tag with_random_db: db_name
+      test "Test basic js rewrites on #{db_name}", context do
+        db_name = context[:db_name]
+
+        create_doc(db_name, @ddoc)
+
+        docs1 = make_docs(0..9)
+        bulk_save(db_name, docs1)
+
+        docs2 = [
+          %{"a" => 1, "b" => 1, "string" => "doc 1", "type" => "complex"},
+          %{"a" => 1, "b" => 2, "string" => "doc 2", "type" => "complex"},
+          %{"a" => "test", "b" => %{}, "string" => "doc 3", "type" => "complex"},
+          %{
+            "a" => "test",
+            "b" => ["test", "essai"],
+            "string" => "doc 4",
+            "type" => "complex"
+          },
+          %{"a" => %{"c" => 1}, "b" => "", "string" => "doc 5", "type" => "complex"}
+        ]
+
+        bulk_save(db_name, docs2)
+
+        # Test simple rewriting
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/foo")
+        assert resp.body == "This is a base64 encoded text"
+        assert resp.headers["Content-Type"] == "text/plain"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/foo2")
+        assert resp.body == "This is a base64 encoded text"
+        assert resp.headers["Content-Type"] == "text/plain"
+
+        # Test POST, hello update world
+        resp =
+          Couch.post("/#{db_name}", body: %{"word" => "plankton", "name" => "Rusty"}).body
+
+        assert resp["ok"]
+        doc_id = resp["id"]
+        assert doc_id
+
+        resp = Couch.put("/#{db_name}/_design/test/_rewrite/hello/#{doc_id}")
+        assert resp.status_code in [201, 202]
+        assert resp.body == "hello doc"
+        assert String.match?(resp.headers["Content-Type"], ~r/charset=utf-8/)
+
+        assert Couch.get("/#{db_name}/#{doc_id}").body["world"] == "hello"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome?name=user")
+        assert resp.body == "Welcome user"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome/user")
+        assert resp.body == "Welcome user"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome2")
+        assert resp.body == "Welcome user"
+
+        resp = Couch.put("/#{db_name}/_design/test/_rewrite/welcome3/test")
+        assert resp.status_code in [201, 202]
+        assert resp.body == "New World"
+        assert String.match?(resp.headers["Content-Type"], ~r/charset=utf-8/)
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome3/test")
+        assert resp.body == "Welcome test"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome4/user")
+        assert resp.body == "Welcome user"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/welcome5/welcome3")
+        assert resp.body == "Welcome welcome3"
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/basicView")
+        assert resp.status_code == 200
+        assert resp.body["total_rows"] == 9
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/FirstKey: [1, 2]/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView2")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 3/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView3")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 4/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView4")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 5/)
+
+        # COUCHDB-1612 - send body rewriting get to post
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/sendBody1")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 5 LineNo: 1/)
+
+        resp = Couch.get("/#{db_name}/_design/test/_rewrite/db/_design/test?meta=true")
+        assert resp.status_code == 200
+        assert resp.body["_id"] == "_design/test"
+        assert Map.has_key?(resp.body, "_revs_info")
+      end
+
+      @tag with_random_db: db_name
+      test "early response on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc = %{
+          _id: "_design/response",
+          rewrites: """
+           function(req){
+            status = parseInt(req.query.status);
+            return {code: status,
+                    body: JSON.stringify({"status": status}),
+                    headers: {'x-foo': 'bar', 'Content-Type': 'application/json'}};
+          }
+          """
+        }
+
+        create_doc(db_name, ddoc)
+
+        resp = Couch.get("/#{db_name}/_design/response/_rewrite?status=200")
+        assert resp.status_code == 200
+        assert resp.headers["x-foo"] == "bar"
+        assert resp.body["status"] == 200
+
+        resp = Couch.get("/#{db_name}/_design/response/_rewrite?status=451")
+        assert resp.status_code == 451
+        assert resp.headers["Content-Type"] == "application/json"
+
+        resp = Couch.get("/#{db_name}/_design/response/_rewrite?status=500")
+        assert resp.status_code == 500
+      end
+
+      @tag with_random_db: db_name
+      test "path relative to server on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc = %{
+          _id: "_design/relative",
+          rewrites: """
+           function(req){
+            return '../../../_uuids'
+          }
+          """
+        }
+
+        create_doc(db_name, ddoc)
+        resp = Couch.get("/#{db_name}/_design/relative/_rewrite/uuids")
+        assert resp.status_code == 200
+        assert length(resp.body["uuids"]) == 1
+      end
+
+      @tag with_random_db: db_name
+      test "loop on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc_loop = %{
+          _id: "_design/loop",
+          rewrites: """
+          function(req) {
+            return '_rewrite/loop';
+          }
+          """
+        }
+
+        create_doc(db_name, ddoc_loop)
+        resp = Couch.get("/#{db_name}/_design/loop/_rewrite/loop")
+        assert resp.status_code == 400
+      end
+
+      @tag with_random_db: db_name
+      test "requests with body preserve the query string rewrite on #{db_name}",
+           context do
+        db_name = context[:db_name]
+
+        ddoc_qs = %{
+          _id: "_design/qs",
+          rewrites:
+            "function (r) { return {path: '../../_changes', query: {'filter': '_doc_ids'}};};"
+        }
+
+        create_doc(db_name, ddoc_qs)
+        create_doc(db_name, %{_id: "qs1"})
+        create_doc(db_name, %{_id: "qs2"})
+
+        resp =
+          Couch.post("/#{db_name}/_design/qs/_rewrite",
+            body: %{doc_ids: ["qs2"]}
+          )
+
+        assert resp.status_code == 200
+        assert length(resp.body["results"]) == 1
+        assert Enum.at(resp.body["results"], 0)["id"] == "qs2"
+      end
+    end
+  )
+end
diff --git a/test/elixir/test/rewrite_test.exs b/test/elixir/test/rewrite_test.exs
index daa2a80..75f1985 100644
--- a/test/elixir/test/rewrite_test.exs
+++ b/test/elixir/test/rewrite_test.exs
@@ -349,15 +349,178 @@ defmodule RewriteTest do
         assert resp.status_code == 200
         assert resp.body["total_rows"] == 9
 
-        # TODO: port _list function tests and everything below in rewrite.js
-        # This is currently broken because _list funcitons default to application/json
-        # response bodies and my attempts to change the content-type from within the
-        # _list function have not yet succeeded.
-        #
-        # Test GET with query params
-        # resp = Couch.get("/#{db_name}/_design/test/_rewrite/simpleForm/basicView", query: %{startkey: 3, endkey: 8})
-        # Logger.error("GOT RESP: #{inspect resp.body}")
-        # assert resp.status_code == 200
+        resp =
+          Rawresp.get(
+            "/#{db_name}/_design/test/_rewrite/simpleForm/basicView?startkey=3&endkey=8"
+          )
+
+        assert resp.status_code == 200
+        assert not String.match?(resp.body, ~r/Key: 1/)
+        assert String.match?(resp.body, ~r/FirstKey: 3/)
+        assert String.match?(resp.body, ~r/LastKey: 8/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/basicViewFixed")
+        assert resp.status_code == 200
+        assert not String.match?(resp.body, ~r/Key: 1/)
+        assert String.match?(resp.body, ~r/FirstKey: 3/)
+        assert String.match?(resp.body, ~r/LastKey: 8/)
+
+        resp =
+          Rawresp.get(
+            "/#{db_name}/_design/test/_rewrite/simpleForm/basicViewFixed?startkey=4"
+          )
+
+        assert resp.status_code == 200
+        assert not String.match?(resp.body, ~r/Key: 1/)
+        assert String.match?(resp.body, ~r/FirstKey: 3/)
+        assert String.match?(resp.body, ~r/LastKey: 8/)
+
+        resp =
+          Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/basicViewPath/3/8")
+
+        assert resp.status_code == 200
+        assert not String.match?(resp.body, ~r/Key: 1/)
+        assert String.match?(resp.body, ~r/FirstKey: 3/)
+        assert String.match?(resp.body, ~r/LastKey: 8/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/FirstKey: [1, 2]/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView2")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 3/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView3")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 4/)
+
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/simpleForm/complexView4")
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 5/)
+
+        resp =
+          Rawresp.get(
+            "/#{db_name}/_design/test/_rewrite/simpleForm/complexView5/test/essai"
+          )
+
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 4/)
+
+        resp =
+          Rawresp.get(
+            "/#{db_name}/_design/test/_rewrite/simpleForm/complexView6?a=test&b=essai"
+          )
+
+        assert resp.status_code == 200
+        assert String.match?(resp.body, ~r/Value: doc 4/)
+
+        resp =
+          Rawresp.get(
+            "/#{db_name}/_design/test/_rewrite/simpleForm/complexView7/test/essai?doc=true"
+          )
+
+        assert resp.status_code == 200
+        result = resp.body |> IO.iodata_to_binary() |> :jiffy.decode([:return_maps])
+        first_row = Enum.at(result["rows"], 0)
+        assert Map.has_key?(first_row, "doc")
+
+        # COUCHDB-2031 - path normalization versus qs params
+        resp = Rawresp.get("/#{db_name}/_design/test/_rewrite/db/_design/test?meta=true")
+        assert resp.status_code == 200
+        result = resp.body |> IO.iodata_to_binary() |> :jiffy.decode([:return_maps])
+        assert result["_id"] == "_design/test"
+        assert Map.has_key?(result, "_revs_info")
+
+        ddoc2 = %{
+          _id: "_design/test2",
+          rewrites: [
+            %{
+              from: "uuids",
+              to: "../../../_uuids"
+            }
+          ]
+        }
+
+        create_doc(db_name, ddoc2)
+        resp = Couch.get("/#{db_name}/_design/test2/_rewrite/uuids")
+        assert resp.status_code == 500
+        assert resp.body["error"] == "insecure_rewrite_rule"
+      end
+
+      @tag with_random_db: db_name
+      @tag config: [
+             {"httpd", "secure_rewrites", "false"}
+           ]
+      test "path relative to server on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc = %{
+          _id: "_design/test2",
+          rewrites: [
+            %{
+              from: "uuids",
+              to: "../../../_uuids"
+            }
+          ]
+        }
+
+        create_doc(db_name, ddoc)
+
+        resp = Couch.get("/#{db_name}/_design/test2/_rewrite/uuids")
+        assert resp.status_code == 200
+        assert length(resp.body["uuids"]) == 1
+      end
+
+      @tag with_random_db: db_name
+      @tag config: [
+             {"httpd", "rewrite_limit", "2"}
+           ]
+      test "loop detection on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc_loop = %{
+          _id: "_design/loop",
+          rewrites: [%{from: "loop", to: "_rewrite/loop"}]
+        }
+
+        create_doc(db_name, ddoc_loop)
+
+        resp = Couch.get("/#{db_name}/_design/loop/_rewrite/loop")
+        assert resp.status_code == 400
+      end
+
+      @tag with_random_db: db_name
+      @tag config: [
+             {"httpd", "rewrite_limit", "2"},
+             {"httpd", "secure_rewrites", "false"}
+           ]
+      test "serial execution is not spuriously counted as loop on #{db_name}", context do
+        db_name = context[:db_name]
+
+        ddoc = %{
+          _id: "_design/test",
+          language: "javascript",
+          _attachments: %{
+            "foo.txt": %{
+              content_type: "text/plain",
+              data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+            }
+          },
+          rewrites: [
+            %{
+              from: "foo",
+              to: "foo.txt"
+            }
+          ]
+        }
+
+        create_doc(db_name, ddoc)
+
+        for _i <- 0..4 do
+          resp = Couch.get("/#{db_name}/_design/test/_rewrite/foo")
+          assert resp.status_code == 200
+        end
       end
     end
   )
diff --git a/test/javascript/tests/rewrite.js b/test/javascript/tests/rewrite.js
index a984936..88479b8 100644
--- a/test/javascript/tests/rewrite.js
+++ b/test/javascript/tests/rewrite.js
@@ -10,7 +10,7 @@
 // License for the specific language governing permissions and limitations under
 // the License.
  
- 
+couchTests.elixir = true;
  
 couchTests.rewrite = function(debug) {
   if (debug) debugger;
diff --git a/test/javascript/tests/rewrite_js.js b/test/javascript/tests/rewrite_js.js
index 0ccdd65..7179fc9 100644
--- a/test/javascript/tests/rewrite_js.js
+++ b/test/javascript/tests/rewrite_js.js
@@ -11,7 +11,7 @@
 // the License.
  
  
- 
+couchTests.elixir = true;
 couchTests.rewrite = function(debug) {
   if (debug) debugger;
   var dbNames = [get_random_db_name(), get_random_db_name() + "test_suite_db/with_slashes"];
@@ -116,7 +116,6 @@ couchTests.rewrite = function(debug) {
       }),
       lists: {
         simpleForm: stringFun(function(head, req) {
-          log("simpleForm");
           send('<ul>');
           var row, row_number = 0, prevKey, firstKey = null;
           while (row = getRow()) {