github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/vfs/vfs_test.go (about) 1 package vfs_test 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "context" 7 "errors" 8 "fmt" 9 "io" 10 "net/http/httptest" 11 "net/url" 12 "os" 13 "path" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/cozy/cozy-stack/model/vfs" 19 "github.com/cozy/cozy-stack/model/vfs/vfsafero" 20 "github.com/cozy/cozy-stack/model/vfs/vfsswift" 21 "github.com/cozy/cozy-stack/pkg/config/config" 22 "github.com/cozy/cozy-stack/pkg/consts" 23 "github.com/cozy/cozy-stack/pkg/couchdb" 24 "github.com/cozy/cozy-stack/pkg/crypto" 25 "github.com/cozy/cozy-stack/pkg/lock" 26 "github.com/cozy/cozy-stack/tests/testutils" 27 "github.com/ncw/swift/v2/swifttest" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "golang.org/x/sync/errgroup" 31 ) 32 33 var mutex lock.ErrorRWLocker 34 var diskQuota int64 35 36 type diskImpl struct{} 37 38 type H map[string]H 39 40 type contexter struct { 41 cluster int 42 domain string 43 prefix string 44 context string 45 } 46 47 func TestVfs(t *testing.T) { 48 if testing.Short() { 49 t.Skip("an instance is required for this test: test skipped due to the use of --short flag") 50 } 51 52 config.UseTestFile(t) 53 testutils.NeedCouchdb(t) 54 55 aferoFS := makeAferoFS(t) 56 swiftFS := makeSwiftFS(t) 57 58 var tests = []struct { 59 name string 60 fs vfs.VFS 61 }{ 62 {"afero", aferoFS}, 63 {"swift", swiftFS}, 64 } 65 66 for _, tt := range tests { 67 fs := tt.fs 68 69 t.Run(tt.name, func(t *testing.T) { 70 t.Run("DiskUsageIsInitiallyZero", func(t *testing.T) { 71 used, err := fs.DiskUsage() 72 assert.NoError(t, err) 73 assert.Equal(t, int64(0), used) 74 }) 75 76 t.Run("GetFileDocFromPathAtRoot", func(t *testing.T) { 77 doc, err := vfs.NewFileDoc("toto", "", -1, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 78 assert.NoError(t, err) 79 80 body := bytes.NewReader([]byte("hello !")) 81 82 file, err := fs.CreateFile(doc, nil) 83 assert.NoError(t, err) 84 85 n, err := io.Copy(file, body) 86 assert.NoError(t, err) 87 assert.Equal(t, len("hello !"), int(n)) 88 89 err = file.Close() 90 assert.NoError(t, err) 91 92 _, err = fs.FileByPath("/toto") 93 assert.NoError(t, err) 94 95 _, err = fs.FileByPath("/noooo") 96 assert.Error(t, err) 97 }) 98 99 t.Run("Remove", func(t *testing.T) { 100 err := vfs.Remove(fs, "foo/bar", fs.EnsureErased) 101 assert.Error(t, err) 102 assert.Equal(t, vfs.ErrNonAbsolutePath, err) 103 104 err = vfs.Remove(fs, "/foo", fs.EnsureErased) 105 assert.Error(t, err) 106 assert.Equal(t, "file does not exist", err.Error()) 107 108 _, err = vfs.Mkdir(fs, "/removeme", nil) 109 if !assert.NoError(t, err) { 110 err = vfs.Remove(fs, "/removeme", fs.EnsureErased) 111 assert.NoError(t, err) 112 } 113 }) 114 115 t.Run("RemoveAll", func(t *testing.T) { 116 origtree := H{ 117 "removemeall/": H{ 118 "dirchild1/": H{ 119 "food/": H{}, 120 "bard/": H{}, 121 }, 122 "dirchild2/": H{ 123 "foof": nil, 124 "barf": nil, 125 }, 126 "dirchild3/": H{}, 127 "filechild1": nil, 128 }, 129 } 130 _ = createTree(t, fs, origtree, consts.RootDirID) 131 132 err := vfs.RemoveAll(fs, "/removemeall", fs.EnsureErased) 133 require.NoError(t, err) 134 135 _, err = fs.DirByPath("/removemeall/dirchild1") 136 assert.Error(t, err) 137 _, err = fs.DirByPath("/removemeall") 138 assert.Error(t, err) 139 }) 140 141 t.Run("DiskUsage", func(t *testing.T) { 142 used, err := fs.DiskUsage() 143 assert.NoError(t, err) 144 assert.Equal(t, len("hello !"), int(used)) 145 }) 146 147 t.Run("GetFileDocFromPath", func(t *testing.T) { 148 dir, _ := vfs.NewDirDoc(fs, "container", "", nil) 149 err := fs.CreateDir(dir) 150 assert.NoError(t, err) 151 152 doc, err := vfs.NewFileDoc("toto", dir.ID(), -1, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 153 assert.NoError(t, err) 154 155 body := bytes.NewReader([]byte("hello !")) 156 157 file, err := fs.CreateFile(doc, nil) 158 assert.NoError(t, err) 159 160 n, err := io.Copy(file, body) 161 assert.NoError(t, err) 162 assert.Equal(t, len("hello !"), int(n)) 163 164 err = file.Close() 165 assert.NoError(t, err) 166 167 _, err = fs.FileByPath("/container/toto") 168 assert.NoError(t, err) 169 170 _, err = fs.FileByPath("/container/noooo") 171 assert.Error(t, err) 172 }) 173 174 t.Run("CreateGetAndModifyFile", func(t *testing.T) { 175 origtree := H{ 176 "createandget1/": H{ 177 "dirchild1/": H{ 178 "food/": H{}, 179 "bard/": H{}, 180 }, 181 "dirchild2/": H{ 182 "foof": nil, 183 "barf": nil, 184 }, 185 "dirchild3/": H{}, 186 "filechild1": nil, 187 }, 188 } 189 190 olddoc := createTree(t, fs, origtree, consts.RootDirID) 191 192 newname := "createandget2" 193 _, err := vfs.ModifyDirMetadata(fs, olddoc, &vfs.DocPatch{ 194 Name: &newname, 195 }) 196 require.NoError(t, err) 197 198 tree, err := fetchTree(fs, "/createandget2") 199 require.NoError(t, err) 200 201 assert.EqualValues(t, origtree["createandget1/"], tree["createandget2/"], "should have same tree") 202 203 fileBefore, err := fs.FileByPath("/createandget2/dirchild2/foof") 204 require.NoError(t, err) 205 206 newfilename := "foof.jpg" 207 _, err = vfs.ModifyFileMetadata(fs, fileBefore, &vfs.DocPatch{ 208 Name: &newfilename, 209 }) 210 require.NoError(t, err) 211 212 fileAfter, err := fs.FileByPath("/createandget2/dirchild2/foof.jpg") 213 require.NoError(t, err) 214 215 assert.Equal(t, "files", fileBefore.Class) 216 assert.Equal(t, "application/octet-stream", fileBefore.Mime) 217 assert.Equal(t, "image", fileAfter.Class) 218 assert.Equal(t, "image/jpeg", fileAfter.Mime) 219 }) 220 221 t.Run("UpdateDir", func(t *testing.T) { 222 origtree := H{ 223 "update1/": H{ 224 "dirchild1/": H{ 225 "food/": H{}, 226 "bard/": H{}, 227 }, 228 "dirchild2/": H{ 229 "foof": nil, 230 "barf": nil, 231 }, 232 "dirchild3/": H{}, 233 "filechild1": nil, 234 }, 235 } 236 237 doc1 := createTree(t, fs, origtree, consts.RootDirID) 238 239 newname := "update2" 240 _, err := vfs.ModifyDirMetadata(fs, doc1, &vfs.DocPatch{ 241 Name: &newname, 242 }) 243 require.NoError(t, err) 244 245 tree, err := fetchTree(fs, "/update2") 246 require.NoError(t, err) 247 248 if !assert.EqualValues(t, origtree["update1/"], tree["update2/"], "should have same tree") { 249 return 250 } 251 252 dirchild2, err := fs.DirByPath("/update2/dirchild2") 253 require.NoError(t, err) 254 255 dirchild3, err := fs.DirByPath("/update2/dirchild3") 256 require.NoError(t, err) 257 258 newfolid := dirchild2.ID() 259 _, err = vfs.ModifyDirMetadata(fs, dirchild3, &vfs.DocPatch{ 260 DirID: &newfolid, 261 }) 262 require.NoError(t, err) 263 264 tree, err = fetchTree(fs, "/update2") 265 require.NoError(t, err) 266 267 assert.EqualValues(t, H{ 268 "update2/": H{ 269 "dirchild1/": H{ 270 "bard/": H{}, 271 "food/": H{}, 272 }, 273 "filechild1": nil, 274 "dirchild2/": H{ 275 "barf": nil, 276 "foof": nil, 277 "dirchild3/": H{}, 278 }, 279 }, 280 }, tree) 281 }) 282 283 t.Run("EncodingOfDirName", func(t *testing.T) { 284 base := "encoding-dir" 285 nfc := "chaîne" 286 nfd := "chaîne" 287 288 origtree := H{base + "/": H{ 289 nfc: H{}, 290 nfd: H{}, 291 }} 292 _ = createTree(t, fs, origtree, consts.RootDirID) 293 294 f1, err := fs.FileByPath("/" + base + "/" + nfc) 295 require.NoError(t, err) 296 assert.Equal(t, nfc, f1.DocName) 297 298 f2, err := fs.FileByPath("/" + base + "/" + nfd) 299 require.NoError(t, err) 300 assert.Equal(t, nfd, f2.DocName) 301 302 assert.NotEqual(t, f1.DocID, f2.DocID) 303 }) 304 305 t.Run("ChangeEncodingOfDirName", func(t *testing.T) { 306 nfc := "dir-nfc-to-nfd-é" 307 nfd := "dir-nfc-to-nfd-é" 308 309 origtree := H{nfc + "/": H{ 310 "dirchild1/": H{ 311 "food/": H{}, 312 "bard/": H{}, 313 }, 314 "dirchild2/": H{}, 315 "filechild1": nil, 316 }} 317 doc := createTree(t, fs, origtree, consts.RootDirID) 318 319 newname := nfd 320 doc, err := vfs.ModifyDirMetadata(fs, doc, &vfs.DocPatch{ 321 Name: &newname, 322 }) 323 require.NoError(t, err) 324 d, err := fs.DirByPath("/" + newname) 325 require.NoError(t, err) 326 assert.Equal(t, newname, d.DocName) 327 328 newname = nfc 329 _, err = vfs.ModifyDirMetadata(fs, doc, &vfs.DocPatch{ 330 Name: &newname, 331 }) 332 require.NoError(t, err) 333 d, err = fs.DirByPath("/" + newname) 334 require.NoError(t, err) 335 assert.Equal(t, newname, d.DocName) 336 }) 337 338 t.Run("Walk", func(t *testing.T) { 339 walktree := H{ 340 "walk/": H{ 341 "dirchild1/": H{ 342 "food/": H{}, 343 "bard/": H{}, 344 }, 345 "dirchild2/": H{ 346 "foof": nil, 347 "barf": nil, 348 }, 349 "dirchild3/": H{}, 350 "filechild1": nil, 351 }, 352 } 353 354 _ = createTree(t, fs, walktree, consts.RootDirID) 355 356 walked := H{} 357 err := vfs.Walk(fs, "/walk", func(name string, dir *vfs.DirDoc, file *vfs.FileDoc, err error) error { 358 if !assert.NoError(t, err) { 359 return err 360 } 361 362 if dir != nil && !assert.Equal(t, dir.Fullpath, name) { 363 return fmt.Errorf("Bad fullpath") 364 } 365 366 if file != nil && !assert.True(t, strings.HasSuffix(name, file.DocName)) { 367 return fmt.Errorf("Bad fullpath") 368 } 369 370 walked[name] = nil 371 return nil 372 }) 373 assert.NoError(t, err) 374 375 expectedWalk := H{ 376 "/walk": nil, 377 "/walk/dirchild1": nil, 378 "/walk/dirchild1/food": nil, 379 "/walk/dirchild1/bard": nil, 380 "/walk/dirchild2": nil, 381 "/walk/dirchild2/foof": nil, 382 "/walk/dirchild2/barf": nil, 383 "/walk/dirchild3": nil, 384 "/walk/filechild1": nil, 385 } 386 387 assert.Equal(t, expectedWalk, walked) 388 }) 389 390 t.Run("WalkAlreadyLocked", func(t *testing.T) { 391 walktree := H{ 392 "walk2/": H{ 393 "dirchild1/": H{ 394 "food/": H{}, 395 "bard/": H{}, 396 }, 397 "dirchild2/": H{ 398 "foof": nil, 399 "barf": nil, 400 }, 401 "dirchild3/": H{}, 402 "filechild1": nil, 403 }, 404 } 405 406 _ = createTree(t, fs, walktree, consts.RootDirID) 407 408 done := make(chan bool) 409 410 go func() { 411 dir, err := fs.DirByPath("/walk2") 412 assert.NoError(t, err) 413 414 assert.NoError(t, mutex.Lock()) 415 defer mutex.Unlock() 416 417 err = vfs.WalkAlreadyLocked(fs, dir, func(_ string, _ *vfs.DirDoc, _ *vfs.FileDoc, err error) error { 418 assert.NoError(t, err) 419 return err 420 }) 421 assert.NoError(t, err) 422 done <- true 423 }() 424 425 select { 426 case <-done: 427 return 428 case <-time.After(3 * time.Second): 429 t.Fatal("deadline: WalkAlreadyLocked is probably trying to acquire the VFS lock") 430 } 431 }) 432 433 t.Run("ContentDisposition", func(t *testing.T) { 434 foo := vfs.ContentDisposition("inline", "foo.jpg") 435 assert.Equal(t, `inline; filename="foo.jpg"`, foo) 436 space := vfs.ContentDisposition("inline", "foo bar.jpg") 437 assert.Equal(t, `inline; filename="foobar.jpg"; filename*=UTF-8''foo%20bar.jpg`, space) 438 accents := vfs.ContentDisposition("inline", "héçà") 439 assert.Equal(t, `inline; filename="h"; filename*=UTF-8''h%C3%A9%C3%A7%C3%A0`, accents) 440 tab := vfs.ContentDisposition("inline", "tab\t") 441 assert.Equal(t, `inline; filename="tab"; filename*=UTF-8''tab%09`, tab) 442 emoji := vfs.ContentDisposition("inline", "🐧") 443 assert.Equal(t, `inline; filename="download"; filename*=UTF-8''%F0%9F%90%A7`, emoji) 444 }) 445 446 t.Run("Archive", func(t *testing.T) { 447 tree := H{ 448 "archive/": H{ 449 "foo.jpg": nil, 450 "foobar.jpg": nil, 451 "hello.jpg": nil, 452 "bar/": H{ 453 "baz/": H{ 454 "one.png": nil, 455 "two.png": nil, 456 }, 457 "z.gif": nil, 458 }, 459 "qux/": H{ 460 "quux": nil, 461 "courge": nil, 462 }, 463 }, 464 } 465 dirdoc := createTree(t, fs, tree, consts.RootDirID) 466 467 foobar, err := fs.FileByPath("/archive/foobar.jpg") 468 assert.NoError(t, err) 469 470 a := &vfs.Archive{ 471 Name: "test", 472 IDs: []string{ 473 foobar.ID(), 474 }, 475 Files: []string{ 476 "/archive/foo.jpg", 477 "/archive/bar", 478 }, 479 } 480 w := httptest.NewRecorder() 481 err = a.Serve(fs, w) 482 assert.NoError(t, err) 483 484 res := w.Result() 485 disposition := res.Header.Get("Content-Disposition") 486 assert.Equal(t, `attachment; filename="test.zip"`, disposition) 487 assert.Equal(t, "application/zip", res.Header.Get("Content-Type")) 488 489 b, err := io.ReadAll(res.Body) 490 assert.NoError(t, err) 491 z, err := zip.NewReader(bytes.NewReader(b), int64(len(b))) 492 assert.NoError(t, err) 493 assert.Equal(t, 7, len(z.File)) 494 zipfiles := H{} 495 for _, f := range z.File { 496 zipfiles[f.Name] = nil 497 } 498 assert.EqualValues(t, H{ 499 "test/foobar.jpg": nil, 500 "test/foo.jpg": nil, 501 "test/bar/": nil, 502 "test/bar/baz/": nil, 503 "test/bar/baz/one.png": nil, 504 "test/bar/baz/two.png": nil, 505 "test/bar/z.gif": nil, 506 }, zipfiles) 507 assert.NoError(t, fs.DestroyDirAndContent(dirdoc, fs.EnsureErased)) 508 }) 509 510 t.Run("CreateFileTooBig", func(t *testing.T) { 511 diskQuota = 1 << (1 * 10) // 1KB 512 defer func() { diskQuota = 0 }() 513 514 diskUsage1, err := fs.DiskUsage() 515 require.NoError(t, err) 516 517 doc1, err := vfs.NewFileDoc( 518 "too-big", 519 consts.RootDirID, 520 diskQuota+1, 521 nil, 522 "application/octet-stream", 523 "application", 524 time.Now(), 525 false, 526 false, 527 false, 528 nil, 529 ) 530 require.NoError(t, err) 531 532 _, err = fs.CreateFile(doc1, nil) 533 assert.Equal(t, vfs.ErrFileTooBig, err) 534 535 doc2, err := vfs.NewFileDoc( 536 "too-big", 537 consts.RootDirID, 538 diskQuota/2, 539 nil, 540 "application/octet-stream", 541 "application", 542 time.Now(), 543 false, 544 false, 545 false, 546 nil, 547 ) 548 require.NoError(t, err) 549 550 f, err := fs.CreateFile(doc2, nil) 551 assert.NoError(t, err) 552 assert.Error(t, f.Close()) 553 554 _, err = fs.FileByPath("/too-big") 555 assert.True(t, os.IsNotExist(err)) 556 557 doc3, err := vfs.NewFileDoc( 558 "too-big", 559 consts.RootDirID, 560 diskQuota/2, 561 nil, 562 "application/octet-stream", 563 "application", 564 time.Now(), 565 false, 566 false, 567 false, 568 nil, 569 ) 570 require.NoError(t, err) 571 572 f, err = fs.CreateFile(doc3, nil) 573 assert.NoError(t, err) 574 _, err = io.Copy(f, bytes.NewReader(crypto.GenerateRandomBytes(int(doc3.ByteSize)))) 575 assert.NoError(t, err) 576 err = f.Close() 577 assert.NoError(t, err) 578 579 diskUsage2, err := fs.DiskUsage() 580 assert.NoError(t, err) 581 assert.Equal(t, diskUsage1+diskQuota/2, diskUsage2) 582 583 doc4, err := vfs.NewFileDoc( 584 "too-big2", 585 consts.RootDirID, 586 -1, 587 nil, 588 "application/octet-stream", 589 "application", 590 time.Now(), 591 false, 592 false, 593 false, 594 nil, 595 ) 596 require.NoError(t, err) 597 598 f, err = fs.CreateFile(doc4, nil) 599 assert.NoError(t, err) 600 _, err = io.Copy(f, bytes.NewReader(crypto.GenerateRandomBytes(int(diskQuota/2+1)))) 601 assert.Error(t, err) 602 assert.Equal(t, vfs.ErrFileTooBig, err) 603 err = f.Close() 604 assert.Error(t, err) 605 assert.Equal(t, vfs.ErrFileTooBig, err) 606 607 _, err = fs.FileByPath("/too-big2") 608 assert.True(t, os.IsNotExist(err)) 609 610 root, err := fs.DirByPath("/") 611 require.NoError(t, err) 612 613 assert.NoError(t, fs.DestroyDirContent(root, fs.EnsureErased)) 614 }) 615 616 t.Run("CreateFileDocCopy", func(t *testing.T) { 617 md5sum := []byte("md5sum") 618 file, err := vfs.NewFileDoc("file", consts.RootDirID, -1, md5sum, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 619 require.NoError(t, err) 620 621 newname := "file (copy).txt" 622 newdoc := vfs.CreateFileDocCopy(file, "12345", newname) 623 assert.Empty(t, newdoc.DocID) 624 assert.Empty(t, newdoc.DocRev) 625 assert.Equal(t, "12345", newdoc.DirID) 626 assert.Equal(t, newname, newdoc.DocName) 627 assert.Equal(t, "text/plain", newdoc.Mime) 628 assert.Equal(t, "text", newdoc.Class) 629 assert.Equal(t, file.ByteSize, newdoc.ByteSize) 630 assert.Equal(t, file.MD5Sum, newdoc.MD5Sum) 631 assert.NotEqual(t, file.CreatedAt, newdoc.CreatedAt) 632 assert.Empty(t, newdoc.ReferencedBy) 633 }) 634 635 t.Run("ConflictName", func(t *testing.T) { 636 tree := H{"existing": nil} 637 _ = createTree(t, fs, tree, consts.RootDirID) 638 639 newname := vfs.ConflictName(fs, consts.RootDirID, "existing", true) 640 assert.Equal(t, "existing (2)", newname) 641 642 tree = H{"existing (2)": nil} 643 _ = createTree(t, fs, tree, consts.RootDirID) 644 645 newname = vfs.ConflictName(fs, consts.RootDirID, "existing", true) 646 assert.Equal(t, "existing (3)", newname) 647 648 tree = H{"existing (3)": nil} 649 _ = createTree(t, fs, tree, consts.RootDirID) 650 651 newname = vfs.ConflictName(fs, consts.RootDirID, "existing (3)", true) 652 assert.Equal(t, "existing (4)", newname) 653 654 tree = H{"existing (copy)": nil} 655 _ = createTree(t, fs, tree, consts.RootDirID) 656 657 newname = vfs.ConflictName(fs, consts.RootDirID, "existing (copy)", true) 658 assert.Equal(t, "existing (copy) (2)", newname) 659 }) 660 661 t.Run("CheckAvailableSpace", func(t *testing.T) { 662 diskQuota = 0 663 664 doc, err := vfs.NewFileDoc("toto", consts.RootDirID, 100, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 665 require.NoError(t, err) 666 _, _, _, err = vfs.CheckAvailableDiskSpace(fs, doc) 667 require.NoError(t, err) 668 669 diskQuota = 100 670 671 doc, err = vfs.NewFileDoc("toto", consts.RootDirID, 100, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 672 require.NoError(t, err) 673 _, _, _, err = vfs.CheckAvailableDiskSpace(fs, doc) 674 require.NoError(t, err) 675 676 doc, err = vfs.NewFileDoc("toto", consts.RootDirID, 101, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 677 require.NoError(t, err) 678 _, _, _, err = vfs.CheckAvailableDiskSpace(fs, doc) 679 assert.Error(t, err) 680 assert.Equal(t, vfs.ErrFileTooBig, err) 681 682 maxFileSize := fs.MaxFileSize() 683 if maxFileSize > 0 { 684 doc, err = vfs.NewFileDoc("toto", consts.RootDirID, maxFileSize+1, nil, "foo/bar", "foo", time.Now(), false, false, false, []string{}) 685 require.NoError(t, err) 686 _, _, _, err = vfs.CheckAvailableDiskSpace(fs, doc) 687 assert.Error(t, err) 688 assert.Equal(t, vfs.ErrMaxFileSize, err) 689 } 690 }) 691 }) 692 } 693 } 694 695 func (d *diskImpl) DiskQuota() int64 { 696 return diskQuota 697 } 698 699 func (h H) String() string { 700 return printH(h, "", 0) 701 } 702 703 func printH(h H, str string, count int) string { 704 for name, hh := range h { 705 for i := 0; i < count; i++ { 706 str += "\t" 707 } 708 str += fmt.Sprintf("%s:\n", name) 709 str += printH(hh, "", count+1) 710 } 711 return str 712 } 713 714 func createTree(t *testing.T, fs vfs.VFS, tree H, dirID string) *vfs.DirDoc { 715 t.Helper() 716 717 if tree == nil { 718 return nil 719 } 720 721 if dirID == "" { 722 dirID = consts.RootDirID 723 } 724 725 var err error 726 var dirdoc *vfs.DirDoc 727 for name, children := range tree { 728 if name[len(name)-1] == '/' { 729 dirdoc, err = vfs.NewDirDoc(fs, name[:len(name)-1], dirID, nil) 730 require.NoError(t, err) 731 732 err = fs.CreateDir(dirdoc) 733 require.NoError(t, err) 734 735 createTree(t, fs, children, dirdoc.ID()) 736 } else { 737 mime, class := vfs.ExtractMimeAndClassFromFilename(name) 738 filedoc, err := vfs.NewFileDoc(name, dirID, -1, nil, mime, class, time.Now(), false, false, false, nil) 739 require.NoError(t, err) 740 741 f, err := fs.CreateFile(filedoc, nil) 742 require.NoError(t, err) 743 744 err = f.Close() 745 require.NoError(t, err) 746 } 747 } 748 return dirdoc 749 } 750 751 func fetchTree(fs vfs.VFS, root string) (H, error) { 752 parent, err := fs.DirByPath(root) 753 if err != nil { 754 return nil, err 755 } 756 h, err := recFetchTree(fs, parent, path.Clean(root)) 757 if err != nil { 758 return nil, err 759 } 760 hh := make(H) 761 hh[parent.DocName+"/"] = h 762 return hh, nil 763 } 764 765 func recFetchTree(fs vfs.VFS, parent *vfs.DirDoc, name string) (H, error) { 766 h := make(H) 767 iter := fs.DirIterator(parent, nil) 768 for { 769 d, f, err := iter.Next() 770 if errors.Is(err, vfs.ErrIteratorDone) { 771 break 772 } 773 if err != nil { 774 return nil, err 775 } 776 if d != nil { 777 if path.Join(name, d.DocName) != d.Fullpath { 778 return nil, fmt.Errorf("Bad fullpath: %s instead of %s", d.Fullpath, path.Join(name, d.DocName)) 779 } 780 children, err := recFetchTree(fs, d, d.Fullpath) 781 if err != nil { 782 return nil, err 783 } 784 h[d.DocName+"/"] = children 785 } else { 786 h[f.DocName] = nil 787 } 788 } 789 return h, nil 790 } 791 792 func (c *contexter) DBCluster() int { return c.cluster } 793 func (c *contexter) DomainName() string { return c.domain } 794 func (c *contexter) DBPrefix() string { return c.prefix } 795 func (c *contexter) GetContextName() string { return c.context } 796 797 func makeAferoFS(t *testing.T) vfs.VFS { 798 t.Helper() 799 800 tempdir := t.TempDir() 801 802 db := &contexter{0, "swift.testvfs.example.org", "swift.testvfs.example.org", "cozy_beta"} 803 index := vfs.NewCouchdbIndexer(db) 804 mutex = config.Lock().ReadWrite(db, "vfs-afero-test") 805 aferoFs, err := vfsafero.New(db, index, &diskImpl{}, mutex, 806 &url.URL{Scheme: "file", Host: "localhost", Path: tempdir}, "io.cozy.vfs.test") 807 require.NoError(t, err) 808 809 require.NoError(t, couchdb.ResetDB(db, consts.Files)) 810 t.Cleanup(func() { _ = couchdb.DeleteDB(db, consts.Files) }) 811 812 g, _ := errgroup.WithContext(context.Background()) 813 couchdb.DefineIndexes(g, db, couchdb.IndexesByDoctype(consts.Files)) 814 couchdb.DefineViews(g, db, couchdb.ViewsByDoctype(consts.Files)) 815 816 require.NoError(t, g.Wait()) 817 require.NoError(t, aferoFs.InitFs()) 818 819 return aferoFs 820 } 821 822 func makeSwiftFS(t *testing.T) vfs.VFS { 823 t.Helper() 824 825 db := &contexter{0, "io.cozy.vfs.test", "io.cozy.vfs.test", "cozy_beta"} 826 index := vfs.NewCouchdbIndexer(db) 827 828 swiftSrv, err := swifttest.NewSwiftServer("localhost") 829 require.NoError(t, err, "failed to create swift server") 830 831 require.NoError(t, config.InitSwiftConnection(config.Fs{ 832 URL: &url.URL{ 833 Scheme: "swift", 834 Host: "localhost", 835 RawQuery: "UserName=swifttest&Password=swifttest&AuthURL=" + url.QueryEscape(swiftSrv.AuthURL), 836 }, 837 })) 838 839 mutex = config.Lock().ReadWrite(db, "vfs-swiftv3-test") 840 swiftFs, err := vfsswift.NewV3(db, index, &diskImpl{}, mutex) 841 require.NoError(t, err) 842 843 require.NoError(t, couchdb.ResetDB(db, consts.Files)) 844 845 g, _ := errgroup.WithContext(context.Background()) 846 couchdb.DefineIndexes(g, db, couchdb.IndexesByDoctype(consts.Files)) 847 couchdb.DefineViews(g, db, couchdb.ViewsByDoctype(consts.Files)) 848 require.NoError(t, g.Wait()) 849 850 require.NoError(t, swiftFs.InitFs()) 851 852 t.Cleanup(func() { 853 _ = couchdb.DeleteDB(db, consts.Files) 854 if swiftSrv != nil { 855 swiftSrv.Close() 856 } 857 }) 858 859 return swiftFs 860 }