diff options
author | ILYA Khlopotov <iilyak@apache.org> | 2020-05-11 08:55:08 -0700 |
---|---|---|
committer | ILYA Khlopotov <iilyak@apache.org> | 2020-05-15 11:21:25 -0700 |
commit | af502eae5aab6282ce9509105d69acc9835d529c (patch) | |
tree | 933dc1a432eb90a5c8817bf882eb5704d9b282ad | |
parent | 404174118f9eb38d44543c6e430e938e865cc269 (diff) | |
download | couchdb-af502eae5aab6282ce9509105d69acc9835d529c.tar.gz |
Add tests for legacy API before refactoring
-rw-r--r-- | src/chttpd/test/exunit/pagination_test.exs | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/chttpd/test/exunit/pagination_test.exs b/src/chttpd/test/exunit/pagination_test.exs new file mode 100644 index 000000000..4b12c8b2f --- /dev/null +++ b/src/chttpd/test/exunit/pagination_test.exs @@ -0,0 +1,302 @@ +defmodule Couch.Test.Pagination do + use ExUnit.Case + import Couch.DBTest, only: [retry_until: 1] + alias Couch.DBTest, as: Utils + + defp create_admin(user_name, password) do + hashed = String.to_charlist(:couch_passwords.hash_admin_password(password)) + :config.set('admins', String.to_charlist(user_name), hashed, false) + end + + defp base_url() do + addr = :config.get('chttpd', 'bind_address', '127.0.0.1') + port = :mochiweb_socket_server.get(:chttpd, :port) + "http://#{addr}:#{port}" + end + + setup_all do + test_ctx = + :test_util.start_couch([:chttpd, :couch_jobs, :couch_views, :couch_eval, :couch_js]) + + :ok = create_admin("adm", "pass") + + on_exit(fn -> + :test_util.stop_couch(test_ctx) + end) + + %{ + base_url: base_url(), + user: "adm", + pass: "pass" + } + end + + defp with_session(context) do + session = Couch.login(context.user, context.pass, base_url: context.base_url) + %{session: session} + end + + defp random_db(context) do + db_name = Utils.random_db_name("db") + + on_exit(fn -> + delete_db(context.session, db_name) + end) + + create_db(context.session, db_name) + %{db_name: db_name} + end + + defp with_docs(context) do + assert Map.has_key?(context, :n_docs), "Please define '@describetag n_docs: 10'" + %{docs: create_docs(context.session, context.db_name, 1..context.n_docs)} + end + + defp with_view(context) do + ddoc_id = "simple" + + ddoc = %{ + _id: "_design/#{ddoc_id}", + views: %{ + all: %{ + map: "function(doc) { emit(doc.string, doc) }" + } + } + } + + create_doc(context.session, context.db_name, ddoc) + %{view_name: "all", ddoc_id: ddoc_id} + end + + def create_db(session, db_name, opts \\ []) do + retry_until(fn -> + resp = Couch.Session.put(session, "/#{db_name}", opts) + assert resp.status_code in [201, 202], "got error #{inspect(resp.body)}" + assert resp.body == %{"ok" => true} + {:ok, resp} + end) + end + + defp delete_db(session, db_name) do + retry_until(fn -> + resp = Couch.Session.delete(session, "/#{db_name}") + assert resp.status_code in [200, 202, 404], "got error #{inspect(resp.body)}" + {:ok, resp} + end) + end + + defp create_doc(session, db_name, body) do + {:ok, body} = + retry_until(fn -> + resp = Couch.Session.post(session, "/#{db_name}", body: body) + assert resp.status_code in [201, 202], "got error #{inspect(resp.body)}" + assert resp.body["ok"] + {:ok, resp.body} + end) + + Map.delete(body, "ok") + end + + defp create_docs(session, db_name, range) do + docs = make_docs(range) + + docs + |> Enum.map(fn doc -> + create_doc(session, db_name, doc) + end) + end + + defp docid(id) do + id |> Integer.to_string() |> String.pad_leading(3, "0") + end + + defp make_docs(id_range) do + for id <- id_range do + str_id = docid(id) + %{"_id" => str_id, "integer" => id, "string" => str_id} + end + end + + describe "Legacy API (10 docs)" do + @describetag n_docs: 10 + setup [:with_session, :random_db, :with_docs] + + test ": _all_docs/queries", ctx do + queries = %{ + queries: [%{descending: false}, %{descending: true}] + } + + resp = + Couch.Session.post(ctx.session, "/#{ctx.db_name}/_all_docs/queries", + body: :jiffy.encode(queries) + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + [q1, q2] = resp.body["results"] + assert q1["rows"] == Enum.reverse(q2["rows"]) + end + end + + for descending <- [false, true] do + describe "Legacy API (10 docs) : _all_docs?descending=#{descending}" do + @describetag n_docs: 10 + @describetag descending: descending + setup [:with_session, :random_db, :with_docs] + + test "total_rows matches the length of rows array", ctx do + resp = + Couch.Session.get(ctx.session, "/#{ctx.db_name}/_all_docs", + query: %{descending: ctx.descending} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + body = resp.body + assert body["total_rows"] == length(body["rows"]) + end + + test "the rows are correctly sorted", ctx do + resp = + Couch.Session.get(ctx.session, "/#{ctx.db_name}/_all_docs", + query: %{descending: ctx.descending} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + body = resp.body + ids = Enum.map(body["rows"], fn row -> row["id"] end) + + if ctx.descending do + assert Enum.reverse(Enum.sort(ids)) == ids + else + assert Enum.sort(ids) == ids + end + end + + test "start_key is respected", ctx do + head_pos = 2 + tail_pos = ctx.n_docs - head_pos + doc_ids = Enum.map(ctx.docs, fn doc -> doc["id"] end) + + {start_pos, doc_ids} = + if ctx.descending do + {head_pos, Enum.reverse(Enum.drop(Enum.sort(doc_ids), -tail_pos))} + else + {tail_pos, Enum.drop(Enum.sort(doc_ids), tail_pos - 1)} + end + + start_key = ~s("#{docid(start_pos)}") + + resp = + Couch.Session.get(ctx.session, "/#{ctx.db_name}/_all_docs", + query: %{descending: ctx.descending, start_key: start_key} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + ids = Enum.map(resp.body["rows"], fn row -> row["id"] end) + assert doc_ids == ids + end + + test "end_key is respected", ctx do + head_pos = 2 + tail_pos = ctx.n_docs - head_pos + doc_ids = Enum.map(ctx.docs, fn doc -> doc["id"] end) + + {end_pos, doc_ids} = + if ctx.descending do + {tail_pos, Enum.reverse(Enum.drop(Enum.sort(doc_ids), tail_pos - 1))} + else + {head_pos, Enum.drop(Enum.sort(doc_ids), -tail_pos)} + end + + end_key = ~s("#{docid(end_pos)}") + + resp = + Couch.Session.get(ctx.session, "/#{ctx.db_name}/_all_docs", + query: %{descending: ctx.descending, end_key: end_key} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + ids = Enum.map(resp.body["rows"], fn row -> row["id"] end) + assert doc_ids == ids + end + + test "range between start_key and end_key works", ctx do + head_pos = 2 + slice_size = 3 + doc_ids = Enum.sort(Enum.map(ctx.docs, fn doc -> doc["id"] end)) + # -1 due to 0 based indexing + # -2 is due to 0 based indexing and inclusive end + slice = Enum.slice(doc_ids, (head_pos - 1)..(head_pos + slice_size - 2)) + + {start_key, end_key, doc_ids} = + if ctx.descending do + reversed = Enum.reverse(slice) + [first | _] = reversed + [last | _] = slice + {~s("#{first}"), ~s("#{last}"), reversed} + else + [first | _] = slice + [last | _] = Enum.reverse(slice) + {~s("#{first}"), ~s("#{last}"), slice} + end + + assert length(doc_ids) == slice_size + + resp = + Couch.Session.get(ctx.session, "/#{ctx.db_name}/_all_docs", + query: %{descending: ctx.descending, start_key: start_key, end_key: end_key} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + ids = Enum.map(resp.body["rows"], fn row -> row["id"] end) + assert doc_ids == ids + end + end + end + + describe "Legacy API (10 docs) : /{db}/_design/{ddoc}/_view" do + @describetag n_docs: 10 + @describetag descending: false + @describetag page_size: 4 + setup [:with_session, :random_db, :with_view, :with_docs] + + test "total_rows matches the length of rows array", ctx do + resp = + Couch.Session.get( + ctx.session, + "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}", + query: %{descending: ctx.descending} + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + body = resp.body + assert body["total_rows"] == length(body["rows"]) + end + end + + describe "Legacy API (10 docs) : /{db}/_design/{ddoc}/_view/queries" do + @describetag n_docs: 10 + @describetag page_size: 4 + setup [:with_session, :random_db, :with_view, :with_docs] + + test "descending is respected", ctx do + queries = %{ + queries: [%{descending: false}, %{descending: true}] + } + + resp = + Couch.Session.post( + ctx.session, + "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}/queries", + body: :jiffy.encode(queries) + ) + + assert resp.status_code == 200, "got error #{inspect(resp.body)}" + + [q1, q2] = resp.body["results"] + q1 = Enum.map(q1["rows"], fn row -> row["id"] end) + q2 = Enum.map(q2["rows"], fn row -> row["id"] end) + assert q1 == Enum.reverse(q2) + assert q1 == Enum.sort(q1) + end + end +end |