summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorJay Doane <jaydoane@apache.org>2023-02-14 14:51:20 -0800
committerJay Doane <jay.s.doane@gmail.com>2023-02-15 20:17:49 -0800
commit395c083026f9735a2a34cda3012151ef94466136 (patch)
treee5a2caca26e17ef3f00efe3917f181bda78151b4 /test
parent97fdac3af36c378a3fe634942c3ac76e785f16de (diff)
downloadcouchdb-395c083026f9735a2a34cda3012151ef94466136.tar.gz
Move elixir search tests to be with other elixir tests
Now that dreyfus is integrated into CouchDB, its elixir tests can be moved to be with the other elixir integration tests. As a bonus, remove some unnecessary configuration.
Diffstat (limited to 'test')
-rw-r--r--test/elixir/test/config/search.elixir32
-rw-r--r--test/elixir/test/config/test-config.ini3
-rw-r--r--test/elixir/test/partition_search_test.exs247
-rw-r--r--test/elixir/test/search_test.exs226
4 files changed, 508 insertions, 0 deletions
diff --git a/test/elixir/test/config/search.elixir b/test/elixir/test/config/search.elixir
new file mode 100644
index 000000000..12450865d
--- /dev/null
+++ b/test/elixir/test/config/search.elixir
@@ -0,0 +1,32 @@
+%{
+ "PartitionSearchTest": [
+ "Cannot do global query with partition view",
+ "Cannot do partition query with global search ddoc",
+ "Only returns docs in partition not those in shard",
+ "Simple query returns partitioned search results",
+ "Works with bookmarks and limit",
+ "Works with limit using POST for on non-partitioned db",
+ "Works with limit using POST for partitioned db",
+ "normal search on non-partitioned dbs still work",
+ "normal search on non-partitioned dbs with limit",
+ "normal search on non-partitioned dbs with over limit",
+ "normal search on non-partitioned dbs without limit",
+ "rejects conflicting partition values",
+ "restricted parameters are not allowed in query or body"
+ ],
+ "SearchTest": [
+ "clean up search index with invalid design document",
+ "drilldown multiple keys multiple values for POST",
+ "drilldown multiple keys single values for GET",
+ "drilldown multiple keys single values for POST",
+ "drilldown multiple query definitions for GET",
+ "drilldown multiple query definitions for POST",
+ "drilldown single key multiple values for GET",
+ "drilldown single key multiple values for POST",
+ "drilldown single key single value for GET",
+ "drilldown single key single value for POST",
+ "drilldown three keys single values for POST",
+ "search returns all items for GET",
+ "search returns all items for POST"
+ ]
+}
diff --git a/test/elixir/test/config/test-config.ini b/test/elixir/test/config/test-config.ini
index 1980139d1..190067643 100644
--- a/test/elixir/test/config/test-config.ini
+++ b/test/elixir/test/config/test-config.ini
@@ -1,2 +1,5 @@
[chttpd]
authentication_handlers = {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler}
+
+[dreyfus]
+name = clouseau@127.0.0.1
diff --git a/test/elixir/test/partition_search_test.exs b/test/elixir/test/partition_search_test.exs
new file mode 100644
index 000000000..121995449
--- /dev/null
+++ b/test/elixir/test/partition_search_test.exs
@@ -0,0 +1,247 @@
+defmodule PartitionSearchTest do
+ use CouchTestCase
+
+ @moduletag :search
+
+ @moduledoc """
+ Test Partition functionality with search
+ """
+
+ def create_search_docs(db_name, pk1 \\ "foo", pk2 \\ "bar") do
+ docs = for i <- 1..10 do
+ id = if rem(i, 2) == 0 do
+ "#{pk1}:#{i}"
+ else
+ "#{pk2}:#{i}"
+ end
+ %{
+ :_id => id,
+ :value => i,
+ :some => "field"
+ }
+ end
+
+ resp = Couch.post("/#{db_name}/_bulk_docs", headers: ["Content-Type": "application/json"], body: %{:docs => docs}, query: %{w: 3})
+ assert resp.status_code in [201, 202]
+ end
+
+ def create_ddoc(db_name, opts \\ %{}) do
+ index_fn = "function(doc) {\n if (doc.some) {\n index('some', doc.some);\n }\n}"
+ default_ddoc = %{
+ indexes: %{
+ books: %{
+ analyzer: %{name: "standard"},
+ index: index_fn
+ }
+ }
+ }
+
+ ddoc = Enum.into(opts, default_ddoc)
+
+ resp = Couch.put("/#{db_name}/_design/library", body: ddoc)
+ assert resp.status_code in [201, 202]
+ assert Map.has_key?(resp.body, "ok") == true
+ end
+
+ def get_ids (resp) do
+ %{:body => %{"rows" => rows}} = resp
+ Enum.map(rows, fn row -> row["id"] end)
+ end
+
+ @tag :with_partitioned_db
+ test "Simple query returns partitioned search results", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["foo:10", "foo:2", "foo:4", "foo:6", "foo:8"]
+
+ url = "/#{db_name}/_partition/bar/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["bar:1", "bar:3", "bar:5", "bar:7", "bar:9"]
+ end
+
+ @tag :with_partitioned_db
+ test "Only returns docs in partition not those in shard", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name, "foo", "bar42")
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["foo:10", "foo:2", "foo:4", "foo:6", "foo:8"]
+ end
+
+ @tag :with_partitioned_db
+ test "Works with bookmarks and limit", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field", limit: 3})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["foo:10", "foo:2", "foo:4"]
+
+ %{:body => %{"bookmark" => bookmark}} = resp
+
+ resp = Couch.get(url, query: %{q: "some:field", limit: 3, bookmark: bookmark})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["foo:6", "foo:8"]
+
+ resp = Couch.get(url, query: %{q: "some:field", limit: 2000, bookmark: bookmark})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert ids == ["foo:6", "foo:8"]
+
+ resp = Couch.get(url, query: %{q: "some:field", limit: 2001, bookmark: bookmark})
+ assert resp.status_code == 400
+ end
+
+ @tag :with_db
+ test "Works with limit using POST for on non-partitioned db", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.post(url, body: %{:q => "some:field", :limit => 1})
+ assert resp.status_code == 200
+ end
+
+ @tag :with_partitioned_db
+ test "Works with limit using POST for partitioned db", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.post(url, body: %{:q => "some:field", :limit => 1})
+ assert resp.status_code == 200
+ end
+
+ @tag :with_partitioned_db
+ test "Cannot do global query with partition view", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 400
+ %{:body => %{"reason" => reason}} = resp
+ assert Regex.match?(~r/mandatory for queries to this index./, reason)
+ end
+
+ @tag :with_partitioned_db
+ test "Cannot do partition query with global search ddoc", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name, options: %{partitioned: false})
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 400
+ %{:body => %{"reason" => reason}} = resp
+ assert reason == "`partition` not supported on this index"
+ end
+
+ @tag :with_db
+ test "normal search on non-partitioned dbs still work", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert Enum.sort(ids) == Enum.sort(["bar:1", "bar:5", "bar:9", "foo:2", "bar:3", "foo:4", "foo:6", "bar:7", "foo:8", "foo:10"])
+ end
+
+ @tag :with_db
+ test "normal search on non-partitioned dbs without limit", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field"})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert Enum.sort(ids) == Enum.sort(["bar:1", "bar:5", "bar:9", "foo:2", "bar:3", "foo:4", "foo:6", "bar:7", "foo:8", "foo:10"])
+ end
+
+ @tag :with_db
+ test "normal search on non-partitioned dbs with limit", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field", limit: 3})
+ assert resp.status_code == 200
+ ids = get_ids(resp)
+ assert Enum.sort(ids) == Enum.sort(["bar:1", "bar:5", "bar:9"])
+ end
+
+ @tag :with_db
+ test "normal search on non-partitioned dbs with over limit", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/library/_search/books"
+ resp = Couch.get(url, query: %{q: "some:field", limit: 201})
+ assert resp.status_code == 400
+ end
+
+ @tag :with_partitioned_db
+ test "rejects conflicting partition values", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ resp = Couch.post(url, body: %{q: "some:field", partition: "bar"})
+ assert resp.status_code == 400
+ end
+
+ @tag :with_partitioned_db
+ test "restricted parameters are not allowed in query or body", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ body = %{q: "some:field", partition: "foo"}
+
+ Enum.each(
+ [
+ {:counts, "[\"type\"]"},
+ {:group_field, "some"},
+ {:ranges, :jiffy.encode(%{price: %{cheap: "[0 TO 100]"}})},
+ {:drilldown, "[\"key\",\"a\"]"},
+ ],
+ fn {key, value} ->
+ url = "/#{db_name}/_partition/foo/_design/library/_search/books"
+ bannedparam = Map.put(body, key, value)
+ get_resp = Couch.get(url, query: bannedparam)
+ %{:body => %{"reason" => get_reason}} = get_resp
+ assert Regex.match?(~r/are incompatible/, get_reason)
+ post_resp = Couch.post(url, body: bannedparam)
+ %{:body => %{"reason" => post_reason}} = post_resp
+ assert Regex.match?(~r/are incompatible/, post_reason)
+ end
+ )
+ end
+end
diff --git a/test/elixir/test/search_test.exs b/test/elixir/test/search_test.exs
new file mode 100644
index 000000000..829b3395f
--- /dev/null
+++ b/test/elixir/test/search_test.exs
@@ -0,0 +1,226 @@
+defmodule SearchTest do
+ use CouchTestCase
+
+ @moduletag :search
+
+ @moduledoc """
+ Test search
+ """
+
+ def create_search_docs(db_name) do
+ resp = Couch.post("/#{db_name}/_bulk_docs",
+ headers: ["Content-Type": "application/json"],
+ body: %{:docs => [
+ %{"item" => "apple", "place" => "kitchen", "state" => "new"},
+ %{"item" => "banana", "place" => "kitchen", "state" => "new"},
+ %{"item" => "carrot", "place" => "kitchen", "state" => "old"},
+ %{"item" => "date", "place" => "lobby", "state" => "unknown"},
+ ]}
+ )
+ assert resp.status_code in [201, 202]
+ end
+
+ def create_ddoc(db_name, opts \\ %{}) do
+ default_ddoc = %{
+ indexes: %{
+ fruits: %{
+ analyzer: %{name: "standard"},
+ index: "function (doc) {\n index(\"item\", doc.item, {facet: true});\n index(\"place\", doc.place, {facet: true});\n index(\"state\", doc.state, {facet: true});\n}"
+ }
+ }
+ }
+
+ ddoc = Enum.into(opts, default_ddoc)
+
+ resp = Couch.put("/#{db_name}/_design/inventory", body: ddoc)
+ assert resp.status_code in [201, 202]
+ assert Map.has_key?(resp.body, "ok") == true
+ end
+
+ def create_invalid_ddoc(db_name, opts \\ %{}) do
+ invalid_ddoc = %{
+ :indexes => [
+ %{"name" => "foo", "ddoc" => "bar", "type" => "text"},
+ ]
+ }
+
+ ddoc = Enum.into(opts, invalid_ddoc)
+
+ resp = Couch.put("/#{db_name}/_design/search", body: ddoc)
+ assert resp.status_code in [201, 202]
+ assert Map.has_key?(resp.body, "ok") == true
+ end
+
+ def get_items (resp) do
+ %{:body => %{"rows" => rows}} = resp
+ Enum.map(rows, fn row -> row["doc"]["item"] end)
+ end
+
+ @tag :with_db
+ test "search returns all items for GET", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.get(url, query: %{q: "*:*", include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "carrot", "date"])
+ end
+
+ @tag :with_db
+ test "drilldown single key single value for GET", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.get(url, query: %{q: "*:*", drilldown: :jiffy.encode(["place", "kitchen"]), include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "carrot"])
+ end
+
+ @tag :with_db
+ test "drilldown single key multiple values for GET", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.get(url, query: %{q: "*:*", drilldown: :jiffy.encode(["state", "new", "unknown"]), include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "date"])
+ end
+
+ @tag :with_db
+ test "drilldown multiple keys single values for GET", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.get(url, query: %{q: "*:*", drilldown: :jiffy.encode([["state", "old"], ["item", "apple"]]), include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == []
+ end
+
+ @tag :with_db
+ test "drilldown multiple query definitions for GET", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits?q=*:*&drilldown=[\"state\",\"old\"]&drilldown=[\"item\",\"apple\"]&include_docs=true"
+ resp = Couch.get(url)
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == []
+ end
+
+
+ @tag :with_db
+ test "search returns all items for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{q: "*:*", include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "carrot", "date"])
+ end
+
+ @tag :with_db
+ test "drilldown single key single value for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{query: "*:*", drilldown: ["place", "kitchen"], include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "carrot"])
+ end
+
+ @tag :with_db
+ test "drilldown single key multiple values for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{query: "*:*", drilldown: ["state", "new", "unknown"], include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == Enum.sort(["apple", "banana", "date"])
+ end
+
+ @tag :with_db
+ test "drilldown multiple keys single values for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{q: "*:*", drilldown: [["state", "old"], ["item", "apple"]], include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == []
+ end
+
+ @tag :with_db
+ test "drilldown three keys single values for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{q: "*:*", drilldown: [["place", "kitchen"], ["state", "new"], ["item", "apple"]], include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == ["apple"]
+ end
+
+ @tag :with_db
+ test "drilldown multiple keys multiple values for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: %{q: "*:*", drilldown: [["state", "old", "new"], ["item", "apple"]], include_docs: true})
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == ["apple"]
+ end
+
+ @tag :with_db
+ test "drilldown multiple query definitions for POST", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+
+ url = "/#{db_name}/_design/inventory/_search/fruits"
+ resp = Couch.post(url, body: "{\"include_docs\": true, \"q\": \"*:*\", \"drilldown\": [\"state\", \"old\"], \"drilldown\": [\"item\", \"apple\"]}")
+ assert resp.status_code == 200
+ ids = get_items(resp)
+ assert Enum.sort(ids) == ["apple"]
+ end
+
+ @tag :with_db
+ test "clean up search index with invalid design document", context do
+ db_name = context[:db_name]
+ create_search_docs(db_name)
+ create_ddoc(db_name)
+ create_invalid_ddoc(db_name)
+
+ resp = Couch.post("/#{db_name}/_search_cleanup")
+ assert resp.status_code in [201, 202]
+ end
+end