@@ -811,9 +811,10 @@ async def mock_compress_partial(*args, **kwargs):
811811 # Overwrites: a job can only replace its own leftover, see ``test_stashing_same_source_twice``
812812
813813
814+ @pytest .mark .parametrize ('stash_mode' , [StashMode .COPY .value , StashMode .COMPRESS_TARGZ .value ])
814815@pytest .mark .asyncio
815- async def test_stashing_compress_skips_missing (generate_calcjob_node , tmp_path , monkeypatch ):
816- """With ``fail_on_missing=False`` a compressed stash skips missing sources instead of failing silently ."""
816+ async def test_stashing_skips_missing (generate_calcjob_node , stash_mode , tmp_path , monkeypatch ):
817+ """With ``fail_on_missing=False`` missing sources are skipped and the stash node records only what was stashed ."""
817818 node = generate_calcjob_node ()
818819 workdir = tmp_path / 'workdir'
819820 workdir .mkdir ()
@@ -825,7 +826,7 @@ async def test_stashing_compress_skips_missing(generate_calcjob_node, tmp_path,
825826 {
826827 'source_list' : ['present.out' , 'missing.out' , 'nomatch*' ],
827828 'target_base' : str (target_base ),
828- 'stash_mode' : StashMode . COMPRESS_TARGZ . value ,
829+ 'stash_mode' : stash_mode ,
829830 },
830831 )
831832
@@ -838,15 +839,42 @@ def get_workdir(self, *args, **kwargs):
838839
839840 with LocalTransport () as transport :
840841 await execmanager .stash_calculation (node , transport )
841- transport .extract (target_base / f'{ node .uuid } .tar.gz' , tmp_path / 'extracted' )
842-
843- assert [path .name for path in (tmp_path / 'extracted' ).iterdir ()] == ['present.out' ]
842+ if stash_mode == StashMode .COPY .value :
843+ stashed = target_base / node .uuid [:2 ] / node .uuid [2 :4 ] / node .uuid [4 :]
844+ else :
845+ stashed = tmp_path / 'extracted'
846+ transport .extract (target_base / f'{ node .uuid } .{ stash_mode } ' , stashed )
844847
845- # The stash node must record only what actually went into the archive
848+ assert [ path . name for path in stashed . iterdir ()] == [ 'present.out' ]
846849 remote_stash = node .base .links .get_outgoing (link_label_filter = 'remote_stash' ).one ().node
847850 assert list (remote_stash .source_list ) == ['present.out' ]
848851
849852
853+ @pytest .mark .parametrize ('stash_mode' , [StashMode .COPY .value , StashMode .COMPRESS_TARGZ .value ])
854+ @pytest .mark .asyncio
855+ async def test_stashing_fail_on_missing_rejects_glob (generate_calcjob_node , stash_mode , tmp_path , monkeypatch ):
856+ """With ``fail_on_missing=True`` glob patterns are rejected, since a non-matching one cannot be told apart."""
857+ node = generate_calcjob_node (workdir = tmp_path )
858+ node .set_option (
859+ 'stash' ,
860+ {
861+ 'source_list' : ['*.out' ],
862+ 'target_base' : str (tmp_path / 'stash' ),
863+ 'stash_mode' : stash_mode ,
864+ 'fail_on_missing' : True ,
865+ },
866+ )
867+
868+ class MockAuthInfo :
869+ def get_workdir (self , * args , ** kwargs ):
870+ return str (tmp_path )
871+
872+ monkeypatch .setattr (node , 'get_authinfo' , MockAuthInfo )
873+
874+ with LocalTransport () as transport , pytest .raises (StashingError , match = 'glob patterns' ):
875+ await execmanager .stash_calculation (node , transport )
876+
877+
850878@pytest .mark .parametrize ('stash_mode' , [StashMode .COPY .value , StashMode .COMPRESS_TARGZ .value ])
851879@pytest .mark .asyncio
852880async def test_stashing_same_source_twice (generate_calcjob_node , aiida_localhost , stash_mode , tmp_path , monkeypatch ):
0 commit comments