summaryrefslogtreecommitdiff
path: root/test/elixir/test/recreate_doc_test.exs
blob: 9ee914dc21e1fcef6c21cf07f724d4f286fdd3f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
defmodule RecreateDocTest do
  use CouchTestCase

  @moduletag :recreate_doc
  @moduletag kind: :single_node

  @moduledoc """
  Test CouchDB document recreation
  This is a port of the recreate_doc.js suite
  """

  @tag :with_db
  test "recreate document", context do
    db_name = context[:db_name]

    # First create a new document with the ID "foo", and delete it again
    doc = %{_id: "foo", a: "bar", b: 42}
    {:ok, resp} = create_doc(db_name, doc)
    first_rev = resp.body["rev"]

    resp = Couch.delete("/#{db_name}/foo?rev=#{first_rev}")
    assert resp.status_code == 200

    # Now create a new document with the same ID, save it, and then modify it
    doc = %{_id: "foo"}

    for _i <- 0..9 do
      {:ok, _} = create_doc(db_name, doc)
      resp = Couch.get("/#{db_name}/foo")

      updated_doc =
        resp.body
        |> Map.put("a", "baz")

      resp = Couch.put("/#{db_name}/foo", body: updated_doc)
      assert resp.status_code == 201
      rev = resp.body["rev"]
      resp = Couch.delete("/#{db_name}/foo?rev=#{rev}")
      assert resp.status_code == 200
    end
  end

  @tag :with_db
  test "COUCHDB-292 - recreate a deleted document", context do
    db_name = context[:db_name]
    # First create a new document with the ID "foo", and delete it again
    doc = %{_id: "foo", a: "bar", b: 42}
    {:ok, resp} = create_doc(db_name, doc)
    first_rev = resp.body["rev"]

    resp = Couch.delete("/#{db_name}/foo?rev=#{first_rev}")
    assert resp.status_code == 200

    # COUCHDB-292 now attempt to save the document with a prev that's since
    # been deleted and this should generate a conflict exception
    updated_doc =
      doc
      |> Map.put(:_rev, first_rev)

    resp = Couch.put("/#{db_name}/foo", body: updated_doc)
    assert resp.status_code == 409

    # same as before, but with binary
    bin_att_doc = %{
      _id: "foo",
      _rev: first_rev,
      _attachments: %{
        "foo.txt": %{
          content_type: "text/plain",
          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
        }
      }
    }

    resp = Couch.put("/#{db_name}/foo", body: bin_att_doc)
    assert resp.status_code == 409
  end

  @tag :with_db
  test "Recreate a deleted document with non-exsistant rev", context do
    db_name = context[:db_name]

    doc = %{_id: "foo", a: "bar", b: 42}
    {:ok, resp} = create_doc(db_name, doc)
    first_rev = resp.body["rev"]

    resp = Couch.delete("/#{db_name}/foo?rev=#{first_rev}")
    assert resp.status_code == 200

    # random non-existant prev rev
    updated_doc =
      doc
      |> Map.put(:_rev, "1-asfafasdf")

    resp = Couch.put("/#{db_name}/foo", body: updated_doc)
    assert resp.status_code == 409

    # random non-existant prev rev with bin
    bin_att_doc = %{
      _id: "foo",
      _rev: "1-aasasfasdf",
      _attachments: %{
        "foo.txt": %{
          content_type: "text/plain",
          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
        }
      }
    }

    resp = Couch.put("/#{db_name}/foo", body: bin_att_doc)
    assert resp.status_code == 409
  end

  @tag :with_db
  test "COUCHDB-1265 - changes feed after we try and break the update_seq tree",
       context do
    db_name = context[:db_name]

    # Test COUCHDB-1265 - Reinserting an old revision into the revision tree causes
    # duplicates in the update_seq tree.
    revs = create_rev_doc(db_name, "a", 3)

    resp =
      Couch.put("/#{db_name}/a",
        body: Enum.at(revs, 0),
        query: [new_edits: false]
      )

    assert resp.status_code == 201

    resp =
      Couch.put("/#{db_name}/a",
        body: Enum.at(revs, -1)
      )

    assert resp.status_code == 201

    resp = Couch.get("/#{db_name}/_changes")
    assert resp.status_code == 200

    assert length(resp.body["results"]) == 1
  end

  # function to create a doc with multiple revisions
  defp create_rev_doc(db_name, id, num_revs) do
    doc = %{_id: id, count: 0}
    {:ok, resp} = create_doc(db_name, doc)
    create_rev_doc(db_name, id, num_revs, [Map.put(doc, :_rev, resp.body["rev"])])
  end

  defp create_rev_doc(db_name, id, num_revs, revs) do
    if length(revs) < num_revs do
      doc = %{_id: id, _rev: Enum.at(revs, -1)[:_rev], count: length(revs)}
      {:ok, resp} = create_doc(db_name, doc)

      create_rev_doc(
        db_name,
        id,
        num_revs,
        revs ++ [Map.put(doc, :_rev, resp.body["rev"])]
      )
    else
      revs
    end
  end
end