@@ -190,6 +190,38 @@ def test_copy_file_fallback(tmp_path):
190190 mock_rename .assert_called_once ()
191191
192192
193+ def test_copy_file_symlink_falls_back_to_hard_link (tmp_path ):
194+ source = tmp_path / "source.txt"
195+ source .write_text ("content" )
196+ target = tmp_path / "target.txt"
197+
198+ path_cls = type (target )
199+
200+ with patch .object (path_cls , "symlink_to" , side_effect = OSError ):
201+ with patch ("os.link" ) as mock_link :
202+ copy_file (target , source , symlink = True )
203+
204+ mock_link .assert_called_once_with (source , target )
205+
206+
207+ def test_copy_file_symlink_fallback_reraises_original_error (tmp_path ):
208+ source = tmp_path / "source.txt"
209+ source .write_text ("content" )
210+ target = tmp_path / "target.txt"
211+
212+ path_cls = type (target )
213+ symlink_error = OSError ("symlink failed" )
214+ hard_link_error = OSError ("hard link failed" )
215+
216+ with patch .object (path_cls , "symlink_to" , side_effect = symlink_error ):
217+ with patch ("os.link" , side_effect = hard_link_error ):
218+ with pytest .raises (OSError ) as exc_info :
219+ copy_file (target , source , symlink = True )
220+
221+ assert exc_info .value is symlink_error
222+ assert exc_info .value .__cause__ is hard_link_error
223+
224+
193225def test_simple_file_lock_timeout (tmp_path ):
194226 lock_file = tmp_path / "lock"
195227
0 commit comments