|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | from collections import defaultdict |
| 4 | +from datetime import datetime, timezone |
4 | 5 | import os |
5 | 6 | from pathlib import Path |
6 | 7 | from shutil import copyfile, rmtree |
|
19 | 20 |
|
20 | 21 | from .fixtures import SampleDandiset, sweep_embargo |
21 | 22 | from .test_helpers import assert_dirtrees_eq |
22 | | -from ..consts import ZARR_MIME_TYPE, EmbargoStatus, dandiset_metadata_file |
| 23 | +from ..consts import ( |
| 24 | + DOWNLOAD_SUFFIX, |
| 25 | + ZARR_MIME_TYPE, |
| 26 | + EmbargoStatus, |
| 27 | + dandiset_metadata_file, |
| 28 | +) |
23 | 29 | from ..dandiapi import AssetType, RemoteBlobAsset, RemoteZarrAsset, RESTFullAPIClient |
24 | 30 | from ..dandiset import Dandiset |
25 | 31 | from ..download import download |
@@ -614,3 +620,90 @@ def mock_request(self, method, path, **kwargs): |
614 | 620 | assert ( |
615 | 621 | request_attempts[url] == 1 |
616 | 622 | ), f"URL {url} should not have been retried but had {request_attempts[url]} attempts" |
| 623 | + |
| 624 | + |
| 625 | +@pytest.mark.ai_generated |
| 626 | +def test_upload_rejects_dandidownload_paths( |
| 627 | + new_dandiset: SampleDandiset, tmp_path: Path |
| 628 | +) -> None: |
| 629 | + """Test that upload rejects assets with .dandidownload paths""" |
| 630 | + dspath = new_dandiset.dspath |
| 631 | + |
| 632 | + # Test 1: Regular file with .dandidownload in path |
| 633 | + badfile_path = dspath / f"test{DOWNLOAD_SUFFIX}" / "file.nwb" |
| 634 | + badfile_path.parent.mkdir(parents=True) |
| 635 | + make_nwb_file( |
| 636 | + badfile_path, |
| 637 | + session_description="test session", |
| 638 | + identifier="test123", |
| 639 | + session_start_time=datetime(2017, 4, 15, 12, tzinfo=timezone.utc), |
| 640 | + subject=pynwb.file.Subject(subject_id="test"), |
| 641 | + ) |
| 642 | + |
| 643 | + with pytest.raises( |
| 644 | + UploadError, |
| 645 | + match=f"contains {DOWNLOAD_SUFFIX} path which indicates incomplete download", |
| 646 | + ): |
| 647 | + new_dandiset.upload(allow_any_path=True) |
| 648 | + |
| 649 | + # Clean up for next test |
| 650 | + rmtree(badfile_path.parent) |
| 651 | + |
| 652 | + # Test 2: Zarr asset with .dandidownload in internal path |
| 653 | + zarr_path = dspath / "test.zarr" |
| 654 | + zarr.save(zarr_path, np.arange(100)) |
| 655 | + |
| 656 | + # Create a .dandidownload directory inside the zarr |
| 657 | + bad_zarr_path = zarr_path / f"sub{DOWNLOAD_SUFFIX}" |
| 658 | + bad_zarr_path.mkdir() |
| 659 | + (bad_zarr_path / "badfile").write_text("bad data") |
| 660 | + |
| 661 | + with pytest.raises( |
| 662 | + UploadError, |
| 663 | + match=f"Zarr asset contains {DOWNLOAD_SUFFIX} path which indicates incomplete download", |
| 664 | + ): |
| 665 | + new_dandiset.upload() |
| 666 | + |
| 667 | + # Clean up |
| 668 | + rmtree(bad_zarr_path) |
| 669 | + |
| 670 | + # Test 3: Zarr asset with .dandidownload in filename |
| 671 | + bad_file_in_zarr = zarr_path / f"data{DOWNLOAD_SUFFIX}" |
| 672 | + bad_file_in_zarr.write_text("bad data") |
| 673 | + |
| 674 | + with pytest.raises( |
| 675 | + UploadError, |
| 676 | + match=f"Zarr asset contains {DOWNLOAD_SUFFIX} path which indicates incomplete download", |
| 677 | + ): |
| 678 | + new_dandiset.upload() |
| 679 | + |
| 680 | + # Clean up |
| 681 | + bad_file_in_zarr.unlink() |
| 682 | + |
| 683 | + # Test 4: Normal zarr should upload fine after removing bad paths |
| 684 | + new_dandiset.upload() |
| 685 | + (asset,) = new_dandiset.dandiset.get_assets() |
| 686 | + assert isinstance(asset, RemoteZarrAsset) |
| 687 | + assert asset.path == "test.zarr" |
| 688 | + |
| 689 | + |
| 690 | +@pytest.mark.ai_generated |
| 691 | +def test_upload_rejects_dandidownload_nwb_file(new_dandiset: SampleDandiset) -> None: |
| 692 | + """Test that upload rejects NWB files with .dandidownload in their path""" |
| 693 | + dspath = new_dandiset.dspath |
| 694 | + |
| 695 | + # Create an NWB file with .dandidownload in its name |
| 696 | + bad_nwb_path = dspath / f"test{DOWNLOAD_SUFFIX}.nwb" |
| 697 | + make_nwb_file( |
| 698 | + bad_nwb_path, |
| 699 | + session_description="test session", |
| 700 | + identifier="test456", |
| 701 | + session_start_time=datetime(2017, 4, 15, 12, tzinfo=timezone.utc), |
| 702 | + subject=pynwb.file.Subject(subject_id="test"), |
| 703 | + ) |
| 704 | + |
| 705 | + with pytest.raises( |
| 706 | + UploadError, |
| 707 | + match=f"contains {DOWNLOAD_SUFFIX} path which indicates incomplete download", |
| 708 | + ): |
| 709 | + new_dandiset.upload(allow_any_path=True) |
0 commit comments