Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions test/elixir/lib/couch/db_test.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
defmodule Couch.DBTest do
import ExUnit.Callbacks, only: [on_exit: 1]
import ExUnit.Assertions, only: [assert: 1, assert: 2]

def set_db_context(context) do
context =
case context do
%{:with_db_name => true} ->
Map.put(context, :db_name, random_db_name())

%{:with_db_name => db_name} when is_binary(db_name) ->
Map.put(context, :db_name, db_name)

%{:with_random_db => db_name} when is_binary(db_name) ->
context
|> Map.put(:db_name, random_db_name(db_name))
|> Map.put(:with_db, true)

%{:with_db => true} ->
Map.put(context, :db_name, random_db_name())

%{:with_db => db_name} when is_binary(db_name) ->
Map.put(context, :db_name, db_name)

_ ->
context
end

if Map.has_key?(context, :with_db) do
{:ok, _} = create_db(context[:db_name])
on_exit(fn -> delete_db(context[:db_name]) end)
end

context
end

def set_config_context(context) do
if is_list(context[:config]) do
Enum.each(context[:config], fn cfg ->
set_config(cfg)
end)
end

context
end

def set_user_context(context) do
case Map.get(context, :user) do
nil ->
context

user when is_list(user) ->
user = create_user(user)

on_exit(fn ->
query = %{:rev => user["_rev"]}
resp = Couch.delete("/_users/#{user["_id"]}", query: query)
assert HTTPotion.Response.success?(resp)
end)

context = Map.put(context, :user, user)
userinfo = user["name"] <> ":" <> user["password"]
Map.put(context, :userinfo, userinfo)
end
end

def random_db_name do
random_db_name("random-test-db")
end

def random_db_name(prefix) do
time = :erlang.monotonic_time()
umi = :erlang.unique_integer([:monotonic])
"#{prefix}-#{time}-#{umi}"
end

def set_config({section, key, value}) do
existing = set_config_raw(section, key, value)

on_exit(fn ->
Enum.each(existing, fn {node, prev_value} ->
if prev_value != "" do
url = "/_node/#{node}/_config/#{section}/#{key}"
headers = ["X-Couch-Persist": "false"]
body = :jiffy.encode(prev_value)
resp = Couch.put(url, headers: headers, body: body)
assert resp.status_code == 200
else
url = "/_node/#{node}/_config/#{section}/#{key}"
headers = ["X-Couch-Persist": "false"]
resp = Couch.delete(url, headers: headers)
assert resp.status_code == 200
end
end)
end)
end

def set_config_raw(section, key, value) do
resp = Couch.get("/_membership")

Enum.map(resp.body["all_nodes"], fn node ->
url = "/_node/#{node}/_config/#{section}/#{key}"
headers = ["X-Couch-Persist": "false"]
body = :jiffy.encode(value)
resp = Couch.put(url, headers: headers, body: body)
assert resp.status_code == 200
{node, resp.body}
end)
end

def create_user(user) do
required = [:name, :password, :roles]

Enum.each(required, fn key ->
assert Keyword.has_key?(user, key), "User missing key: #{key}"
end)

name = Keyword.get(user, :name)
password = Keyword.get(user, :password)
roles = Keyword.get(user, :roles)

assert is_binary(name), "User name must be a string"
assert is_binary(password), "User password must be a string"
assert is_list(roles), "Roles must be a list of strings"

Enum.each(roles, fn role ->
assert is_binary(role), "Roles must be a list of strings"
end)

user_doc = %{
"_id" => "org.couchdb.user:" <> name,
"type" => "user",
"name" => name,
"roles" => roles,
"password" => password
}

resp = Couch.get("/_users/#{user_doc["_id"]}")

user_doc =
case resp.status_code do
404 ->
user_doc

sc when sc >= 200 and sc < 300 ->
Map.put(user_doc, "_rev", resp.body["_rev"])
end

resp = Couch.post("/_users", body: user_doc)
assert HTTPotion.Response.success?(resp)
assert resp.body["ok"]
Map.put(user_doc, "_rev", resp.body["rev"])
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

# Generate range of docs with strings as keys
def make_docs(id_range) do
for id <- id_range, str_id = Integer.to_string(id) do
%{"_id" => str_id, "integer" => id, "string" => str_id}
end
end

# Generate range of docs with atoms as keys, which are more
# idiomatic, and are encoded by jiffy to binaries
def create_docs(id_range) do
for id <- id_range, str_id = Integer.to_string(id) do
%{_id: str_id, integer: id, string: str_id}
end
end

def retry_until(condition, sleep \\ 100, timeout \\ 5000) do
retry_until(condition, now(:ms), sleep, timeout)
end

defp retry_until(condition, start, sleep, timeout) do
now = now(:ms)

if now > start + timeout do
raise "timed out after #{now - start} ms"
else
try do
if condition.() do
:ok
else
raise ExUnit.AssertionError
end
rescue
ExUnit.AssertionError ->
:timer.sleep(sleep)
retry_until(condition, start, sleep, timeout)
end
end
end

defp now(:ms) do
div(:erlang.system_time(), 1_000_000)
end

@spec rev(map(), map()) :: map()
def rev(doc = %{_id: id}, %{"id" => id, "rev" => rev}) do
Map.put(doc, :_rev, rev)
end

@spec rev([map()], [map()]) :: [map()]
def rev(docs, rows) when length(docs) == length(rows) do
for {doc, row} <- Enum.zip(docs, rows), do: rev(doc, row)
end

def pretty_inspect(resp) do
opts = [pretty: true, width: 20, limit: :infinity, printable_limit: :infinity]
inspect(resp, opts)
end
end
5 changes: 5 additions & 0 deletions test/elixir/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Foo.Mixfile do
app: :foo,
version: "0.1.0",
elixir: "~> 1.5",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
deps: deps()
]
Expand All @@ -18,6 +19,10 @@ defmodule Foo.Mixfile do
]
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

# Run "mix help deps" to learn about dependencies.
defp deps do
[
Expand Down
27 changes: 27 additions & 0 deletions test/elixir/test/support/couch_test_case.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule CouchTestCase do
use ExUnit.CaseTemplate

using do
quote do
require Logger
use ExUnit.Case

import Couch.DBTest
end
end

setup context do
setup_funs = [
&Couch.DBTest.set_db_context/1,
&Couch.DBTest.set_config_context/1,
&Couch.DBTest.set_user_context/1
]

context =
Enum.reduce(setup_funs, context, fn setup_fun, acc ->
setup_fun.(acc)
end)

{:ok, context}
end
end
Loading