1# Elixir Test Suite
2
3Proof of concept porting the JS test suite to Elixir.
4
5Currently the basics.js suite has been partially ported over.
6
7To run the suite:
8
9```
10mix deps.get
11mix test --trace
12```
13
14## Set CouchDB credentials
15
16By default the Elixir tests require CouchDB running at http://127.0.0.1:15984 with credentials `adm:pass`.
17You can override those using the following:
18
19```
20$ EX_USERNAME=myusername EX_PASSWORD=password EX_COUCH_URL=http://my-couchdb.com mix test
21```
22
23## Tests to port
24
25X means done, - means partially
26
27  - [X] Port all_docs.js
28  - [X] Port attachment_names.js
29  - [X] Port attachment_paths.js
30  - [X] Port attachment_ranges.js
31  - [X] Port attachments.js
32  - [X] Port attachments_multipart.js
33  - [X] Port attachment_views.js
34  - [X] Port auth_cache.js
35  - [X] Port basics.js
36  - [X] Port batch_save.js
37  - [X] Port bulk_docs.js
38  - [X] Port changes.js
39  - [X] Port coffee.js
40  - [X] Port compact.js
41  - [X] Port config.js
42  - [X] Port conflicts.js
43  - [X] Port cookie_auth.js
44  - [X] Port copy_doc.js
45  - [X] Port design_docs.js
46  - [X] Port design_docs_query.js
47  - [X] Port design_options.js
48  - [X] Port design_paths.js
49  - [X] Port erlang_views.js
50  - [X] Port etags_head.js
51  - [ ] ~~Port etags_views.js~~ (skipped in js test suite)
52  - [X] Port form_submit.js
53  - [X] Port http.js
54  - [X] Port invalid_docids.js
55  - [X] Port jsonp.js
56  - [X] Port large_docs.js
57  - [X] Port list_views.js
58  - [X] Port lorem_b64.txt
59  - [X] Port lorem.txt
60  - [X] Port lots_of_docs.js
61  - [X] Port method_override.js
62  - [X] Port multiple_rows.js
63  - [X] Port proxyauth.js
64  - [X] Port purge.js
65  - [X] Port reader_acl.js
66  - [X] Port recreate_doc.js
67  - [X] Port reduce_builtin.js
68  - [X] Port reduce_false.js
69  - [ ] ~~Port reduce_false_temp.js~~
70  - [X] Port reduce.js
71  - [X] Port replication.js
72  - [ ] Port replicator_db_bad_rep_id.js
73  - [ ] Port replicator_db_by_doc_id.js
74  - [ ] Port replicator_db_compact_rep_db.js
75  - [ ] Port replicator_db_continuous.js
76  - [ ] Port replicator_db_credential_delegation.js
77  - [ ] Port replicator_db_field_validation.js
78  - [ ] Port replicator_db_filtered.js
79  - [ ] Port replicator_db_identical_continuous.js
80  - [ ] Port replicator_db_identical.js
81  - [ ] Port replicator_db_invalid_filter.js
82  - [ ] Port replicator_db_security.js
83  - [ ] Port replicator_db_simple.js
84  - [ ] Port replicator_db_successive.js
85  - [ ] Port replicator_db_survives.js
86  - [ ] Port replicator_db_swap_rep_db.js
87  - [ ] Port replicator_db_update_security.js
88  - [ ] Port replicator_db_user_ctx.js
89  - [ ] Port replicator_db_write_auth.js
90  - [X] Port rev_stemming.js
91  - [X] Port rewrite.js
92  - [X] Port rewrite_js.js
93  - [X] Port security_validation.js
94  - [X] Port show_documents.js
95  - [ ] Port stats.js
96  - [X] Port update_documents.js
97  - [X] Port users_db.js
98  - [X] Port users_db_security.js
99  - [X] Port utf8.js
100  - [X] Port uuids.js
101  - [X] Port view_collation.js
102  - [X] Port view_collation_raw.js
103  - [X] Port view_compaction.js
104  - [X] Port view_conflicts.js
105  - [X] Port view_errors.js
106  - [X] Port view_include_docs.js
107  - [X] Port view_multi_key_all_docs.js
108  - [X] Port view_multi_key_design.js
109  - [ ] ~~Port view_multi_key_temp.js~~
110  - [X] Port view_offsets.js
111  - [X] Port view_pagination.js
112  - [X] Port view_sandboxing.js
113  - [X] Port view_update_seq.js
114
115# Using ExUnit to write unit tests
116
117Elixir has a number of benefits which makes writing unit tests easier.
118For example it is trivial to do codegeneration of tests.
119Bellow we present a few use cases where code-generation is really helpful.
120
121## How to write ExUnit tests
122
1231. Create new file in test/exunit/ directory (the file name should match *_test.exs)
1242. In case it is a first file in the directory create test_helper.exs (look at src/couch/test/exunit/test_helper.exs to get an idea)
1253. define test module which does `use Couch.Test.ExUnit.Case`
1264. Define test cases in the module
127
128You can run tests either:
129- using make: `make exunit`
130- using mix: BUILDDIR=`pwd` ERL_LIBS=`pwd`/src MIX_ENV=test mix test --trace
131
132## Generating tests from spec
133
134Sometimes we have some data in structured format and want
135to generate test cases using that data. This is easy in Elixir.
136For example suppose we have following spec:
137```
138{
139	"{db_name}/_view_cleanup": {
140		"roles": ["_admin"]
141    }
142}
143```
144We can use this spec to generate test cases
145```
146defmodule GenerateTestsFromSpec do
147  use ExUnit.Case
148  require Record
149  Record.defrecordp :user_ctx, Record.extract(:user_ctx, from_lib: "couch/include/couch_db.hrl")
150  Record.defrecordp :httpd, Record.extract(:httpd, from_lib: "couch/include/couch_db.hrl")
151
152  {:ok, spec_bin} = File.read("roles.json")
153  spec = :jiffy.decode(spec_bin, [:return_maps])
154  Enum.each spec, fn {path, path_spec} ->
155    roles = path_spec["roles"]
156    @roles roles
157    @path_parts String.split(path, "/")
158    test "Access with `#{inspect(roles)}` roles" do
159      req = httpd(path_parts: @path_parts, user_ctx: user_ctx(roles: @roles))
160      :chttpd_auth_request.authorize_request(req)
161    end
162  end
163end
164```
165As a result we would get
166```
167GenerateTestsFromSpec
168  * test Access with `["_admin"]` roles (0.00ms)
169```
170
171## Test all possible combinations
172
173Sometimes we want to test all possible permutations for parameters.
174This can be accomplished using something like the following:
175
176```
177defmodule Permutations do
178  use ExUnit.Case
179  pairs = :couch_tests_combinatorics.product([
180    [:remote, :local], [:remote, :local]
181  ])
182  for [source, dest] <- pairs do
183    @source source
184    @dest dest
185    test "Replication #{source} -> #{dest}" do
186     assert :ok == :ok
187    end
188  end
189end
190```
191
192This would produce following tests
193```
194Permutations
195  * test Replication remote -> remote (0.00ms)
196  * test Replication local -> remote (0.00ms)
197  * test Replication remote -> local (0.00ms)
198  * test Replication local -> local (0.00ms)
199```
200
201## Reuseing of common setups
202
203The setup functions are quite similar in lots of tests therefore it makes
204sense to reuse them. The idea is to add shared setup functions into either
205- test/elixir/lib/setup/common.ex
206- test/elixir/lib/setup/<something>.ex
207
208The setup functions looks like the following:
209```
210defmodule Foo do
211  alias Couch.Test.Setup.Step
212
213  def httpd_with_admin(setup) do
214    setup
215      |> Step.Start.new(:start, extra_apps: [:chttpd])
216      |> Step.User.new(:admin, roles: [:server_admin])
217  end
218end
219```
220
221These parts of a setup chain can be invoked as follows:
222```
223defmodule Couch.Test.CRUD do
224  use Couch.Test.ExUnit.Case
225  alias Couch.Test.Utils
226
227  alias Couch.Test.Setup
228
229  alias Couch.Test.Setup.Step
230
231  def with_db(context, setup) do
232    setup =
233      setup
234      |> Setup.Common.httpd_with_db()
235      |> Setup.run()
236
237    context =
238      Map.merge(context, %{
239        db_name: setup |> Setup.get(:db) |> Step.Create.DB.name(),
240        base_url: setup |> Setup.get(:start) |> Step.Start.clustered_url(),
241        user: setup |> Setup.get(:admin) |> Step.User.name()
242      })
243
244    {context, setup}
245  end
246
247  describe "Database CRUD using Fabric API" do
248    @describetag setup: &__MODULE__.with_db/2
249    test "Create DB", ctx do
250      IO.puts("base_url: #{ctx.base_url}")
251      IO.puts("admin: #{ctx.user}")
252      IO.puts("db_name: #{ctx.db_name}")
253    end
254  end
255end
256```
257