1
2
3
4
5
6
7
8
9
10
11
12
13 package fs
14
15 import (
16 "context"
17 "net/http"
18 "os"
19 "path/filepath"
20 "testing"
21
22 "gitlab.com/flimzy/testy"
23
24 "github.com/go-kivik/kivik/v4"
25 internal "github.com/go-kivik/kivik/v4/int/errors"
26 "github.com/go-kivik/kivik/v4/x/fsdb/filesystem"
27 )
28
29 func TestPut(t *testing.T) {
30 if isGopherJS117 {
31 t.Skip("Tests broken for GopherJS 1.17")
32 }
33
34 type tt struct {
35 fs filesystem.Filesystem
36 path string
37 dbname string
38 id string
39 doc interface{}
40 options kivik.Option
41 status int
42 err string
43 expected string
44 }
45 tests := testy.NewTable()
46 tests.Add("invalid docID", tt{
47 path: "doesntmatter",
48 dbname: "doesntmatter",
49 id: "_foo",
50 status: http.StatusBadRequest,
51 err: "only reserved document ids may start with underscore",
52 })
53 tests.Add("invalid document", tt{
54 path: "doesntmatter",
55 dbname: "doesntmatter",
56 id: "foo",
57 doc: make(chan int),
58 status: http.StatusBadRequest,
59 err: "json: unsupported type: chan int",
60 })
61 tests.Add("create with revid", func(t *testing.T) interface{} {
62 tmpdir := tempDir(t)
63 tests.Cleanup(cleanTmpdir(tmpdir))
64 if err := os.Mkdir(filepath.Join(tmpdir, "foo"), 0o777); err != nil {
65 t.Fatal(err)
66 }
67
68 return tt{
69 path: tmpdir,
70 dbname: "foo",
71 id: "foo",
72 doc: map[string]string{"foo": "bar", "_rev": "1-xxx"},
73 status: http.StatusConflict,
74 err: "document update conflict",
75 }
76 })
77 tests.Add("simple create", func(t *testing.T) interface{} {
78 tmpdir := tempDir(t)
79 tests.Cleanup(cleanTmpdir(tmpdir))
80 if err := os.Mkdir(filepath.Join(tmpdir, "foo"), 0o777); err != nil {
81 t.Fatal(err)
82 }
83
84 return tt{
85 path: tmpdir,
86 dbname: "foo",
87 id: "foo",
88 doc: map[string]string{"foo": "bar"},
89 expected: "1-04edfaf9abdaed3c0accf6c463e78fd4",
90 }
91 })
92 tests.Add("update conflict, doc key", func(t *testing.T) interface{} {
93 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
94 tests.Cleanup(cleanTmpdir(tmpdir))
95
96 return tt{
97 path: tmpdir,
98 dbname: "db_put",
99 id: "foo",
100 doc: map[string]string{"foo": "bar", "_rev": "2-asdf"},
101 status: http.StatusConflict,
102 err: "document update conflict",
103 }
104 })
105 tests.Add("update conflict, options", func(t *testing.T) interface{} {
106 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
107 tests.Cleanup(cleanTmpdir(tmpdir))
108
109 return tt{
110 path: tmpdir,
111 dbname: "db_put",
112 id: "foo",
113 doc: map[string]string{"foo": "bar"},
114 options: kivik.Rev("2-asdf"),
115 status: http.StatusConflict,
116 err: "document update conflict",
117 }
118 })
119 tests.Add("no explicit rev", func(t *testing.T) interface{} {
120 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
121 tests.Cleanup(cleanTmpdir(tmpdir))
122
123 return tt{
124 path: tmpdir,
125 dbname: "db_put",
126 id: "foo",
127 doc: map[string]string{"foo": "bar"},
128 status: http.StatusConflict,
129 err: "document update conflict",
130 }
131 })
132 tests.Add("revs mismatch", tt{
133 path: "/tmp",
134 dbname: "doesntmatter",
135 id: "foo",
136 doc: map[string]string{"foo": "bar", "_rev": "2-asdf"},
137 options: kivik.Rev("3-asdf"),
138 status: http.StatusBadRequest,
139 err: "document rev from request body and query string have different values",
140 })
141 tests.Add("proper update", func(t *testing.T) interface{} {
142 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
143 tests.Cleanup(cleanTmpdir(tmpdir))
144
145 return tt{
146 path: tmpdir,
147 dbname: "db_put",
148 id: "foo",
149 doc: map[string]string{"foo": "quxx", "_rev": "1-beea34a62a215ab051862d1e5d93162e"},
150 expected: "2-ff3a4f106331244679a6cac83a74ae48",
151 }
152 })
153 tests.Add("design doc", func(t *testing.T) interface{} {
154 tmpdir := tempDir(t)
155 tests.Cleanup(cleanTmpdir(tmpdir))
156 if err := os.Mkdir(filepath.Join(tmpdir, "foo"), 0o777); err != nil {
157 t.Fatal(err)
158 }
159
160 return tt{
161 path: tmpdir,
162 dbname: "foo",
163 id: "_design/foo",
164 doc: map[string]string{"foo": "bar"},
165 expected: "1-04edfaf9abdaed3c0accf6c463e78fd4",
166 }
167 })
168 tests.Add("invalid doc id", tt{
169 path: "/tmp",
170 dbname: "doesntmatter",
171 id: "_oink",
172 doc: map[string]string{"foo": "bar"},
173 status: http.StatusBadRequest,
174 err: "only reserved document ids may start with underscore",
175 })
176 tests.Add("invalid attachments", tt{
177 path: "/tmp",
178 dbname: "doesntmatter",
179 id: "foo",
180 doc: map[string]interface{}{
181 "foo": "bar",
182 "_attachments": 123,
183 },
184 status: http.StatusBadRequest,
185 err: "json: cannot unmarshal number into Go struct field RevMeta._attachments of type map[string]*cdb.Attachment",
186 })
187 tests.Add("attachment", func(t *testing.T) interface{} {
188 tmpdir := tempDir(t)
189 tests.Cleanup(cleanTmpdir(tmpdir))
190 if err := os.Mkdir(filepath.Join(tmpdir, "foo"), 0o777); err != nil {
191 t.Fatal(err)
192 }
193
194 return tt{
195 path: tmpdir,
196 dbname: "foo",
197 id: "foo",
198 doc: map[string]interface{}{
199 "foo": "bar",
200 "_attachments": map[string]interface{}{
201 "foo.txt": map[string]interface{}{
202 "content_type": "text/plain",
203 "data": []byte("Testing"),
204 },
205 },
206 },
207 expected: "1-c706e75b505ddddeed04b959cfcb0ace",
208 }
209 })
210 tests.Add("new_edits=false, no rev", func(t *testing.T) interface{} {
211 tmpdir := tempDir(t)
212 tests.Cleanup(cleanTmpdir(tmpdir))
213 if err := os.Mkdir(filepath.Join(tmpdir, "foo"), 0o777); err != nil {
214 t.Fatal(err)
215 }
216
217 return tt{
218 path: tmpdir,
219 dbname: "foo",
220 id: "foo",
221 doc: map[string]string{
222 "foo": "bar",
223 },
224 options: kivik.Param("new_edits", false),
225 status: http.StatusBadRequest,
226 err: "_rev required with new_edits=false",
227 }
228 })
229 tests.Add("new_edits=false, rev already exists", func(t *testing.T) interface{} {
230 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
231 tests.Cleanup(cleanTmpdir(tmpdir))
232
233 return tt{
234 path: tmpdir,
235 dbname: "db_put",
236 id: "foo",
237 doc: map[string]string{
238 "_rev": "1-beea34a62a215ab051862d1e5d93162e",
239 "foo": "bar",
240 },
241 options: kivik.Param("new_edits", false),
242 expected: "1-beea34a62a215ab051862d1e5d93162e",
243 }
244 })
245 tests.Add("new_edits=false", func(t *testing.T) interface{} {
246 tmpdir := testy.CopyTempDir(t, "testdata/db_put", 1)
247 tests.Cleanup(cleanTmpdir(tmpdir))
248
249 return tt{
250 path: tmpdir,
251 dbname: "db_put",
252 id: "foo",
253 doc: map[string]string{
254 "_rev": "1-other",
255 "foo": "bar",
256 },
257 options: kivik.Param("new_edits", false),
258 expected: "1-other",
259 }
260 })
261
262 tests.Run(t, func(t *testing.T, tt tt) {
263 if tt.path == "" {
264 t.Fatalf("path must be set")
265 }
266 fs := tt.fs
267 if fs == nil {
268 fs = filesystem.Default()
269 }
270 c := &client{root: tt.path, fs: fs}
271 db, err := c.newDB(tt.dbname)
272 if err != nil {
273 t.Fatal(err)
274 }
275 opts := tt.options
276 if opts == nil {
277 opts = kivik.Params(nil)
278 }
279 rev, err := db.Put(context.Background(), tt.id, tt.doc, opts)
280 if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
281 t.Error(d)
282 }
283 if err != nil {
284 return
285 }
286 if rev != tt.expected {
287 t.Errorf("Unexpected rev returned: %s", rev)
288 }
289 if d := testy.DiffAsJSON(testy.Snapshot(t), testy.JSONDir{
290 Path: tt.path,
291 NoMD5Sum: true,
292 FileContent: true,
293 }); d != nil {
294 t.Error(d)
295 }
296 })
297 }
298
View as plain text