@@ -312,3 +312,174 @@ def test_scan_compiled_extensions(
312312 assert matches == [pathlib .Path (filename )]
313313 else :
314314 assert matches == []
315+
316+
317+ class TestEnsureGitArchival :
318+ """Tests for ensure_git_archival()."""
319+
320+ @pytest .fixture ()
321+ def mock_ctx (self , tmp_path : pathlib .Path ) -> Mock :
322+ """Create a mock context whose build_dir returns sdist_root_dir."""
323+ ctx = Mock (spec = context .WorkContext )
324+ mock_pbi = Mock ()
325+ mock_pbi .build_dir .side_effect = lambda d : d
326+ mock_pbi .generate_git_archival = False
327+ ctx .package_build_info .return_value = mock_pbi
328+ return ctx
329+
330+ @pytest .fixture ()
331+ def req (self ) -> Requirement :
332+ """Create a test requirement."""
333+ return Requirement ("test-package" )
334+
335+ def test_skips_when_no_file_exists (
336+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
337+ ) -> None :
338+ """Verify no file is created when none existed before."""
339+ version = Version ("1.2.3" )
340+ result = sources .ensure_git_archival (
341+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
342+ )
343+ archival = tmp_path / ".git_archival.txt"
344+
345+ assert result is None
346+ assert not archival .is_file ()
347+
348+ def test_replaces_unprocessed_file (
349+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
350+ ) -> None :
351+ """Verify unprocessed template file is replaced."""
352+ archival = tmp_path / ".git_archival.txt"
353+ archival .write_text (
354+ "node: $Format:%H$\n "
355+ "node-date: $Format:%cI$\n "
356+ "describe-name: $Format:%(describe:tags=true)$\n "
357+ )
358+ version = Version ("4.5.6" )
359+ result = sources .ensure_git_archival (
360+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
361+ )
362+
363+ assert result is False
364+ content = archival .read_text ()
365+ assert "describe-name: 4.5.6\n " in content
366+ assert "%(describe" not in content
367+
368+ def test_preserves_valid_file (
369+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
370+ ) -> None :
371+ """Verify a valid archival file is left untouched."""
372+ archival = tmp_path / ".git_archival.txt"
373+ original = (
374+ "node: abc123\n "
375+ "node-date: 2025-01-01T00:00:00+00:00\n "
376+ "describe-name: v1.0.0-0-gabc123\n "
377+ )
378+ archival .write_text (original )
379+ version = Version ("9.9.9" )
380+ result = sources .ensure_git_archival (
381+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
382+ )
383+
384+ assert result is True
385+ assert archival .read_text () == original
386+
387+ def test_preserves_valid_file_describe_name_only (
388+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
389+ ) -> None :
390+ """Verify a file with only describe-name is valid."""
391+ archival = tmp_path / ".git_archival.txt"
392+ original = "describe-name: 2.0.0\n "
393+ archival .write_text (original )
394+ version = Version ("9.9.9" )
395+ result = sources .ensure_git_archival (
396+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
397+ )
398+
399+ assert result is True
400+ assert archival .read_text () == original
401+
402+ def test_replaces_truncated_file (
403+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
404+ ) -> None :
405+ """Verify a truncated file missing required fields is replaced."""
406+ archival = tmp_path / ".git_archival.txt"
407+ archival .write_text ("node-date: 2025-01-01T00:00:00+00:00\n " )
408+ version = Version ("3.0.0" )
409+ result = sources .ensure_git_archival (
410+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
411+ )
412+
413+ assert result is False
414+ content = archival .read_text ()
415+ assert "describe-name: 3.0.0\n " in content
416+
417+ def test_replaces_file_with_empty_values (
418+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
419+ ) -> None :
420+ """Verify a file with required fields but empty values is replaced."""
421+ archival = tmp_path / ".git_archival.txt"
422+ archival .write_text ("describe-name:\n " )
423+ version = Version ("5.0.0" )
424+ result = sources .ensure_git_archival (
425+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
426+ )
427+
428+ assert result is False
429+ content = archival .read_text ()
430+ assert "describe-name: 5.0.0\n " in content
431+
432+ def test_skips_when_build_dir_missing (
433+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
434+ ) -> None :
435+ """Verify no error when build directory does not exist."""
436+ nonexistent = tmp_path / "no-such-dir"
437+ version = Version ("1.0.0" )
438+ result = sources .ensure_git_archival (
439+ ctx = mock_ctx , req = req , sdist_root_dir = nonexistent , version = version
440+ )
441+
442+ assert result is None
443+ assert not nonexistent .exists ()
444+
445+ def test_skips_when_git_dir_exists (
446+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
447+ ) -> None :
448+ """Verify no file is created when .git directory exists."""
449+ (tmp_path / ".git" ).mkdir ()
450+ version = Version ("1.0.0" )
451+ result = sources .ensure_git_archival (
452+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
453+ )
454+
455+ assert result is True
456+ assert not (tmp_path / ".git_archival.txt" ).exists ()
457+
458+ def test_creates_file_when_config_enabled (
459+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
460+ ) -> None :
461+ """Verify file is created when generate_git_archival is enabled."""
462+ mock_ctx .package_build_info .return_value .generate_git_archival = True
463+ version = Version ("1.2.3" )
464+ result = sources .ensure_git_archival (
465+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
466+ )
467+ archival = tmp_path / ".git_archival.txt"
468+
469+ assert result is False
470+ assert archival .is_file ()
471+ content = archival .read_text ()
472+ assert "describe-name: 1.2.3\n " in content
473+
474+ def test_skips_creation_when_config_disabled (
475+ self , tmp_path : pathlib .Path , mock_ctx : Mock , req : Requirement
476+ ) -> None :
477+ """Verify no file is created when generate_git_archival is disabled."""
478+ version = Version ("1.2.3" )
479+ result = sources .ensure_git_archival (
480+ ctx = mock_ctx , req = req , sdist_root_dir = tmp_path , version = version
481+ )
482+ archival = tmp_path / ".git_archival.txt"
483+
484+ assert result is None
485+ assert not archival .is_file ()
0 commit comments