test_git_signing.py
1 """E2E tests for git commit signing and verification.""" 2 3 import shutil 4 from pathlib import Path 5 6 import pytest 7 8 from helpers.cli import run_auths, run_git 9 from helpers.git import configure_signing, make_commit 10 11 12 def _generate_allowed_signers(auths_bin, git_repo: Path, env: dict) -> Path: 13 """Generate allowed-signers file inside the git repo's .auths/ dir.""" 14 auths_dir = git_repo / ".auths" 15 auths_dir.mkdir(exist_ok=True) 16 signers_file = auths_dir / "allowed_signers" 17 run_auths( 18 auths_bin, 19 [ 20 "git", 21 "allowed-signers", 22 "--repo", 23 env["AUTHS_HOME"], 24 "--output", 25 str(signers_file), 26 ], 27 env=env, 28 ).assert_success() 29 return signers_file 30 31 32 @pytest.mark.requires_binary 33 class TestGitSigning: 34 @pytest.fixture(autouse=True) 35 def _check_ssh_keygen(self): 36 if not shutil.which("ssh-keygen"): 37 pytest.skip("ssh-keygen not found") 38 39 def test_sign_commit_roundtrip( 40 self, auths_bin, auths_sign_bin, init_identity, git_repo 41 ): 42 configure_signing(git_repo, auths_sign_bin, init_identity) 43 sha = make_commit(git_repo, "signed commit", init_identity) 44 assert len(sha) == 40 45 46 _generate_allowed_signers(auths_bin, git_repo, init_identity) 47 48 result = run_auths( 49 auths_bin, ["verify", sha], cwd=git_repo, env=init_identity 50 ) 51 if result.returncode != 0: 52 pytest.skip(f"verify not available: {result.stderr}") 53 result.assert_success() 54 55 def test_verify_unsigned_commit(self, auths_bin, init_identity, git_repo): 56 sha = make_commit(git_repo, "unsigned commit", init_identity) 57 58 _generate_allowed_signers(auths_bin, git_repo, init_identity) 59 60 result = run_auths( 61 auths_bin, ["verify", sha], cwd=git_repo, env=init_identity 62 ) 63 # Unsigned commit should report as unverified 64 if result.returncode == 0: 65 pass 66 else: 67 result.assert_failure() 68 69 def test_sign_and_verify_multiple_commits( 70 self, auths_bin, auths_sign_bin, init_identity, git_repo 71 ): 72 configure_signing(git_repo, auths_sign_bin, init_identity) 73 74 shas = [] 75 for i in range(3): 76 sha = make_commit(git_repo, f"commit {i}", init_identity) 77 shas.append(sha) 78 79 _generate_allowed_signers(auths_bin, git_repo, init_identity) 80 81 for sha in shas: 82 result = run_auths( 83 auths_bin, ["verify", sha], cwd=git_repo, env=init_identity 84 ) 85 if result.returncode != 0: 86 pytest.skip(f"verify not available: {result.stderr}") 87 88 def test_auths_sign_binary_direct( 89 self, auths_sign_bin, init_identity, tmp_path 90 ): 91 data_file = tmp_path / "message.txt" 92 data_file.write_text("test message") 93 94 result = run_auths( 95 auths_sign_bin, 96 ["-Y", "sign", "-n", "git", "-f", "auths:main", str(data_file)], 97 env=init_identity, 98 ) 99 if result.returncode != 0: 100 pytest.skip(f"auths-sign direct not available: {result.stderr}") 101 102 # Should produce SSHSIG output 103 assert "SIGNATURE" in result.stdout or result.returncode == 0 104 105 def test_allowed_signers_generation( 106 self, auths_bin, init_identity, git_repo, tmp_path 107 ): 108 signers_file = tmp_path / "signers.txt" 109 result = run_auths( 110 auths_bin, 111 [ 112 "git", 113 "allowed-signers", 114 "--repo", 115 init_identity["AUTHS_HOME"], 116 "--output", 117 str(signers_file), 118 ], 119 env=init_identity, 120 ) 121 if result.returncode != 0: 122 pytest.skip(f"allowed-signers not available: {result.stderr}") 123 124 assert signers_file.exists() 125 content = signers_file.read_text() 126 assert len(content.strip()) > 0