1
2
3
4
5
6
7
8
9
10
11
12
13 package cdb
14
15 import (
16 "context"
17 "net/http"
18 "os"
19 "regexp"
20 "runtime"
21 "testing"
22
23 "gitlab.com/flimzy/testy"
24
25 "github.com/go-kivik/kivik/v4"
26 internal "github.com/go-kivik/kivik/v4/int/errors"
27 "github.com/go-kivik/kivik/v4/x/fsdb/filesystem"
28 )
29
30 const isGopherJS117 = runtime.GOARCH == "js"
31
32 func TestDocumentPersist(t *testing.T) {
33 if isGopherJS117 {
34 t.Skip("Tests broken for GopherJS 1.17")
35 }
36 type tt struct {
37 path string
38 doc *Document
39 status int
40 err string
41 }
42 tests := testy.NewTable()
43 tests.Add("nil doc", func(t *testing.T) interface{} {
44 var tmpdir string
45 tests.Cleanup(testy.TempDir(t, &tmpdir))
46
47 return tt{
48 path: tmpdir,
49 status: http.StatusBadRequest,
50 err: "document has no revisions",
51 }
52 })
53 tests.Add("no revs", func(t *testing.T) interface{} {
54 var tmpdir string
55 tests.Cleanup(testy.TempDir(t, &tmpdir))
56
57 cdb := New(tmpdir, filesystem.Default())
58
59 return tt{
60 path: tmpdir,
61 doc: cdb.NewDocument("foo"),
62 status: http.StatusBadRequest,
63 err: "document has no revisions",
64 }
65 })
66 tests.Add("new doc, one rev", func(t *testing.T) interface{} {
67 var tmpdir string
68 tests.Cleanup(testy.TempDir(t, &tmpdir))
69
70 cdb := New(tmpdir, filesystem.Default())
71 doc := cdb.NewDocument("foo")
72 rev, err := cdb.NewRevision(map[string]string{
73 "value": "bar",
74 })
75 if err != nil {
76 t.Fatal(err)
77 }
78 if _, err := doc.AddRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
79 t.Fatal(err)
80 }
81
82 return tt{
83 path: tmpdir,
84 doc: doc,
85 }
86 })
87 tests.Add("update existing doc", func(t *testing.T) interface{} {
88 tmpdir := testy.CopyTempDir(t, "testdata/persist.update", 0)
89 tests.Cleanup(func() error {
90 return os.RemoveAll(tmpdir)
91 })
92
93 cdb := New(tmpdir)
94 doc, err := cdb.OpenDocID("foo", kivik.Params(nil))
95 if err != nil {
96 t.Fatal(err)
97 }
98 rev, err := cdb.NewRevision(map[string]interface{}{
99 "_rev": "1-xxx",
100 "value": "bar",
101 "_revisions": map[string]interface{}{
102 "start": 2,
103 "ids": []string{"yyy", "xxx"},
104 },
105 })
106 if err != nil {
107 t.Fatal(err)
108 }
109 if _, err := doc.AddRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
110 t.Fatal(err)
111 }
112
113 return tt{
114 path: tmpdir,
115 doc: doc,
116 }
117 })
118 tests.Add("update existing doc with attachments", func(t *testing.T) interface{} {
119 tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
120 tests.Cleanup(func() error {
121 return os.RemoveAll(tmpdir)
122 })
123
124 cdb := New(tmpdir)
125 doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
126 if err != nil {
127 t.Fatal(err)
128 }
129 rev, err := cdb.NewRevision(map[string]interface{}{
130 "_rev": "1-xxx",
131 "value": "bar",
132 "_revisions": map[string]interface{}{
133 "start": 2,
134 "ids": []string{"yyy", "xxx"},
135 },
136 "_attachments": map[string]interface{}{
137 "bar.txt": map[string]interface{}{
138 "content_type": "text/plain",
139 "data": []byte("Additional content"),
140 },
141 "foo.txt": map[string]interface{}{
142 "content_type": "text/plain",
143 "stub": true,
144 },
145 },
146 })
147 if err != nil {
148 t.Fatal(err)
149 }
150 if _, err := doc.addRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
151 t.Fatal(err)
152 }
153
154 return tt{
155 path: tmpdir,
156 doc: doc,
157 }
158 })
159
160 tests.Run(t, func(t *testing.T, tt tt) {
161 err := tt.doc.persist(context.TODO())
162 if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
163 t.Error(d)
164 }
165 if err != nil {
166 return
167 }
168 if d := testy.DiffInterface(testy.Snapshot(t), tt.doc, tmpdirRE(tt.path)); d != nil {
169 t.Error(d)
170 }
171 if d := testy.DiffAsJSON(testy.Snapshot(t, "fs"), testy.JSONDir{
172 Path: tt.path,
173 NoMD5Sum: true,
174 FileContent: true,
175 }); d != nil {
176 t.Error(d)
177 }
178 })
179 }
180
181 func tmpdirRE(path string) testy.Replacement {
182 return testy.Replacement{
183 Regexp: regexp.MustCompile(`len=\d+\) "` + regexp.QuoteMeta(path)),
184 Replacement: `len=X) "<tmpdir>`,
185 }
186 }
187
188 func TestDocumentAddRevision(t *testing.T) {
189 if isGopherJS117 {
190 t.Skip("Tests broken for GopherJS 1.17")
191 }
192
193 type tt struct {
194 path string
195 doc *Document
196 rev *Revision
197 options kivik.Option
198 status int
199 err string
200 expected string
201 }
202 tests := testy.NewTable()
203 tests.Add("stub with bad digest", func(t *testing.T) interface{} {
204 tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
205 tests.Cleanup(func() error {
206 return os.RemoveAll(tmpdir)
207 })
208
209 cdb := New(tmpdir)
210 doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
211 if err != nil {
212 t.Fatal(err)
213 }
214 rev, err := cdb.NewRevision(map[string]interface{}{
215 "_rev": "1-xxx",
216 "value": "bar",
217 "_revisions": map[string]interface{}{
218 "start": 2,
219 "ids": []string{"yyy", "xxx"},
220 },
221 "_attachments": map[string]interface{}{
222 "foo.txt": map[string]interface{}{
223 "content_type": "text/plain",
224 "stub": true,
225 "digest": "md5-asdf",
226 },
227 },
228 })
229 if err != nil {
230 t.Fatal(err)
231 }
232
233 return tt{
234 path: tmpdir,
235 doc: doc,
236 rev: rev,
237 status: http.StatusBadRequest,
238 err: "invalid attachment data for foo.txt",
239 }
240 })
241 tests.Add("stub with wrong revpos", func(t *testing.T) interface{} {
242 tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
243 tests.Cleanup(func() error {
244 return os.RemoveAll(tmpdir)
245 })
246
247 cdb := New(tmpdir)
248 doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
249 if err != nil {
250 t.Fatal(err)
251 }
252 rev, err := cdb.NewRevision(map[string]interface{}{
253 "_rev": "1-xxx",
254 "value": "bar",
255 "_revisions": map[string]interface{}{
256 "start": 2,
257 "ids": []string{"yyy", "xxx"},
258 },
259 "_attachments": map[string]interface{}{
260 "foo.txt": map[string]interface{}{
261 "content_type": "text/plain",
262 "stub": true,
263 "revpos": 6,
264 },
265 },
266 })
267 if err != nil {
268 t.Fatal(err)
269 }
270
271 return tt{
272 path: tmpdir,
273 doc: doc,
274 rev: rev,
275 status: http.StatusBadRequest,
276 err: "invalid attachment data for foo.txt",
277 }
278 })
279 tests.Add("stub with 0 revpos", func(t *testing.T) interface{} {
280 tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
281 tests.Cleanup(func() error {
282 return os.RemoveAll(tmpdir)
283 })
284
285 cdb := New(tmpdir)
286 doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
287 if err != nil {
288 t.Fatal(err)
289 }
290 rev, err := cdb.NewRevision(map[string]interface{}{
291 "_rev": "1-xxx",
292 "value": "bar",
293 "_revisions": map[string]interface{}{
294 "start": 2,
295 "ids": []string{"yyy", "xxx"},
296 },
297 "_attachments": map[string]interface{}{
298 "foo.txt": map[string]interface{}{
299 "content_type": "text/plain",
300 "stub": true,
301 "revpos": 0,
302 },
303 },
304 })
305 if err != nil {
306 t.Fatal(err)
307 }
308
309 return tt{
310 path: tmpdir,
311 doc: doc,
312 rev: rev,
313 status: http.StatusBadRequest,
314 err: "invalid attachment data for foo.txt",
315 }
316 })
317 tests.Add("upload attachment", func(t *testing.T) interface{} {
318 var tmpdir string
319 tests.Cleanup(testy.TempDir(t, &tmpdir))
320
321 cdb := New(tmpdir)
322 doc := cdb.NewDocument("foo")
323 rev, err := cdb.NewRevision(map[string]interface{}{
324 "value": "bar",
325 "_revisions": map[string]interface{}{
326 "start": 2,
327 "ids": []string{"yyy", "xxx"},
328 },
329 "_attachments": map[string]interface{}{
330 "!foo.txt": map[string]interface{}{
331 "content_type": "text/plain",
332 "data": []byte("some test content"),
333 },
334 },
335 })
336 if err != nil {
337 t.Fatal(err)
338 }
339
340 return tt{
341 path: tmpdir,
342 doc: doc,
343 rev: rev,
344 expected: "1-1472ad25836971f236294ad7b19d9f65",
345 }
346 })
347 tests.Add("re-upload identical attachment", func(t *testing.T) interface{} {
348 tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
349 tests.Cleanup(func() error {
350 return os.RemoveAll(tmpdir)
351 })
352
353 cdb := New(tmpdir)
354 doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
355 if err != nil {
356 t.Fatal(err)
357 }
358 rev, err := cdb.NewRevision(map[string]interface{}{
359 "_rev": "1-xxx",
360 "_attachments": map[string]interface{}{
361 "foo.txt": map[string]interface{}{
362 "content_type": "text/plain",
363 "data": []byte("Test content\n"),
364 },
365 },
366 })
367 if err != nil {
368 t.Fatal(err)
369 }
370
371 return tt{
372 path: tmpdir,
373 doc: doc,
374 rev: rev,
375 expected: "2-61afc657ebc34041a2568f5d5ab9fc71",
376 }
377 })
378
379 tests.Run(t, func(t *testing.T, tt tt) {
380 opts := tt.options
381 if opts == nil {
382 opts = kivik.Params(nil)
383 }
384 revid, err := tt.doc.addRevision(context.TODO(), tt.rev, opts)
385 if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
386 t.Error(d)
387 }
388 if err != nil {
389 return
390 }
391 if revid != tt.expected {
392 t.Errorf("Unexpected revd: %s", revid)
393 }
394 if d := testy.DiffInterface(testy.Snapshot(t), tt.doc, tmpdirRE(tt.path)); d != nil {
395 t.Error(d)
396 }
397 if d := testy.DiffAsJSON(testy.Snapshot(t, "fs"), testy.JSONDir{
398 Path: tt.path,
399 NoMD5Sum: true,
400 FileContent: true,
401 }); d != nil {
402 t.Error(d)
403 }
404 })
405 }
406
View as plain text