Skip to content

Commit dbed92e

Browse files
committed
Port rev_stemming into elixir
1 parent ef160d6 commit dbed92e

File tree

3 files changed

+195
-1
lines changed

3 files changed

+195
-1
lines changed

test/elixir/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ X means done, - means partially
8787
- [ ] Port replicator_db_update_security.js
8888
- [ ] Port replicator_db_user_ctx.js
8989
- [ ] Port replicator_db_write_auth.js
90-
- [ ] Port rev_stemming.js
90+
- [X] Port rev_stemming.js
9191
- [X] Port rewrite.js
9292
- [ ] Port rewrite_js.js
9393
- [X] Port security_validation.js
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
defmodule RevStemmingTest do
2+
use CouchTestCase
3+
4+
@moduletag :revs
5+
6+
@moduledoc """
7+
This is a port of the rev_stemming.js suite
8+
"""
9+
10+
@new_limit 5
11+
12+
@tag :with_db
13+
test "revs limit update", context do
14+
db_name = context[:db_name]
15+
16+
resp = Couch.get("/#{db_name}/_revs_limit")
17+
assert resp.body == 1000
18+
19+
create_rev_doc(db_name, "foo", @new_limit + 1)
20+
resp = Couch.get("/#{db_name}/foo?revs=true")
21+
assert length(resp.body["_revisions"]["ids"]) == @new_limit + 1
22+
23+
resp =
24+
Couch.put("/#{db_name}/_revs_limit",
25+
body: "#{@new_limit}",
26+
headers: ["Content-type": "application/json"]
27+
)
28+
29+
assert resp.status_code == 200
30+
31+
create_rev_doc(db_name, "foo", @new_limit + 1)
32+
resp = Couch.get("/#{db_name}/foo?revs=true")
33+
assert length(resp.body["_revisions"]["ids"]) == @new_limit
34+
end
35+
36+
@tag :with_db
37+
test "revs limit produces replication conflict ", context do
38+
db_name = context[:db_name]
39+
40+
db_name_b = "#{db_name}_b"
41+
create_db(db_name_b)
42+
delete_db_on_exit([db_name_b])
43+
44+
resp =
45+
Couch.put("/#{db_name}/_revs_limit",
46+
body: "#{@new_limit}",
47+
headers: ["Content-type": "application/json"]
48+
)
49+
50+
assert resp.status_code == 200
51+
52+
create_rev_doc(db_name, "foo", @new_limit + 1)
53+
resp = Couch.get("/#{db_name}/foo?revs=true")
54+
assert length(resp.body["_revisions"]["ids"]) == @new_limit
55+
56+
# If you replicate after you make more edits than the limit, you'll
57+
# cause a spurious edit conflict.
58+
replicate(db_name, db_name_b)
59+
resp = Couch.get("/#{db_name_b}/foo?conflicts=true")
60+
assert not Map.has_key?(resp.body, "_conflicts")
61+
62+
create_rev_doc(db_name, "foo", @new_limit - 1)
63+
64+
# one less edit than limit, no conflict
65+
replicate(db_name, db_name_b)
66+
resp = Couch.get("/#{db_name_b}/foo?conflicts=true")
67+
assert not Map.has_key?(resp.body, "_conflicts")
68+
prev_conflicted_rev = resp.body["_rev"]
69+
70+
# now we hit the limit
71+
create_rev_doc(db_name, "foo", @new_limit + 1)
72+
73+
replicate(db_name, db_name_b)
74+
resp = Couch.get("/#{db_name_b}/foo?conflicts=true")
75+
assert Map.has_key?(resp.body, "_conflicts")
76+
77+
conflicted_rev =
78+
resp.body["_conflicts"]
79+
|> Enum.at(0)
80+
81+
# we have a conflict, but the previous replicated rev is always the losing
82+
# conflict
83+
assert conflicted_rev == prev_conflicted_rev
84+
end
85+
86+
@tag :with_db
87+
test "revs limit is kept after compaction", context do
88+
db_name = context[:db_name]
89+
90+
create_rev_doc(db_name, "bar", @new_limit + 1)
91+
resp = Couch.get("/#{db_name}/bar?revs=true")
92+
assert length(resp.body["_revisions"]["ids"]) == @new_limit + 1
93+
94+
resp =
95+
Couch.put("/#{db_name}/_revs_limit",
96+
body: "#{@new_limit}",
97+
headers: ["Content-type": "application/json"]
98+
)
99+
100+
assert resp.status_code == 200
101+
102+
# We having already updated bar before setting the limit, so it's still got
103+
# a long rev history. compact to stem the revs.
104+
resp = Couch.get("/#{db_name}/bar?revs=true")
105+
assert length(resp.body["_revisions"]["ids"]) == @new_limit
106+
107+
compact(db_name)
108+
wait_until_compact_complete(db_name)
109+
110+
# force reload because ETags don't honour compaction
111+
resp =
112+
Couch.get("/#{db_name}/bar?revs=true",
113+
headers: ["if-none-match": "pommes"]
114+
)
115+
116+
assert length(resp.body["_revisions"]["ids"]) == @new_limit
117+
end
118+
119+
# function to create a doc with multiple revisions
120+
defp create_rev_doc(db_name, id, num_revs) do
121+
resp = Couch.get("/#{db_name}/#{id}")
122+
123+
doc =
124+
if resp.status_code == 200 do
125+
resp.body
126+
else
127+
%{_id: id, count: 0}
128+
end
129+
130+
{:ok, resp} = create_doc(db_name, doc)
131+
create_rev_doc(db_name, id, num_revs, [Map.put(doc, :_rev, resp.body["rev"])])
132+
end
133+
134+
defp create_rev_doc(db_name, id, num_revs, revs) do
135+
if length(revs) < num_revs do
136+
doc = %{_id: id, _rev: Enum.at(revs, -1)[:_rev], count: length(revs)}
137+
{:ok, resp} = create_doc(db_name, doc)
138+
139+
create_rev_doc(
140+
db_name,
141+
id,
142+
num_revs,
143+
revs ++ [Map.put(doc, :_rev, resp.body["rev"])]
144+
)
145+
else
146+
revs
147+
end
148+
end
149+
150+
defp build_uri(db_name) do
151+
username = System.get_env("EX_USERNAME") || "adm"
152+
password = System.get_env("EX_PASSWORD") || "pass"
153+
154+
"/#{db_name}"
155+
|> Couch.process_url()
156+
|> URI.parse()
157+
|> Map.put(:userinfo, "#{username}:#{password}")
158+
|> URI.to_string()
159+
end
160+
161+
defp replicate(src, tgt) do
162+
src_uri = build_uri(src)
163+
tgt_uri = build_uri(tgt)
164+
165+
body = %{source: src_uri, target: tgt_uri}
166+
167+
resp = Couch.post("/_replicate", body: body)
168+
assert resp.status_code == 200
169+
resp.body
170+
end
171+
172+
def delete_db_on_exit(db_names) when is_list(db_names) do
173+
on_exit(fn ->
174+
Enum.each(db_names, fn name ->
175+
delete_db(name)
176+
end)
177+
end)
178+
end
179+
180+
defp compact(db_name) do
181+
resp = Couch.post("/#{db_name}/_compact")
182+
assert resp.status_code == 202
183+
resp.body
184+
end
185+
186+
defp wait_until_compact_complete(db_name) do
187+
retry_until(
188+
fn -> Map.get(info(db_name), "compact_running") == false end,
189+
200,
190+
10_000
191+
)
192+
end
193+
end

test/javascript/tests/rev_stemming.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// License for the specific language governing permissions and limitations under
1111
// the License.
1212

13+
couchTests.elixir = true;
1314
couchTests.rev_stemming = function(debug) {
1415

1516
var db_name_orig = get_random_db_name();

0 commit comments

Comments
 (0)