blob: 5b40965f60f1a3299945592a982ebb265c2c4a77 [file] [log] [blame]
defmodule AllDocsTest do
use CouchTestCase
@moduletag :all_docs
@moduledoc """
Test CouchDB _all_docs
This is a port of the all_docs.js suite
"""
# TODO: do we need to bring this in?
# var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"}, {w: 3});
@tag :with_db
test "All Docs tests", context do
db_name = context[:db_name]
resp1 = Couch.post("/#{db_name}", body: %{:_id => "0", :a => 1, :b => 1}).body
resp2 = Couch.post("/#{db_name}", body: %{:_id => "3", :a => 4, :b => 16}).body
resp3 = Couch.post("/#{db_name}", body: %{:_id => "1", :a => 2, :b => 4}).body
resp4 = Couch.post("/#{db_name}", body: %{:_id => "2", :a => 3, :b => 9}).body
assert resp1["ok"]
assert resp2["ok"]
assert resp3["ok"]
assert resp4["ok"]
revs = [resp1["rev"], resp2["rev"], resp3["rev"], resp4["rev"]]
# Check _all_docs
resp = Couch.get("/#{db_name}/_all_docs").body
rows = resp["rows"]
assert resp["total_rows"] == length(rows)
Enum.each(rows, fn row ->
assert row["id"] >= "0" && row["id"] <= "4"
end)
# Check _all_docs with descending=true
resp = Couch.get("/#{db_name}/_all_docs", query: %{:descending => true}).body
rows = resp["rows"]
assert resp["total_rows"] == length(rows)
# Check _all_docs offset
retry_until(fn ->
resp = Couch.get("/#{db_name}/_all_docs", query: %{:startkey => "\"2\""}).body
assert resp["offset"] == 2
end)
# Confirm that queries may assume raw collation
resp =
Couch.get(
"/#{db_name}/_all_docs",
query: %{
:startkey => "\"org.couchdb.user:\"",
:endkey => "\"org.couchdb.user;\""
}
)
assert Enum.empty?(resp.body["rows"])
# Check that all docs show up in the changes feed; order can vary
resp = Couch.get("/#{db_name}/_changes").body
Enum.each(resp["results"], fn row ->
assert Enum.member?(revs, hd(row["changes"])["rev"]),
"doc #{row["id"]} should be in changes"
end)
# Check that deletions also show up right
doc1 = Couch.get("/#{db_name}/1").body
assert Couch.delete("/#{db_name}/1", query: %{:rev => doc1["_rev"]}).body["ok"]
changes = Couch.get("/#{db_name}/_changes").body["results"]
assert length(changes) == 4
retry_until(fn ->
deleted = Enum.filter(changes, fn row -> row["deleted"] end)
assert length(deleted) == 1
assert hd(deleted)["id"] == "1"
end)
# (remember old seq)
orig_doc = Enum.find(changes, fn row -> row["id"] == "3" end)
# Perform an update
doc3 = Couch.get("/#{db_name}/3").body
doc3 = Map.put(doc3, :updated, "totally")
assert Couch.put("/#{db_name}/3", body: doc3).body["ok"]
# The update should make doc id 3 have another seq num
# (not nec. higher or the last though)
changes = Couch.get("/#{db_name}/_changes").body["results"]
assert length(changes) == 4
updated_doc = Enum.find(changes, fn row -> row["id"] == "3" end)
assert orig_doc["seq"] != updated_doc["seq"], "seq num should be different"
# Ok, now let's see what happens with include docs
changes =
Couch.get("/#{db_name}/_changes", query: %{:include_docs => true}).body["results"]
assert length(changes) == 4
updated_doc = Enum.find(changes, fn row -> row["id"] == doc3["_id"] end)
assert updated_doc["doc"]["updated"] == "totally"
deleted_doc = Enum.find(changes, fn row -> row["deleted"] end)
assert deleted_doc["doc"]["_deleted"]
# Test _all_docs with keys
rows =
Couch.post(
"/#{db_name}/_all_docs",
query: %{:include_docs => true},
body: %{:keys => ["1"]}
).body["rows"]
row = hd(rows)
assert length(rows) == 1
assert row["key"] == "1"
assert row["id"] == "1"
assert row["value"]["deleted"]
assert row["doc"] == :null
# Add conflicts
conflicted_doc1 = %{
:_id => "3",
:_rev => "2-aa01552213fafa022e6167113ed01087",
:value => "X"
}
conflicted_doc2 = %{
:_id => "3",
:_rev => "2-ff01552213fafa022e6167113ed01087",
:value => "Z"
}
assert Couch.put(
"/#{db_name}/3",
query: %{:new_edits => false},
body: conflicted_doc1
).body["ok"]
assert Couch.put(
"/#{db_name}/3",
query: %{:new_edits => false},
body: conflicted_doc2
).body["ok"]
win_rev = Couch.get("/#{db_name}/3").body
changes =
Couch.get(
"/#{db_name}/_changes",
query: %{:include_docs => true, :conflicts => true, :style => "all_docs"}
).body["results"]
doc3 = Enum.find(changes, fn row -> row["id"] == "3" end)
assert doc3["id"] == "3"
assert length(doc3["changes"]) == 3
assert win_rev["_rev"] == hd(doc3["changes"])["rev"]
assert is_list(doc3["doc"]["_conflicts"])
assert length(doc3["doc"]["_conflicts"]) == 2
rows =
Couch.get(
"/#{db_name}/_all_docs",
query: %{:include_docs => true, :conflicts => true}
).body["rows"]
assert length(rows) == 3
change = hd(tl(tl(rows)))
assert change["key"] == "3"
assert change["id"] == "3"
assert change["value"]["rev"] == win_rev["_rev"]
assert change["doc"]["_rev"] == win_rev["_rev"]
assert change["doc"]["_id"] == "3"
assert is_list(change["doc"]["_conflicts"])
assert length(change["doc"]["_conflicts"]) == 2
# Test that _all_docs collates sanely
assert Couch.post("/#{db_name}", body: %{:_id => "Z", :foo => "Z"}).body["ok"]
assert Couch.post("/#{db_name}", body: %{:_id => "a", :foo => "a"}).body["ok"]
rows =
Couch.get(
"/#{db_name}/_all_docs",
query: %{:startkey => "\"Z\"", :endkey => "\"Z\""}
).body["rows"]
assert length(rows) == 1
end
@tag :with_db
test "GET with one key", context do
db_name = context[:db_name]
{:ok, _} = create_doc(
db_name,
%{
_id: "foo",
bar: "baz"
}
)
{:ok, _} = create_doc(
db_name,
%{
_id: "foo2",
bar: "baz2"
}
)
resp = Couch.get(
"/#{db_name}/_all_docs",
query: %{
:key => "\"foo\"",
}
)
assert resp.status_code == 200
assert length(Map.get(resp, :body)["rows"]) == 1
end
@tag :with_db
test "POST with empty body", context do
db_name = context[:db_name]
resp = Couch.post("/#{db_name}/_bulk_docs", body: %{docs: create_docs(0..2)})
assert resp.status_code == 201
resp = Couch.post(
"/#{db_name}/_all_docs",
body: %{}
)
assert resp.status_code == 200
assert length(Map.get(resp, :body)["rows"]) == 3
end
@tag :with_db
test "POST with keys and limit", context do
db_name = context[:db_name]
resp = Couch.post("/#{db_name}/_bulk_docs", body: %{docs: create_docs(0..3)})
assert resp.status_code == 201
resp = Couch.post(
"/#{db_name}/_all_docs",
body: %{
:keys => [1, 2],
:limit => 1
}
)
assert resp.status_code == 200
assert length(Map.get(resp, :body)["rows"]) == 1
end
@tag :with_db
test "POST with query parameter and JSON body", context do
db_name = context[:db_name]
resp = Couch.post("/#{db_name}/_bulk_docs", body: %{docs: create_docs(0..3)})
assert resp.status_code == 201
resp = Couch.post(
"/#{db_name}/_all_docs",
query: %{
:limit => 1
},
body: %{
:keys => [1, 2]
}
)
assert resp.status_code == 200
assert length(Map.get(resp, :body)["rows"]) == 1
end
@tag :with_db
test "POST edge case with colliding parameters - query takes precedence", context do
db_name = context[:db_name]
resp = Couch.post("/#{db_name}/_bulk_docs", body: %{docs: create_docs(0..3)})
assert resp.status_code == 201
resp = Couch.post(
"/#{db_name}/_all_docs",
query: %{
:limit => 1
},
body: %{
:keys => [1, 2],
:limit => 2
}
)
assert resp.status_code == 200
assert length(Map.get(resp, :body)["rows"]) == 1
end
end