github.com/go-kivik/kivik/v4@v4.3.2/x/fsdb/put_test.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 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 }