You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ch...@apache.org on 2018/06/22 22:15:30 UTC
[couchdb] 01/31: WIP: Elixir test suite
This is an automated email from the ASF dual-hosted git repository.
chewbranca pushed a commit to branch elixir-suite
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit d0993752a230ac29dc356fd0518d136a8a64c970
Author: Russell Branca <ch...@apache.org>
AuthorDate: Wed Nov 15 23:58:57 2017 +0000
WIP: Elixir test suite
---
elixir_suite/README.md | 12 ++++
elixir_suite/config/config.exs | 30 ++++++++++
elixir_suite/config/test.exs | 3 +
elixir_suite/lib/couch.ex | 33 +++++++++++
elixir_suite/mix.exs | 30 ++++++++++
elixir_suite/mix.lock | 3 +
elixir_suite/test/basics_test.exs | 118 ++++++++++++++++++++++++++++++++++++++
elixir_suite/test/test_helper.exs | 73 +++++++++++++++++++++++
8 files changed, 302 insertions(+)
diff --git a/elixir_suite/README.md b/elixir_suite/README.md
new file mode 100644
index 0000000..a7aedd3
--- /dev/null
+++ b/elixir_suite/README.md
@@ -0,0 +1,12 @@
+# Elixir Test Suite
+
+Proof of concept porting the JS test suite to Elixir.
+
+Currently the basics.js suite has been partially ported over.
+
+To run the suite:
+
+```
+mix deps.get
+mix test --trace
+```
diff --git a/elixir_suite/config/config.exs b/elixir_suite/config/config.exs
new file mode 100644
index 0000000..966ae83
--- /dev/null
+++ b/elixir_suite/config/config.exs
@@ -0,0 +1,30 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+use Mix.Config
+
+# This configuration is loaded before any dependency and is restricted
+# to this project. If another project depends on this project, this
+# file won't be loaded nor affect the parent project. For this reason,
+# if you want to provide default values for your application for
+# 3rd-party users, it should be done in your "mix.exs" file.
+
+# You can configure your application as:
+#
+# config :foo, key: :value
+#
+# and access this configuration in your application as:
+#
+# Application.get_env(:foo, :key)
+#
+# You can also configure a 3rd-party app:
+#
+# config :logger, level: :info
+#
+
+# It is also possible to import configuration files, relative to this
+# directory. For example, you can emulate configuration per environment
+# by uncommenting the line below and defining dev.exs, test.exs and such.
+# Configuration from the imported file will override the ones defined
+# here (which is why it is important to import them last).
+#
+# import_config "#{Mix.env}.exs"
diff --git a/elixir_suite/config/test.exs b/elixir_suite/config/test.exs
new file mode 100644
index 0000000..4b28ea9
--- /dev/null
+++ b/elixir_suite/config/test.exs
@@ -0,0 +1,3 @@
+config :logger,
+ backends: [:console],
+ compile_time_purge_level: :debug
diff --git a/elixir_suite/lib/couch.ex b/elixir_suite/lib/couch.ex
new file mode 100644
index 0000000..aafe829
--- /dev/null
+++ b/elixir_suite/lib/couch.ex
@@ -0,0 +1,33 @@
+defmodule Couch do
+ use HTTPotion.Base
+
+ @moduledoc """
+ CouchDB library to power test suite.
+ """
+
+ def process_url(url) do
+ "http://localhost:15984" <> url
+ end
+
+ def process_request_headers(headers) do
+ headers
+ |> Dict.put(:"User-Agent", "couch-potion")
+ |> Dict.put(:"Content-Type", "application/json")
+ end
+
+ def process_options(options) do
+ Dict.put options, :basic_auth, {"adm", "pass"}
+ end
+
+ def process_request_body(body) do
+ if is_map(body) do
+ :jiffy.encode(body)
+ else
+ body
+ end
+ end
+
+ def process_response_body(body) do
+ body |> IO.iodata_to_binary |> :jiffy.decode([:return_maps])
+ end
+end
diff --git a/elixir_suite/mix.exs b/elixir_suite/mix.exs
new file mode 100644
index 0000000..9b0f642
--- /dev/null
+++ b/elixir_suite/mix.exs
@@ -0,0 +1,30 @@
+defmodule Foo.Mixfile do
+ use Mix.Project
+
+ def project do
+ [
+ app: :foo,
+ version: "0.1.0",
+ elixir: "~> 1.5",
+ start_permanent: Mix.env == :prod,
+ deps: deps()
+ ]
+ end
+
+ # Run "mix help compile.app" to learn about applications.
+ def application do
+ [
+ extra_applications: [:logger]
+ ]
+ end
+
+ # Run "mix help deps" to learn about dependencies.
+ defp deps do
+ [
+ # {:dep_from_hexpm, "~> 0.3.0"},
+ {:httpotion, "~> 3.0"},
+ {:jiffy, "~> 0.14.11"}
+ # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
+ ]
+ end
+end
diff --git a/elixir_suite/mix.lock b/elixir_suite/mix.lock
new file mode 100644
index 0000000..0723e94
--- /dev/null
+++ b/elixir_suite/mix.lock
@@ -0,0 +1,3 @@
+%{"httpotion": {:hex, :httpotion, "3.0.3", "17096ea1a7c0b2df74509e9c15a82b670d66fc4d66e6ef584189f63a9759428d", [], [{:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: false]}], "hexpm"},
+ "ibrowse": {:hex, :ibrowse, "4.4.0", "2d923325efe0d2cb09b9c6a047b2835a5eda69d8a47ed6ff8bc03628b764e991", [], [], "hexpm"},
+ "jiffy": {:hex, :jiffy, "0.14.11", "919a87d491c5a6b5e3bbc27fafedc3a0761ca0b4c405394f121f582fd4e3f0e5", [], [], "hexpm"}}
diff --git a/elixir_suite/test/basics_test.exs b/elixir_suite/test/basics_test.exs
new file mode 100644
index 0000000..87b4aff
--- /dev/null
+++ b/elixir_suite/test/basics_test.exs
@@ -0,0 +1,118 @@
+defmodule BasicsTest do
+ use CouchTestCase
+
+ @moduledoc """
+ Test CouchDB basics.
+ This is a port of the basics.js suite
+ """
+
+ test "Session contains adm context" do
+ userCtx = Couch.get("/_session").body["userCtx"]
+ assert userCtx["name"] == "adm", "Should have adm user context"
+ assert userCtx["roles"] == ["_admin"], "Should have _admin role"
+ end
+
+ test "Welcome endpoint" do
+ assert Couch.get("/").body["couchdb"] == "Welcome", "Should say welcome"
+ end
+
+ @tag :with_db
+ test "PUT on existing DB should return 412 instead of 500", context do
+ db_name = context[:db_name]
+ assert Couch.put("/#{db_name}").status_code == 412
+ end
+
+ @tag :with_db_name
+ test "Creating a new DB should return location header", context do
+ db_name = context[:db_name]
+ {:ok, resp} = create_db(db_name)
+ msg = "Should return Location header for new db"
+ assert String.ends_with?(resp.headers["location"], db_name), msg
+ {:ok, _} = delete_db(db_name)
+ end
+
+ @tag :with_db_name
+ test "Creating a new DB with slashes should return Location header (COUCHDB-411)", context do
+ db_name = context[:db_name] <> "%2Fwith_slashes"
+ {:ok, resp} = create_db(db_name)
+ msg = "Should return Location header for new db"
+ assert String.ends_with?(resp.headers["location"], db_name), msg
+ {:ok, _} = delete_db(db_name)
+ end
+
+ @tag :with_db
+ test "Created database has appropriate db info name", context do
+ db_name = context[:db_name]
+ assert Couch.get("/#{db_name}").body["db_name"] == db_name, "Get correct database name"
+ end
+
+ @tag :with_db
+ test "Database should be in _all_dbs", context do
+ assert context[:db_name] in Couch.get("/_all_dbs").body, "Db name in _all_dbs"
+ end
+
+ @tag :with_db
+ test "Empty database should have zero docs", context do
+ assert Couch.get("/#{context[:db_name]}").body["doc_count"] == 0, "Empty doc count in empty db"
+ end
+
+ @tag :with_db
+ test "Create a document and save it to the database", context do
+ resp = Couch.post("/#{context[:db_name]}", [body: %{:_id => "0", :a => 1, :b => 1}])
+ assert resp.status_code == 201, "Should be 201 created"
+ assert resp.body["id"], "Id should be present"
+ assert resp.body["rev"], "Rev should be present"
+
+ resp2 = Couch.get("/#{context[:db_name]}/#{resp.body["id"]}")
+ assert resp2.body["_id"] == resp.body["id"], "Ids should match"
+ assert resp2.body["_rev"] == resp.body["rev"], "Revs should match"
+ end
+
+ @tag :with_db
+ test "Revs info status is good", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, sample_doc_foo())
+ resp = Couch.get("/#{db_name}/foo", [query: %{:revs_info => true}])
+ assert hd(resp.body["_revs_info"])["status"] == "available", "Revs info is available"
+ end
+
+ @tag :with_db
+ test "Make sure you can do a seq=true option", context do
+ db_name = context[:db_name]
+ {:ok, _} = create_doc(db_name, sample_doc_foo())
+ resp = Couch.get("/#{db_name}/foo", [query: %{:local_seq => true}])
+ assert resp.body["_local_seq"] == 1, "Local seq value == 1"
+ end
+
+ @tag :with_db
+ test "Can create several documents", context do
+ db_name = context[:db_name]
+ assert Couch.post("/#{db_name}", [body: %{:_id => "1", :a => 2, :b => 4}]).body["ok"]
+ assert Couch.post("/#{db_name}", [body: %{:_id => "2", :a => 3, :b => 9}])
+ assert Couch.post("/#{db_name}", [body: %{:_id => "3", :a => 4, :b => 16}]).body["ok"]
+ assert Couch.get("/#{db_name}").body["doc_count"] == 3
+ end
+
+ @tag :with_db
+ test "Regression test for COUCHDB-954", context do
+ db_name = context[:db_name]
+ doc = %{:_id => "COUCHDB-954", :a => 1}
+
+ resp1 = Couch.post("/#{db_name}", [body: doc])
+ assert resp1.body["ok"]
+ old_rev = resp1.body["rev"]
+
+ doc = Map.put(doc, :_rev, old_rev)
+ resp2 = Couch.post("/#{db_name}", [body: doc])
+ assert resp2.body["ok"]
+ new_rev = resp2.body["rev"]
+
+ # TODO: enable chunked encoding
+ #resp3 = Couch.get("/#{db_name}/COUCHDB-954", [query: %{:open_revs => "[#{old_rev}, #{new_rev}]"}])
+ #assert length(resp3.body) == 2, "Should get two revisions back"
+ #resp3 = Couch.get("/#{db_name}/COUCHDB-954", [query: %{:open_revs => "[#{old_rev}]", :latest => true}])
+ #assert resp3.body["_rev"] == new_rev
+ end
+
+
+end
diff --git a/elixir_suite/test/test_helper.exs b/elixir_suite/test/test_helper.exs
new file mode 100644
index 0000000..181642b
--- /dev/null
+++ b/elixir_suite/test/test_helper.exs
@@ -0,0 +1,73 @@
+ExUnit.start()
+
+# TODO
+#def random_db_name do
+# "asdf"
+#end
+
+defmodule CouchTestCase do
+ use ExUnit.Case
+
+ defmacro __using__(_opts) do
+ quote do
+ require Logger
+ use ExUnit.Case
+
+ setup context do
+ db_name = if context[:with_db] != nil or context[:with_db_name] != nil do
+ if context[:with_db] != nil and context[:with_db] != true do
+ context[:with_db]
+ else
+ case context[:with_db_name] do
+ nil -> random_db_name()
+ true -> random_db_name()
+ name -> name
+ end
+ end
+ end
+
+ if context[:with_db] != nil do
+ {:ok, _} = create_db(db_name)
+
+ on_exit(fn -> delete_db(db_name) end)
+ end
+
+ {:ok, db_name: db_name}
+ end
+
+ def random_db_name do
+ time = :erlang.monotonic_time()
+ umi = :erlang.unique_integer([:monotonic])
+ "random-test-db-#{time}-#{umi}"
+ end
+
+ def create_db(db_name) do
+ resp = Couch.put("/#{db_name}")
+ assert resp.status_code == 201
+ assert resp.body == %{"ok" => true}
+ {:ok, resp}
+ end
+
+ def delete_db(db_name) do
+ resp = Couch.delete("/#{db_name}")
+ assert resp.status_code == 200
+ assert resp.body == %{"ok" => true}
+ {:ok, resp}
+ end
+
+ def create_doc(db_name, body) do
+ resp = Couch.post("/#{db_name}", [body: body])
+ assert resp.status_code == 201
+ assert resp.body["ok"]
+ {:ok, resp}
+ end
+
+ def sample_doc_foo do
+ %{
+ "_id": "foo",
+ "bar": "baz"
+ }
+ end
+ end
+ end
+end