This is an automated email from the ASF dual-hosted git repository. jan pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit ea16a558d3bd5653eb67b2a899ec7ce8161c4eb2 Author: James Coglan <[email protected]> AuthorDate: Fri Oct 3 14:45:45 2025 +0100 Port a basic _find test to Elixir along with the user_docs fixture it depends on --- test/elixir/test/config/suite.elixir | 3 + test/elixir/test/mango/02_basic_find_test.exs | 28 ++ test/elixir/test/support/mango_database.ex | 70 +++++ test/elixir/test/support/user_docs.ex | 399 ++++++++++++++++++++++++++ 4 files changed, 500 insertions(+) diff --git a/test/elixir/test/config/suite.elixir b/test/elixir/test/config/suite.elixir index 1cbdfccf3..a9d58e823 100644 --- a/test/elixir/test/config/suite.elixir +++ b/test/elixir/test/config/suite.elixir @@ -729,5 +729,8 @@ "Creating-Updating/Deleting doc with overriden quorum should return 201-Created/200-OK", "Creating/Deleting DB should return 202-Acepted", "Creating/Updating/Deleting doc should return 202-Acepted" + ], + "BasicFindTest": [ + "simple find" ] } diff --git a/test/elixir/test/mango/02_basic_find_test.exs b/test/elixir/test/mango/02_basic_find_test.exs new file mode 100644 index 000000000..e6ae1ad64 --- /dev/null +++ b/test/elixir/test/mango/02_basic_find_test.exs @@ -0,0 +1,28 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +defmodule BasicFindTest do + use CouchTestCase + + @db_name "basic-find" + + setup do + UserDocs.setup(@db_name) + end + + test "simple find" do + docs = MangoDatabase.find(@db_name, %{"age" => %{"$lt" => 35}}) + user_ids = Enum.map(docs, fn doc -> doc["user_id"] end) + + assert user_ids == [9, 1, 7] + end +end diff --git a/test/elixir/test/support/mango_database.ex b/test/elixir/test/support/mango_database.ex new file mode 100644 index 000000000..80dcd338c --- /dev/null +++ b/test/elixir/test/support/mango_database.ex @@ -0,0 +1,70 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +defmodule MangoDatabase do + def recreate(db, opts \\ []) do + resp = Couch.get("/#{db}") + if resp.status_code == 200 do + docs = resp.body["doc_count"] + resp.body["doc_del_count"] + if docs > 0 do + delete(db) + create(db, opts) + end + else + create(db, opts) + end + end + + defp create(db, opts) do + partitioned = Keyword.get(opts, :partitioned, false) + Couch.put("/#{db}?partitioned=#{partitioned}") + end + + defp delete(db) do + Couch.delete("/#{db}") + end + + # TODO: make this use batches if necessary + def save_docs(db, docs) do + resp = Couch.post("/#{db}/_bulk_docs", body: %{"docs" => docs}) + end + + def create_index(db, fields, name) do + resp = Couch.post("/#{db}/_index", body: %{ + "index" => %{"fields" => fields}, + "name" => name, + "ddoc" => name, + "type" => "json", + "w" => 3 + }) + end + + def create_text_index(db) do + # TODO + end + + # TODO: port more options from src/mango/test/mango.py `def find(...)` + def find(db, selector, opts \\ []) do + defaults = [use_index: nil, skip: 0, limit: 25, r: 1, conflicts: false] + options = Keyword.merge(defaults, opts) + + resp = Couch.post("/#{db}/_find", body: %{ + "selector" => selector, + "use_index" => options[:use_index], + "skip" => options[:skip], + "limit" => options[:limit], + "r" => options[:r], + "conflicts" => options[:conflicts] + }) + resp.body["docs"] + end +end diff --git a/test/elixir/test/support/user_docs.ex b/test/elixir/test/support/user_docs.ex new file mode 100644 index 000000000..456fd0b81 --- /dev/null +++ b/test/elixir/test/support/user_docs.ex @@ -0,0 +1,399 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +defmodule UserDocs do + @moduledoc """ + Generated with http://www.json-generator.com/ + + With this pattern: + + [ + '{{repeat(20)}}', + { + _id: '{{guid()}}', + user_id: "{{index()}}", + name: { + first: "{{firstName()}}", + last: "{{surname()}}" + }, + age: "{{integer(18,90)}}", + location: { + state: "{{state()}}", + city: "{{city()}}", + address: { + street: "{{street()}}", + number: "{{integer(10, 10000)}}" + } + }, + company: "{{company()}}", + email: "{{email()}}", + manager: "{{bool()}}", + twitter: function(tags) { + if(this.manager) + return; + return "@" + this.email.split("@")[0]; + }, + favorites: [ + "{{repeat(2,5)}}", + "{{random('C', 'C++', 'Python', 'Ruby', 'Erlang', 'Lisp')}}" + ] + } + ] + """ + + @partitions 3 + + @docs [ + %{ + "_id" => "71562648-6acb-42bc-a182-df6b1f005b09", + "user_id" => 0, + "name" => %{"first" => "Stephanie", "last" => "Kirkland"}, + "age" => 48, + "location" => %{ + "state" => "Nevada", + "city" => "Ronco", + "address" => %{"street" => "Evergreen Avenue", "number" => 347}, + }, + "company" => "Dreamia", + "email" => "[email protected]", + "manager" => false, + "twitter" => "@stephaniekirkland", + "favorites" => ["Ruby", "C", "Python"], + "test" => [%{"a" => 1}, %{"b" => 2}], + }, + %{ + "_id" => "12a2800c-4fe2-45a8-8d78-c084f4e242a9", + "user_id" => 1, + "name" => %{"first" => "Abbott", "last" => "Watson"}, + "age" => 31, + "location" => %{ + "state" => "Connecticut", + "city" => "Gerber", + "address" => %{"street" => "Huntington Street", "number" => 8987}, + }, + "company" => "Talkola", + "email" => "[email protected]", + "manager" => false, + "twitter" => "@abbottwatson", + "favorites" => ["Ruby", "Python", "C", %{"Versions" => %{"Alpha" => "Beta"}}], + "test" => [%{"a" => 1, "b" => 2}], + }, + %{ + "_id" => "48ca0455-8bd0-473f-9ae2-459e42e3edd1", + "user_id" => 2, + "name" => %{"first" => "Shelly", "last" => "Ewing"}, + "age" => 42, + "location" => %{ + "state" => "New Mexico", + "city" => "Thornport", + "address" => %{"street" => "Miller Avenue", "number" => 7100}, + }, + "company" => "Zialactic", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["Lisp", "Python", "Erlang"], + "test_in" => %{"val1" => 1, "val2" => "val2"}, + }, + %{ + "_id" => "0461444c-e60a-457d-a4bb-b8d811853f21", + "user_id" => 3, + "name" => %{"first" => "Madelyn", "last" => "Soto"}, + "age" => 79, + "location" => %{ + "state" => "Utah", + "city" => "Albany", + "address" => %{"street" => "Stockholm Street", "number" => 710}, + }, + "company" => "Tasmania", + "email" => "[email protected]", + "manager" => true, + "favorites" => [["Lisp", "Erlang", "Python"], "Erlang", "C", "Erlang"], + "11111" => "number_field", + "22222" => %{"33333" => "nested_number_field"}, + }, + %{ + "_id" => "8e1c90c0-ac18-4832-8081-40d14325bde0", + "user_id" => 4, + "name" => %{"first" => "Nona", "last" => "Horton"}, + "age" => 61, + "location" => %{ + "state" => "Georgia", + "city" => "Corinne", + "address" => %{"street" => "Woodhull Street", "number" => 6845}, + }, + "company" => "Signidyne", + "email" => "[email protected]", + "manager" => false, + "twitter" => "@nonahorton", + "favorites" => ["Lisp", "C", "Ruby", "Ruby"], + "name.first" => "name dot first", + }, + %{ + "_id" => "a33d5457-741a-4dce-a217-3eab28b24e3e", + "user_id" => 5, + "name" => %{"first" => "Sheri", "last" => "Perkins"}, + "age" => 73, + "location" => %{ + "state" => "Michigan", + "city" => "Nutrioso", + "address" => %{"street" => "Bassett Avenue", "number" => 5648}, + }, + "company" => "Myopium", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["Lisp", "Lisp"], + }, + %{ + "_id" => "b31dad3f-ae8b-4f86-8327-dfe8770beb27", + "user_id" => 6, + "name" => %{"first" => "Tate", "last" => "Guy"}, + "age" => 47, + "location" => %{ + "state" => "Illinois", + "city" => "Helen", + "address" => %{"street" => "Schenck Court", "number" => 7392}, + }, + "company" => "Prosely", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["C", "Lisp", "Ruby", "C"], + }, + %{ + "_id" => "659d0430-b1f4-413a-a6b7-9ea1ef071325", + "user_id" => 7, + "name" => %{"first" => "Jewell", "last" => "Stafford"}, + "age" => 33, + "location" => %{ + "state" => "Iowa", + "city" => "Longbranch", + "address" => %{"street" => "Dodworth Street", "number" => 3949}, + }, + "company" => "Niquent", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["C", "C", "Ruby", "Ruby", "Erlang"], + "exists_field" => "should_exist1", + "ordered" => nil, + }, + %{ + "_id" => "6c0afcf1-e57e-421d-a03d-0c0717ebf843", + "user_id" => 8, + "name" => %{"first" => "James", "last" => "Mcdaniel"}, + "age" => 68, + "location" => %{ + "state" => "Maine", + "city" => "Craig", + "address" => %{"street" => "Greene Avenue", "number" => 8776}, + }, + "company" => "Globoil", + "email" => "[email protected]", + "manager" => true, + "favorites" => nil, + "exists_field" => "should_exist2", + "ordered" => false, + }, + %{ + "_id" => "954272af-d5ed-4039-a5eb-8ed57e9def01", + "user_id" => 9, + "name" => %{"first" => "Ramona", "last" => "Floyd"}, + "age" => 22, + "location" => %{ + "state" => "Missouri", + "city" => "Foxworth", + "address" => %{"street" => "Lott Place", "number" => 1697}, + }, + "company" => "Manglo", + "email" => "[email protected]", + "manager" => true, + "twitter" => nil, + "favorites" => ["Lisp", "Erlang", "Python"], + "exists_array" => ["should", "exist", "array1"], + "complex_field_value" => '+-()%{}[]^~&&*||"\\/? =>!', + "ordered" => true, + }, + %{ + "_id" => "e900001d-bc48-48a6-9b1a-ac9a1f5d1a03", + "user_id" => 10, + "name" => %{"first" => "Charmaine", "last" => "Mills"}, + "age" => 43, + "location" => %{ + "state" => "New Hampshire", + "city" => "Kiskimere", + "address" => %{"street" => "Nostrand Avenue", "number" => 4503}, + }, + "company" => "Lyria", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["Erlang", "Erlang"], + "exists_array" => ["should", "exist", "array2"], + "ordered" => 9, + }, + %{ + "_id" => "b06aadcf-cd0f-4ca6-9f7e-2c993e48d4c4", + "user_id" => 11, + "name" => %{"first" => "Mathis", "last" => "Hernandez"}, + "age" => 75, + "location" => %{ + "state" => "Hawaii", + "city" => "Dupuyer", + "address" => %{"street" => "Bancroft Place", "number" => 2741}, + }, + "company" => "Affluex", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["Ruby", "Lisp", "C", "C++", "C++"], + "exists_object" => %{"should" => "object"}, + "ordered" => 10_000, + }, + %{ + "_id" => "5b61abc1-a3d3-4092-b9d7-ced90e675536", + "user_id" => 12, + "name" => %{"first" => "Patti", "last" => "Rosales"}, + "age" => 71, + "location" => %{ + "state" => "Pennsylvania", + "city" => "Juntura", + "address" => %{"street" => "Hunterfly Place", "number" => 7683}, + }, + "company" => "Oulu", + "email" => "[email protected]", + "manager" => true, + "favorites" => ["C", "Python", "Lisp"], + "exists_object" => %{"another" => "object"}, + "ordered" => "a", + }, + %{ + "_id" => "b1e70402-8add-4068-af8f-b4f3d0feb049", + "user_id" => 13, + "name" => %{"first" => "Whitley", "last" => "Harvey"}, + "age" => 78, + "location" => %{ + "state" => "Minnesota", + "city" => "Trail", + "address" => %{"street" => "Pleasant Place", "number" => 8766}, + }, + "company" => nil, + "email" => "[email protected]", + "manager" => false, + "twitter" => "@whitleyharvey", + "favorites" => ["C", "Ruby", "Ruby"], + "ordered" => "A", + }, + %{ + "_id" => "c78c529f-0b07-4947-90a6-d6b7ca81da62", + "user_id" => 14, + "name" => %{"first" => "Faith", "last" => "Hess"}, + "age" => 51, + "location" => %{ + "state" => "North Dakota", + "city" => "Axis", + "address" => %{"street" => "Brightwater Avenue", "number" => 1106}, + }, + "foo" => "bar car apple", + "company" => "Pharmex", + "email" => "[email protected]", + "favorites" => ["Erlang", "Python", "Lisp"], + "ordered" => "aa", + } + ] + + @users_docs [ + %{ + "_id" => "org.couchdb.user =>demo01", + "name" => "demo01", + "username" => "demo01", + "password" => "apple01", + "roles" => ["design"], + "order" => 1, + "type" => "user", + }, + %{ + "_id" => "org.couchdb.user =>demo02", + "name" => "demo02", + "username" => "demo02", + "password" => "apple02", + "roles" => ["reader"], + "order" => 2, + "type" => "user", + }, + %{ + "_id" => "org.couchdb.user =>demo03", + "name" => "demo03", + "username" => "demo03", + "password" => "apple03", + "roles" => ["reader", "writer"], + "order" => 3, + "type" => "user", + } + ] + + def setup_users(db) do + MangoDatabase.recreate(db) + MangoDatabase.save_docs(db, @users_docs) + end + + def setup(db, index_type \\ "view", partitioned \\ false) do + MangoDatabase.recreate(db, [partitioned: partitioned]) + docs = @docs + + if partitioned do + partition_docs = Enum.map(Enum.with_index(docs), fn {doc, index} -> + partition = rem(index, @partitions) + %{doc | "_id" => "#{partition}:#{doc["_id"]}"} + end) + MangoDatabase.save_docs(db, partition_docs) + else + MangoDatabase.save_docs(db, docs) + end + + case index_type do + "view" -> add_view_indexes(db) + "text" -> add_text_indexes(db) + "special" -> :ok + end + end + + def len() do + length(@docs) + end + + defp add_view_indexes(db) do + indexes = [ + {["user_id"], "user_id"}, + {["name.last", "name.first"], "name"}, + {["age"], "age"}, + { + [ + "location.state", + "location.city", + "location.address.street", + "location.address.number", + ], + "location", + }, + {["company", "manager"], "company_and_manager"}, + {["manager"], "manager"}, + {["favorites"], "favorites"}, + {["favorites.3"], "favorites_3"}, + {["twitter"], "twitter"}, + {["ordered"], "ordered"}, + ] + + Enum.each(indexes, fn {idx, name} -> + MangoDatabase.create_index(db, idx, name) + end) + end + + defp add_text_indexes(db) do + MangoDatabase.create_text_index(db) + end +end
