Fix GPG verification runtime handling
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _find_repo_root() -> Path:
|
||||
here = Path(__file__).resolve()
|
||||
for parent in here.parents:
|
||||
if (parent / "pyproject.toml").is_file() and (
|
||||
parent / "src" / "pkgmgr"
|
||||
).is_dir():
|
||||
return parent
|
||||
raise RuntimeError(
|
||||
"Could not determine repository root for pkgmgr integration test"
|
||||
)
|
||||
|
||||
|
||||
class TestGitVerificationRuntimeDependencies(unittest.TestCase):
|
||||
def test_flake_app_includes_git_and_gpg_runtime_tools(self) -> None:
|
||||
repo_root = _find_repo_root()
|
||||
flake_text = (repo_root / "flake.nix").read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn("pkgs.git", flake_text)
|
||||
self.assertIn("pkgs.gnupg", flake_text)
|
||||
|
||||
def test_distro_dependency_scripts_install_gpg_tools(self) -> None:
|
||||
repo_root = _find_repo_root()
|
||||
expected_packages = {
|
||||
"arch": "gnupg",
|
||||
"debian": "gnupg",
|
||||
"ubuntu": "gnupg",
|
||||
"fedora": "gnupg2",
|
||||
"centos": "gnupg2",
|
||||
}
|
||||
|
||||
missing: list[str] = []
|
||||
for distro, package_name in expected_packages.items():
|
||||
script_path = (
|
||||
repo_root / "scripts" / "installation" / distro / "dependencies.sh"
|
||||
)
|
||||
content = script_path.read_text(encoding="utf-8")
|
||||
if not re.search(rf"\b{re.escape(package_name)}\b", content):
|
||||
missing.append(
|
||||
f"{distro}: expected package {package_name} in {script_path}"
|
||||
)
|
||||
|
||||
if missing:
|
||||
self.fail(
|
||||
"Git signature verification runtime dependencies are incomplete:\n"
|
||||
+ "\n".join(f" - {item}" for item in missing)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,7 +1,8 @@
|
||||
import unittest
|
||||
import subprocess
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.core.git.errors import GitNotRepositoryError, GitRunError
|
||||
from pkgmgr.core.git.errors import GitNotRepositoryError
|
||||
from pkgmgr.core.git.queries.get_latest_signing_key import (
|
||||
GitLatestSigningKeyQueryError,
|
||||
get_latest_signing_key,
|
||||
@@ -10,25 +11,53 @@ from pkgmgr.core.git.queries.get_latest_signing_key import (
|
||||
|
||||
class TestGetLatestSigningKey(unittest.TestCase):
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
return_value="ABCDEF1234567890\n",
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=["git", "log", "-1", "--format=%GK"],
|
||||
returncode=0,
|
||||
stdout="ABCDEF1234567890\n",
|
||||
stderr="",
|
||||
),
|
||||
)
|
||||
def test_strips_output(self, _mock_run) -> None:
|
||||
out = get_latest_signing_key(cwd="/tmp/repo")
|
||||
self.assertEqual(out, "ABCDEF1234567890")
|
||||
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
side_effect=GitRunError("boom"),
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=["git", "log", "-1", "--format=%GK"],
|
||||
returncode=1,
|
||||
stdout="",
|
||||
stderr="boom",
|
||||
),
|
||||
)
|
||||
def test_wraps_git_run_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitLatestSigningKeyQueryError):
|
||||
with self.assertRaisesRegex(GitLatestSigningKeyQueryError, "boom"):
|
||||
get_latest_signing_key(cwd="/tmp/repo")
|
||||
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
side_effect=GitNotRepositoryError("no repo"),
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=["git", "log", "-1", "--format=%GK"],
|
||||
returncode=128,
|
||||
stdout="",
|
||||
stderr="fatal: not a git repository",
|
||||
),
|
||||
)
|
||||
def test_does_not_catch_not_repository_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitNotRepositoryError):
|
||||
get_latest_signing_key(cwd="/tmp/no-repo")
|
||||
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.subprocess.run",
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=["git", "log", "-1", "--format=%GK"],
|
||||
returncode=0,
|
||||
stdout="",
|
||||
stderr="error: cannot run gpg: No such file or directory",
|
||||
),
|
||||
)
|
||||
def test_raises_when_git_reports_gpg_runtime_error(self, _mock_run) -> None:
|
||||
with self.assertRaisesRegex(GitLatestSigningKeyQueryError, "cannot run gpg"):
|
||||
get_latest_signing_key(cwd="/tmp/repo")
|
||||
|
||||
@@ -77,6 +77,23 @@ class TestVerifyRepository(unittest.TestCase):
|
||||
self.assertEqual(commit, "")
|
||||
self.assertEqual(key, "")
|
||||
|
||||
def test_verified_gpg_query_error_does_not_add_missing_key_fallback(self) -> None:
|
||||
repo = {"verified": {"commit": None, "gpg_keys": ["ABC"]}}
|
||||
with (
|
||||
patch("pkgmgr.core.repository.verify.get_head_commit", return_value=""),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
side_effect=GitLatestSigningKeyQueryError("cannot run gpg"),
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
|
||||
|
||||
self.assertFalse(ok)
|
||||
self.assertIn("cannot run gpg", " ".join(errors))
|
||||
self.assertFalse(any("no signing key was found" in e for e in errors))
|
||||
self.assertEqual(commit, "")
|
||||
self.assertEqual(key, "")
|
||||
|
||||
def test_strict_pull_collects_remote_error_message(self) -> None:
|
||||
repo = {"verified": {"commit": "expected", "gpg_keys": None}}
|
||||
with (
|
||||
|
||||
Reference in New Issue
Block a user