github.com/go-kivik/kivik/v4@v4.3.2/x/fsdb/get_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 "errors" 18 "io" 19 "net/http" 20 "testing" 21 22 "gitlab.com/flimzy/testy" 23 24 "github.com/go-kivik/kivik/v4" 25 "github.com/go-kivik/kivik/v4/driver" 26 internal "github.com/go-kivik/kivik/v4/int/errors" 27 "github.com/go-kivik/kivik/v4/x/fsdb/filesystem" 28 ) 29 30 func TestGet(t *testing.T) { 31 type tt struct { 32 fs filesystem.Filesystem 33 setup func(*testing.T, *db) 34 final func(*testing.T, *db) 35 path, dbname string 36 id string 37 options kivik.Option 38 expected *driver.Document 39 status int 40 err string 41 } 42 tests := testy.NewTable() 43 tests.Add("no id", tt{ 44 path: "", 45 dbname: "foo", 46 status: http.StatusBadRequest, 47 err: "no docid specified", 48 }) 49 tests.Add("not found", tt{ 50 dbname: "asdf", 51 id: "foo", 52 status: http.StatusNotFound, 53 err: `^missing$`, 54 }) 55 tests.Add("forbidden", func(*testing.T) interface{} { 56 return tt{ 57 fs: &filesystem.MockFS{ 58 OpenFunc: func(_ string) (filesystem.File, error) { 59 return nil, statusError{status: http.StatusForbidden, error: errors.New("permission denied")} 60 }, 61 }, 62 dbname: "foo", 63 id: "foo", 64 status: http.StatusForbidden, 65 err: "permission denied$", 66 } 67 }) 68 tests.Add("success, no attachments", tt{ 69 path: "testdata", 70 dbname: "db_foo", 71 id: "noattach", 72 expected: &driver.Document{ 73 Rev: "1-xxxxxxxxxx", 74 }, 75 }) 76 tests.Add("success, attachment stub", tt{ 77 path: "testdata", 78 dbname: "db_foo", 79 id: "withattach", 80 expected: &driver.Document{ 81 Rev: "2-yyyyyyyyy", 82 }, 83 }) 84 tests.Add("success, include mp attachments", tt{ 85 path: "testdata", 86 dbname: "db_foo", 87 id: "withattach", 88 options: kivik.Param("attachments", true), 89 expected: &driver.Document{ 90 Rev: "2-yyyyyyyyy", 91 }, 92 }) 93 tests.Add("success, include inline attachments", tt{ 94 path: "testdata", 95 dbname: "db_foo", 96 id: "withattach", 97 options: kivik.Params(map[string]interface{}{ 98 "attachments": true, 99 "header:accept": "application/json", 100 }), 101 expected: &driver.Document{ 102 Rev: "2-yyyyyyyyy", 103 }, 104 }) 105 tests.Add("specify current rev", tt{ 106 path: "testdata", 107 dbname: "db_foo", 108 id: "noattach", 109 options: kivik.Rev("1-xxxxxxxxxx"), 110 expected: &driver.Document{ 111 Rev: "1-xxxxxxxxxx", 112 }, 113 }) 114 tests.Add("specify old rev", tt{ 115 path: "testdata", 116 dbname: "db_foo", 117 id: "withattach", 118 options: kivik.Rev("1-xxxxxxxxxx"), 119 expected: &driver.Document{ 120 Rev: "1-xxxxxxxxxx", 121 }, 122 }) 123 tests.Add("autorev", tt{ 124 path: "testdata", 125 dbname: "db_foo", 126 id: "autorev", 127 expected: &driver.Document{ 128 Rev: "6-", 129 }, 130 }) 131 tests.Add("intrev", tt{ 132 path: "testdata", 133 dbname: "db_foo", 134 id: "intrev", 135 expected: &driver.Document{ 136 Rev: "6-", 137 }, 138 }) 139 tests.Add("norev", tt{ 140 path: "testdata", 141 dbname: "db_foo", 142 id: "norev", 143 expected: &driver.Document{ 144 Rev: "1-", 145 }, 146 }) 147 tests.Add("noid", tt{ 148 path: "testdata", 149 dbname: "db_foo", 150 id: "noid", 151 expected: &driver.Document{ 152 Rev: "6-", 153 }, 154 }) 155 tests.Add("wrong id", tt{ 156 path: "testdata", 157 dbname: "db_foo", 158 id: "wrongid", 159 expected: &driver.Document{ 160 Rev: "6-", 161 }, 162 }) 163 tests.Add("yaml", tt{ 164 path: "testdata", 165 dbname: "db_foo", 166 id: "yamltest", 167 expected: &driver.Document{ 168 Rev: "3-", 169 }, 170 }) 171 tests.Add("specify current rev yaml", tt{ 172 path: "testdata", 173 dbname: "db_foo", 174 id: "yamltest", 175 options: kivik.Rev("3-"), 176 expected: &driver.Document{ 177 Rev: "3-", 178 }, 179 }) 180 tests.Add("specify old rev yaml", tt{ 181 path: "testdata", 182 dbname: "db_foo", 183 id: "yamltest", 184 options: kivik.Rev("2-xxx"), 185 expected: &driver.Document{ 186 Rev: "2-xxx", 187 }, 188 }) 189 tests.Add("specify bogus rev yaml", tt{ 190 path: "testdata", 191 dbname: "db_foo", 192 id: "yamltest", 193 options: kivik.Rev("1-oink"), 194 status: http.StatusNotFound, 195 err: "missing", 196 }) 197 tests.Add("ddoc yaml", tt{ 198 path: "testdata", 199 dbname: "db_foo", 200 id: "_design/users", 201 expected: &driver.Document{ 202 Rev: "2-", 203 }, 204 }) 205 tests.Add("ddoc rev yaml", tt{ 206 path: "testdata", 207 dbname: "db_foo", 208 id: "_design/users", 209 options: kivik.Rev("2-"), 210 expected: &driver.Document{ 211 Rev: "2-", 212 }, 213 }) 214 tests.Add("revs", tt{ 215 path: "testdata", 216 dbname: "db_foo", 217 id: "wrongid", 218 options: kivik.Param("revs", true), 219 expected: &driver.Document{ 220 Rev: "6-", 221 }, 222 }) 223 tests.Add("revs real", tt{ 224 path: "testdata", 225 dbname: "db_foo", 226 id: "noattach", 227 options: kivik.Param("revs", true), 228 expected: &driver.Document{ 229 Rev: "1-xxxxxxxxxx", 230 }, 231 }) 232 tests.Add("note--XkWjFv13acvjJTt-CGJJ8hXlWE", tt{ 233 path: "testdata", 234 dbname: "db_att", 235 id: "note--XkWjFv13acvjJTt-CGJJ8hXlWE", 236 expected: &driver.Document{ 237 Rev: "1-fbaabe005e0f4e5685a68f857c0777d6", 238 }, 239 }) 240 tests.Add("note--XkWjFv13acvjJTt-CGJJ8hXlWE + attachments", tt{ 241 path: "testdata", 242 dbname: "db_att", 243 id: "note--XkWjFv13acvjJTt-CGJJ8hXlWE", 244 options: kivik.Param("attachments", true), 245 expected: &driver.Document{ 246 Rev: "1-fbaabe005e0f4e5685a68f857c0777d6", 247 }, 248 }) 249 tests.Add("revs_info=true", tt{ 250 path: "testdata", 251 dbname: "db_foo", 252 id: "autorev", 253 options: kivik.Param("revs_info", true), 254 expected: &driver.Document{ 255 Rev: "6-", 256 }, 257 }) 258 tests.Add("revs, explicit", tt{ 259 path: "testdata", 260 dbname: "db_foo", 261 id: "withrevs", 262 options: kivik.Param("revs", true), 263 expected: &driver.Document{ 264 Rev: "8-asdf", 265 }, 266 }) 267 tests.Add("specify current rev, revs_info=true", tt{ 268 path: "testdata", 269 dbname: "db_foo", 270 id: "yamltest", 271 options: kivik.Params(map[string]interface{}{ 272 "rev": "3-", 273 "revs_info": true, 274 }), 275 expected: &driver.Document{ 276 Rev: "3-", 277 }, 278 }) 279 tests.Add("specify conflicting rev, revs_info=true", tt{ 280 path: "testdata", 281 dbname: "db_foo", 282 id: "yamltest", 283 options: kivik.Params(map[string]interface{}{ 284 "rev": "2-xxx", 285 "revs_info": true, 286 }), 287 expected: &driver.Document{ 288 Rev: "2-xxx", 289 }, 290 }) 291 tests.Add("specify rev, revs=true", tt{ 292 path: "testdata", 293 dbname: "db_foo", 294 id: "withrevs", 295 options: kivik.Params(map[string]interface{}{ 296 "rev": "8-asdf", 297 "revs": true, 298 }), 299 expected: &driver.Document{ 300 Rev: "8-asdf", 301 }, 302 }) 303 tests.Add("interrupted put", tt{ 304 // This tests a put which was aborted, leaving the attachments in 305 // {db}/.{docid}/{rev}/{filename}, while the winning rev is at 306 // the friendlier location of {db}/{docid}.{ext} 307 path: "testdata", 308 dbname: "db_foo", 309 id: "abortedput", 310 options: kivik.Param("attachments", true), 311 expected: &driver.Document{ 312 Rev: "2-yyyyyyyyy", 313 }, 314 }) 315 tests.Add("no winner, tied rev", tt{ 316 // This tests a put which was aborted, leaving the attachments in 317 // {db}/.{docid}/{rev}/{filename}, while the winning rev is at 318 // the friendlier location of {db}/{docid}.{ext} 319 path: "testdata", 320 dbname: "get_nowinner", 321 id: "foo", 322 expected: &driver.Document{ 323 Rev: "1-yyy", 324 }, 325 }) 326 tests.Add("no winner, greater rev", tt{ 327 // This tests a put which was aborted, leaving the attachments in 328 // {db}/.{docid}/{rev}/{filename}, while the winning rev is at 329 // the friendlier location of {db}/{docid}.{ext} 330 path: "testdata", 331 dbname: "get_nowinner", 332 id: "bar", 333 expected: &driver.Document{ 334 Rev: "2-yyy", 335 }, 336 }) 337 tests.Add("atts split between winning and revs dir", tt{ 338 path: "testdata", 339 dbname: "get_split_atts", 340 id: "foo", 341 options: kivik.Param("attachments", true), 342 expected: &driver.Document{ 343 Rev: "2-zzz", 344 }, 345 }) 346 tests.Add("atts split between two revs", tt{ 347 path: "testdata", 348 dbname: "get_split_atts", 349 id: "bar", 350 options: kivik.Param("attachments", true), 351 expected: &driver.Document{ 352 Rev: "2-yyy", 353 }, 354 }) 355 tests.Add("non-standard filenames", tt{ 356 path: "testdata", 357 dbname: "db_nonascii", 358 id: "note-i_ɪ", 359 expected: &driver.Document{ 360 Rev: "1-", 361 }, 362 }) 363 tests.Add("deleted doc", tt{ 364 path: "testdata", 365 dbname: "db_foo", 366 id: "deleted", 367 status: http.StatusNotFound, 368 err: "deleted", 369 }) 370 tests.Add("deleted doc, specific rev", tt{ 371 path: "testdata", 372 dbname: "db_foo", 373 id: "deleted", 374 options: kivik.Rev("3-"), 375 expected: &driver.Document{ 376 Rev: "3-", 377 }, 378 }) 379 380 tests.Run(t, func(t *testing.T, tt tt) { 381 dir := tt.path 382 if dir == "" { 383 dir = tempDir(t) 384 t.Cleanup(func() { 385 rmdir(t, dir) 386 }) 387 } 388 fs := tt.fs 389 if fs == nil { 390 fs = filesystem.Default() 391 } 392 c := &client{root: dir, fs: fs} 393 db, err := c.newDB(tt.dbname) 394 if err != nil { 395 t.Fatal(err) 396 } 397 if tt.setup != nil { 398 tt.setup(t, db) 399 } 400 opts := tt.options 401 if opts == nil { 402 opts = kivik.Params(nil) 403 } 404 result, err := db.Get(context.Background(), tt.id, opts) 405 if d := internal.StatusErrorDiffRE(tt.err, tt.status, err); d != "" { 406 t.Error(d) 407 } 408 if err != nil { 409 return 410 } 411 if d := testy.DiffAsJSON(testy.Snapshot(t), result.Body); d != nil { 412 t.Errorf("document:\n%s", d) 413 } 414 if result.Attachments != nil { 415 attachments := result.Attachments 416 t.Cleanup(func() { 417 _ = attachments.Close() 418 }) 419 att := &driver.Attachment{} 420 for { 421 if err := result.Attachments.Next(att); err != nil { 422 if err == io.EOF { 423 break 424 } 425 t.Fatal(err) 426 } 427 if d := testy.DiffText(&testy.File{Path: "testdata/" + testy.Stub(t) + "_" + att.Filename}, att.Content); d != nil { 428 t.Errorf("Attachment %s content:\n%s", att.Filename, d) 429 } 430 _ = att.Content.Close() 431 att.Content = nil 432 if d := testy.DiffAsJSON(&testy.File{Path: "testdata/" + testy.Stub(t) + "_" + att.Filename + "_struct"}, att); d != nil { 433 t.Errorf("Attachment %s struct:\n%s", att.Filename, d) 434 } 435 } 436 } 437 result.Body = nil 438 result.Attachments = nil 439 if d := testy.DiffInterface(tt.expected, result); d != nil { 440 t.Error(d) 441 } 442 if tt.final != nil { 443 tt.final(t, db) 444 } 445 }) 446 }