github.com/cs3org/reva/v2@v2.27.7/internal/http/services/archiver/manager/archiver_test.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package manager 20 21 import ( 22 "archive/tar" 23 "archive/zip" 24 "bytes" 25 "context" 26 "errors" 27 "io" 28 "os" 29 "path" 30 "strings" 31 "testing" 32 33 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 34 downMock "github.com/cs3org/reva/v2/pkg/storage/utils/downloader/mock" 35 walkerMock "github.com/cs3org/reva/v2/pkg/storage/utils/walker/mock" 36 "github.com/cs3org/reva/v2/pkg/test" 37 ) 38 39 func UnTar(dir string, r io.Reader) error { 40 tr := tar.NewReader(r) 41 for { 42 hdr, err := tr.Next() 43 if err == io.EOF { 44 break // finish to read the archive 45 } 46 if err != nil { 47 return err 48 } 49 50 p := path.Join(dir, hdr.Name) 51 52 switch hdr.Typeflag { 53 case tar.TypeDir: 54 err = os.MkdirAll(p, 0755) 55 if err != nil { 56 return err 57 } 58 case tar.TypeReg: 59 d := path.Dir(p) 60 err := os.MkdirAll(d, 0755) 61 if err != nil { 62 return err 63 } 64 file, err := os.Create(p) 65 if err != nil { 66 return err 67 } 68 _, err = io.Copy(file, tr) 69 if err != nil { 70 return err 71 } 72 default: 73 return errors.New("not supported") 74 } 75 } 76 return nil 77 } 78 79 func TestCreateTar(t *testing.T) { 80 81 tests := []struct { 82 name string 83 src test.Dir 84 config Config 85 files []string 86 expected test.Dir 87 err error 88 }{ 89 { 90 name: "one file", 91 src: test.Dir{ 92 "foo": test.File{ 93 Content: "foo", 94 }, 95 }, 96 config: Config{ 97 MaxSize: 3, 98 MaxNumFiles: 1, 99 }, 100 files: []string{"foo"}, 101 expected: test.Dir{ 102 "foo": test.File{ 103 Content: "foo", 104 }, 105 }, 106 err: nil, 107 }, 108 { 109 name: "one big file", 110 src: test.Dir{ 111 "foo": test.File{ 112 Content: strings.Repeat("a", 1024*1024), 113 }, 114 }, 115 config: Config{ 116 MaxSize: 1024 * 1024 * 2, 117 MaxNumFiles: 1000, 118 }, 119 files: []string{"foo"}, 120 expected: test.Dir{ 121 "foo": test.File{ 122 Content: strings.Repeat("a", 1024*1024), 123 }, 124 }, 125 err: nil, 126 }, 127 { 128 name: "one file - error max files reached", 129 src: test.Dir{ 130 "foo": test.File{ 131 Content: "foo", 132 }, 133 }, 134 config: Config{ 135 MaxSize: 3, 136 MaxNumFiles: 0, 137 }, 138 files: []string{"foo"}, 139 expected: nil, 140 err: ErrMaxFileCount{}, 141 }, 142 { 143 name: "one file - error max size reached", 144 src: test.Dir{ 145 "foo": test.File{ 146 Content: "foo", 147 }, 148 }, 149 config: Config{ 150 MaxSize: 0, 151 MaxNumFiles: 1, 152 }, 153 files: []string{"foo"}, 154 expected: nil, 155 err: ErrMaxSize{}, 156 }, 157 { 158 name: "one folder empty", 159 src: test.Dir{ 160 "foo": test.Dir{}, 161 }, 162 config: Config{ 163 MaxSize: 1000, 164 MaxNumFiles: 1, 165 }, 166 files: []string{"foo"}, 167 expected: test.Dir{ 168 "foo": test.Dir{}, 169 }, 170 err: nil, 171 }, 172 { 173 name: "one folder empty - error max files reached", 174 src: test.Dir{ 175 "foo": test.Dir{}, 176 }, 177 config: Config{ 178 MaxSize: 1000, 179 MaxNumFiles: 0, 180 }, 181 files: []string{"foo"}, 182 expected: nil, 183 err: ErrMaxFileCount{}, 184 }, 185 { 186 name: "one folder - one file in", 187 src: test.Dir{ 188 "foo": test.Dir{ 189 "bar": test.File{ 190 Content: "bar", 191 }, 192 }, 193 }, 194 config: Config{ 195 MaxSize: 1000, 196 MaxNumFiles: 1000, 197 }, 198 files: []string{"foo"}, 199 expected: test.Dir{ 200 "foo": test.Dir{ 201 "bar": test.File{ 202 Content: "bar", 203 }, 204 }, 205 }, 206 err: nil, 207 }, 208 { 209 name: "multiple folders/files in root dir - tar all", 210 src: test.Dir{ 211 "foo": test.Dir{ 212 "bar": test.File{ 213 Content: "bar", 214 }, 215 }, 216 "foobar": test.File{ 217 Content: "foobar", 218 }, 219 "other_dir": test.Dir{ 220 "nested_dir": test.Dir{ 221 "foo": test.File{ 222 Content: "foo", 223 }, 224 "bar": test.File{ 225 Content: "bar", 226 }, 227 }, 228 "nested_file": test.File{ 229 Content: "nested_file", 230 }, 231 }, 232 }, 233 config: Config{ 234 MaxSize: 100000, 235 MaxNumFiles: 1000, 236 }, 237 files: []string{"foo", "foobar", "other_dir"}, 238 expected: test.Dir{ 239 "foo": test.Dir{ 240 "bar": test.File{ 241 Content: "bar", 242 }, 243 }, 244 "foobar": test.File{ 245 Content: "foobar", 246 }, 247 "other_dir": test.Dir{ 248 "nested_dir": test.Dir{ 249 "foo": test.File{ 250 Content: "foo", 251 }, 252 "bar": test.File{ 253 Content: "bar", 254 }, 255 }, 256 "nested_file": test.File{ 257 Content: "nested_file", 258 }, 259 }, 260 }, 261 err: nil, 262 }, 263 { 264 name: "multiple folders/files in root dir - tar partial", 265 src: test.Dir{ 266 "foo": test.Dir{ 267 "bar": test.File{ 268 Content: "bar", 269 }, 270 }, 271 "foobar": test.File{ 272 Content: "foobar", 273 }, 274 "other_dir": test.Dir{ 275 "nested_dir": test.Dir{ 276 "foo": test.File{ 277 Content: "foo", 278 }, 279 "bar": test.File{ 280 Content: "bar", 281 }, 282 }, 283 "nested_file": test.File{ 284 Content: "nested_file", 285 }, 286 }, 287 }, 288 config: Config{ 289 MaxSize: 100000, 290 MaxNumFiles: 1000, 291 }, 292 files: []string{"foo", "foobar"}, 293 expected: test.Dir{ 294 "foo": test.Dir{ 295 "bar": test.File{ 296 Content: "bar", 297 }, 298 }, 299 "foobar": test.File{ 300 Content: "foobar", 301 }, 302 }, 303 err: nil, 304 }, 305 { 306 name: "multiple folders/files in root dir - tar different levels", 307 src: test.Dir{ 308 "foo": test.Dir{ 309 "bar": test.File{ 310 Content: "bar", 311 }, 312 }, 313 "foobar": test.File{ 314 Content: "foobar", 315 }, 316 "other_dir": test.Dir{ 317 "nested_dir": test.Dir{ 318 "foo": test.File{ 319 Content: "foo", 320 }, 321 "bar": test.File{ 322 Content: "bar", 323 }, 324 }, 325 "nested_file": test.File{ 326 Content: "nested_file", 327 }, 328 }, 329 }, 330 config: Config{ 331 MaxSize: 100000, 332 MaxNumFiles: 1000, 333 }, 334 files: []string{"foobar", "other_dir/nested_dir/foo", "other_dir/nested_dir/bar"}, 335 expected: test.Dir{ 336 "foobar": test.File{ 337 Content: "foobar", 338 }, 339 "other_dir": test.Dir{ 340 "nested_dir": test.Dir{ 341 "foo": test.File{ 342 Content: "foo", 343 }, 344 "bar": test.File{ 345 Content: "bar", 346 }, 347 }, 348 }, 349 }, 350 err: nil, 351 }, 352 { 353 name: "multiple folders/files in root dir with extesions", 354 src: test.Dir{ 355 "foo": test.Dir{ 356 "bar.txt": test.File{ 357 Content: "qwerty\ntest", 358 }, 359 }, 360 "main.py": test.File{ 361 Content: "print(\"Hello world!\")\n", 362 }, 363 "other_dir": test.Dir{ 364 "images": test.Dir{ 365 "foo.png": test.File{ 366 Content: "<png content>", 367 }, 368 "bar.jpg": test.File{ 369 Content: "<jpg content>", 370 }, 371 }, 372 }, 373 }, 374 config: Config{ 375 MaxSize: 100000, 376 MaxNumFiles: 1000, 377 }, 378 files: []string{"foo/bar.txt", "main.py", "other_dir"}, 379 expected: test.Dir{ 380 "foo": test.Dir{ 381 "bar.txt": test.File{ 382 Content: "qwerty\ntest", 383 }, 384 }, 385 "main.py": test.File{ 386 Content: "print(\"Hello world!\")\n", 387 }, 388 "other_dir": test.Dir{ 389 "images": test.Dir{ 390 "foo.png": test.File{ 391 Content: "<png content>", 392 }, 393 "bar.jpg": test.File{ 394 Content: "<jpg content>", 395 }, 396 }, 397 }, 398 }, 399 err: nil, 400 }, 401 } 402 403 for _, tt := range tests { 404 405 t.Run(tt.name, func(t *testing.T) { 406 407 ctx := context.TODO() 408 409 tmpdir, cleanup, err := test.NewTestDir(tt.src) 410 if err != nil { 411 t.Fatal(err) 412 } 413 defer cleanup() 414 415 resources := []*provider.ResourceId{} 416 for _, f := range tt.files { 417 resources = append(resources, &provider.ResourceId{OpaqueId: path.Join(tmpdir, f)}) 418 } 419 420 w := walkerMock.NewWalker(tmpdir) 421 d := downMock.NewDownloader() 422 423 arch, err := NewArchiver(resources, w, d, tt.config) 424 if err != nil { 425 t.Fatal(err) 426 } 427 428 var tarFile bytes.Buffer 429 430 cl, err := arch.CreateTar(ctx, &tarFile) 431 if err != tt.err { 432 t.Fatalf("error result different from expected: got=%v, expected=%v", err, tt.err) 433 } 434 cl() 435 436 tarTmpDir, cleanup, err := test.TmpDir() 437 if err != nil { 438 t.Fatal(err) 439 } 440 defer cleanup() 441 442 err = UnTar(tarTmpDir, &tarFile) 443 if err != nil { 444 t.Fatal(err) 445 } 446 447 if tt.expected != nil { 448 expectedTmp, cleanup, err := test.NewTestDir(tt.expected) 449 if err != nil { 450 t.Fatal(err) 451 } 452 defer cleanup() 453 if !test.DirEquals(tarTmpDir, expectedTmp) { 454 t.Fatalf("untar dir %s different from expected %s", tarTmpDir, expectedTmp) 455 } 456 } 457 458 }) 459 460 } 461 462 } 463 464 func UnZip(dir string, r io.Reader) error { 465 // save the file temporarely 466 tmp, cleanup, err := test.TmpDir() 467 if err != nil { 468 return err 469 } 470 defer cleanup() 471 472 zipFile := path.Join(tmp, "tmp.zip") 473 zfile, err := os.Create(zipFile) 474 if err != nil { 475 return err 476 } 477 478 _, err = io.Copy(zfile, r) 479 if err != nil { 480 return err 481 } 482 zfile.Close() 483 484 // open the tmp zip file and read it 485 zr, err := zip.OpenReader(zipFile) 486 if err != nil { 487 return err 488 } 489 defer zr.Close() 490 491 for _, f := range zr.File { 492 493 p := path.Join(dir, f.Name) 494 495 d := path.Dir(p) 496 err := os.MkdirAll(d, 0755) 497 if err != nil { 498 return err 499 } 500 501 if strings.HasSuffix(f.Name, "/") { 502 // is a dir 503 err := os.Mkdir(p, 0755) 504 if err != nil { 505 return err 506 } 507 } else { 508 // is a regular file 509 file, err := os.Create(p) 510 if err != nil { 511 return err 512 } 513 514 rc, err := f.Open() 515 if err != nil { 516 return err 517 } 518 defer zr.Close() 519 _, err = io.Copy(file, rc) 520 if err != nil { 521 return err 522 } 523 } 524 } 525 return nil 526 } 527 528 func TestCreateZip(t *testing.T) { 529 530 tests := []struct { 531 name string 532 src test.Dir 533 config Config 534 files []string 535 expected test.Dir 536 err error 537 }{ 538 { 539 name: "one file", 540 src: test.Dir{ 541 "foo": test.File{ 542 Content: "foo", 543 }, 544 }, 545 config: Config{ 546 MaxSize: 3, 547 MaxNumFiles: 1, 548 }, 549 files: []string{"foo"}, 550 expected: test.Dir{ 551 "foo": test.File{ 552 Content: "foo", 553 }, 554 }, 555 err: nil, 556 }, 557 { 558 name: "one big file", 559 src: test.Dir{ 560 "foo": test.File{ 561 Content: strings.Repeat("a", 1024*1024), 562 }, 563 }, 564 config: Config{ 565 MaxSize: 1024 * 1024 * 2, 566 MaxNumFiles: 1000, 567 }, 568 files: []string{"foo"}, 569 expected: test.Dir{ 570 "foo": test.File{ 571 Content: strings.Repeat("a", 1024*1024), 572 }, 573 }, 574 err: nil, 575 }, 576 { 577 name: "one file - error max files reached", 578 src: test.Dir{ 579 "foo": test.File{ 580 Content: "foo", 581 }, 582 }, 583 config: Config{ 584 MaxSize: 3, 585 MaxNumFiles: 0, 586 }, 587 files: []string{"foo"}, 588 expected: nil, 589 err: ErrMaxFileCount{}, 590 }, 591 { 592 name: "one file - error max size reached", 593 src: test.Dir{ 594 "foo": test.File{ 595 Content: "foo", 596 }, 597 }, 598 config: Config{ 599 MaxSize: 0, 600 MaxNumFiles: 1, 601 }, 602 files: []string{"foo"}, 603 expected: nil, 604 err: ErrMaxSize{}, 605 }, 606 { 607 name: "one folder empty", 608 src: test.Dir{ 609 "foo": test.Dir{}, 610 }, 611 config: Config{ 612 MaxSize: 1000, 613 MaxNumFiles: 1, 614 }, 615 files: []string{"foo"}, 616 expected: test.Dir{ 617 "foo": test.Dir{}, 618 }, 619 err: nil, 620 }, 621 { 622 name: "one folder empty - error max files reached", 623 src: test.Dir{ 624 "foo": test.Dir{}, 625 }, 626 config: Config{ 627 MaxSize: 1000, 628 MaxNumFiles: 0, 629 }, 630 files: []string{"foo"}, 631 expected: nil, 632 err: ErrMaxFileCount{}, 633 }, 634 { 635 name: "one folder - one file in", 636 src: test.Dir{ 637 "foo": test.Dir{ 638 "bar": test.File{ 639 Content: "bar", 640 }, 641 }, 642 }, 643 config: Config{ 644 MaxSize: 1000, 645 MaxNumFiles: 1000, 646 }, 647 files: []string{"foo"}, 648 expected: test.Dir{ 649 "foo": test.Dir{ 650 "bar": test.File{ 651 Content: "bar", 652 }, 653 }, 654 }, 655 err: nil, 656 }, 657 { 658 name: "multiple folders/files in root dir - tar all", 659 src: test.Dir{ 660 "foo": test.Dir{ 661 "bar": test.File{ 662 Content: "bar", 663 }, 664 }, 665 "foobar": test.File{ 666 Content: "foobar", 667 }, 668 "other_dir": test.Dir{ 669 "nested_dir": test.Dir{ 670 "foo": test.File{ 671 Content: "foo", 672 }, 673 "bar": test.File{ 674 Content: "bar", 675 }, 676 }, 677 "nested_file": test.File{ 678 Content: "nested_file", 679 }, 680 }, 681 }, 682 config: Config{ 683 MaxSize: 100000, 684 MaxNumFiles: 1000, 685 }, 686 files: []string{"foo", "foobar", "other_dir"}, 687 expected: test.Dir{ 688 "foo": test.Dir{ 689 "bar": test.File{ 690 Content: "bar", 691 }, 692 }, 693 "foobar": test.File{ 694 Content: "foobar", 695 }, 696 "other_dir": test.Dir{ 697 "nested_dir": test.Dir{ 698 "foo": test.File{ 699 Content: "foo", 700 }, 701 "bar": test.File{ 702 Content: "bar", 703 }, 704 }, 705 "nested_file": test.File{ 706 Content: "nested_file", 707 }, 708 }, 709 }, 710 err: nil, 711 }, 712 { 713 name: "multiple folders/files in root dir - tar partial", 714 src: test.Dir{ 715 "foo": test.Dir{ 716 "bar": test.File{ 717 Content: "bar", 718 }, 719 }, 720 "foobar": test.File{ 721 Content: "foobar", 722 }, 723 "other_dir": test.Dir{ 724 "nested_dir": test.Dir{ 725 "foo": test.File{ 726 Content: "foo", 727 }, 728 "bar": test.File{ 729 Content: "bar", 730 }, 731 }, 732 "nested_file": test.File{ 733 Content: "nested_file", 734 }, 735 }, 736 }, 737 config: Config{ 738 MaxSize: 100000, 739 MaxNumFiles: 1000, 740 }, 741 files: []string{"foo", "foobar"}, 742 expected: test.Dir{ 743 "foo": test.Dir{ 744 "bar": test.File{ 745 Content: "bar", 746 }, 747 }, 748 "foobar": test.File{ 749 Content: "foobar", 750 }, 751 }, 752 err: nil, 753 }, 754 { 755 name: "multiple folders/files in root dir - tar different levels", 756 src: test.Dir{ 757 "foo": test.Dir{ 758 "bar": test.File{ 759 Content: "bar", 760 }, 761 }, 762 "foobar": test.File{ 763 Content: "foobar", 764 }, 765 "other_dir": test.Dir{ 766 "nested_dir": test.Dir{ 767 "foo": test.File{ 768 Content: "foo", 769 }, 770 "bar": test.File{ 771 Content: "bar", 772 }, 773 }, 774 "nested_file": test.File{ 775 Content: "nested_file", 776 }, 777 }, 778 }, 779 config: Config{ 780 MaxSize: 100000, 781 MaxNumFiles: 1000, 782 }, 783 files: []string{"foobar", "other_dir/nested_dir/foo", "other_dir/nested_dir/bar"}, 784 expected: test.Dir{ 785 "foobar": test.File{ 786 Content: "foobar", 787 }, 788 "other_dir": test.Dir{ 789 "nested_dir": test.Dir{ 790 "foo": test.File{ 791 Content: "foo", 792 }, 793 "bar": test.File{ 794 Content: "bar", 795 }, 796 }, 797 }, 798 }, 799 err: nil, 800 }, 801 { 802 name: "multiple folders/files in root dir with extesions", 803 src: test.Dir{ 804 "foo": test.Dir{ 805 "bar.txt": test.File{ 806 Content: "qwerty\ntest", 807 }, 808 }, 809 "main.py": test.File{ 810 Content: "print(\"Hello world!\")\n", 811 }, 812 "other_dir": test.Dir{ 813 "images": test.Dir{ 814 "foo.png": test.File{ 815 Content: "<png content>", 816 }, 817 "bar.jpg": test.File{ 818 Content: "<jpg content>", 819 }, 820 }, 821 }, 822 }, 823 config: Config{ 824 MaxSize: 100000, 825 MaxNumFiles: 1000, 826 }, 827 files: []string{"foo/bar.txt", "main.py", "other_dir"}, 828 expected: test.Dir{ 829 "foo": test.Dir{ 830 "bar.txt": test.File{ 831 Content: "qwerty\ntest", 832 }, 833 }, 834 "main.py": test.File{ 835 Content: "print(\"Hello world!\")\n", 836 }, 837 "other_dir": test.Dir{ 838 "images": test.Dir{ 839 "foo.png": test.File{ 840 Content: "<png content>", 841 }, 842 "bar.jpg": test.File{ 843 Content: "<jpg content>", 844 }, 845 }, 846 }, 847 }, 848 err: nil, 849 }, 850 } 851 852 for _, tt := range tests { 853 854 t.Run(tt.name, func(t *testing.T) { 855 856 ctx := context.TODO() 857 858 tmpdir, cleanup, err := test.NewTestDir(tt.src) 859 if err != nil { 860 t.Fatal(err) 861 } 862 defer cleanup() 863 864 resources := []*provider.ResourceId{} 865 for _, f := range tt.files { 866 resources = append(resources, &provider.ResourceId{OpaqueId: path.Join(tmpdir, f)}) 867 } 868 869 w := walkerMock.NewWalker(tmpdir) 870 d := downMock.NewDownloader() 871 872 arch, err := NewArchiver(resources, w, d, tt.config) 873 if err != nil { 874 t.Fatal(err) 875 } 876 877 var zipFile bytes.Buffer 878 879 cl, err := arch.CreateZip(ctx, &zipFile) 880 if err != tt.err { 881 t.Fatalf("error result different from expected: got=%v, expected=%v", err, tt.err) 882 } 883 cl() 884 885 if tt.expected != nil { 886 zipTmpDir, cleanup, err := test.TmpDir() 887 if err != nil { 888 t.Fatal(err) 889 } 890 defer cleanup() 891 892 err = UnZip(zipTmpDir, &zipFile) 893 if err != nil { 894 t.Fatal(err) 895 } 896 897 expectedTmp, cleanup, err := test.NewTestDir(tt.expected) 898 if err != nil { 899 t.Fatal(err) 900 } 901 defer cleanup() 902 if !test.DirEquals(zipTmpDir, expectedTmp) { 903 t.Fatalf("unzip dir %s different from expected %s", zipTmpDir, expectedTmp) 904 } 905 } 906 907 }) 908 909 } 910 911 }