executet 'ruff format --check .'
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / lint-shell (push) Has been cancelled
Mark stable commit / lint-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / lint-shell (push) Has been cancelled
Mark stable commit / lint-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
This commit is contained in:
@@ -8,8 +8,13 @@ from pkgmgr.core.git.commands import GitDeleteRemoteBranchError
|
||||
|
||||
class TestCloseBranch(unittest.TestCase):
|
||||
@patch("builtins.input", return_value="y")
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", return_value="feature-x")
|
||||
@patch("pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.get_current_branch",
|
||||
return_value="feature-x",
|
||||
)
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.close_branch.fetch")
|
||||
@patch("pkgmgr.actions.branch.close_branch.checkout")
|
||||
@patch("pkgmgr.actions.branch.close_branch.pull")
|
||||
@@ -40,22 +45,36 @@ class TestCloseBranch(unittest.TestCase):
|
||||
delete_remote_branch.assert_called_once_with("origin", "feature-x", cwd=".")
|
||||
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", return_value="main")
|
||||
@patch("pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main"
|
||||
)
|
||||
def test_refuses_to_close_base_branch(self, _resolve, _current) -> None:
|
||||
with self.assertRaises(RuntimeError):
|
||||
close_branch(None)
|
||||
|
||||
@patch("builtins.input", return_value="n")
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", return_value="feature-x")
|
||||
@patch("pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.get_current_branch",
|
||||
return_value="feature-x",
|
||||
)
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.close_branch.fetch")
|
||||
def test_close_branch_aborts_on_no(self, fetch, _resolve, _current, _input_mock) -> None:
|
||||
def test_close_branch_aborts_on_no(
|
||||
self, fetch, _resolve, _current, _input_mock
|
||||
) -> None:
|
||||
close_branch(None, cwd=".")
|
||||
fetch.assert_not_called()
|
||||
|
||||
@patch("builtins.input")
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", return_value="feature-x")
|
||||
@patch("pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.get_current_branch",
|
||||
return_value="feature-x",
|
||||
)
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.close_branch.fetch")
|
||||
@patch("pkgmgr.actions.branch.close_branch.checkout")
|
||||
@patch("pkgmgr.actions.branch.close_branch.pull")
|
||||
@@ -90,14 +109,22 @@ class TestCloseBranch(unittest.TestCase):
|
||||
delete_local_branch.assert_called_once_with("feature-x", cwd=".", force=False)
|
||||
delete_remote_branch.assert_called_once_with("origin", "feature-x", cwd=".")
|
||||
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", side_effect=GitRunError("fail"))
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.get_current_branch",
|
||||
side_effect=GitRunError("fail"),
|
||||
)
|
||||
def test_close_branch_errors_if_cannot_detect_branch(self, _current) -> None:
|
||||
with self.assertRaises(RuntimeError):
|
||||
close_branch(None)
|
||||
|
||||
@patch("builtins.input", return_value="y")
|
||||
@patch("pkgmgr.actions.branch.close_branch.get_current_branch", return_value="feature-x")
|
||||
@patch("pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.get_current_branch",
|
||||
return_value="feature-x",
|
||||
)
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.close_branch.resolve_base_branch", return_value="main"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.close_branch.fetch")
|
||||
@patch("pkgmgr.actions.branch.close_branch.checkout")
|
||||
@patch("pkgmgr.actions.branch.close_branch.pull")
|
||||
|
||||
@@ -8,11 +8,15 @@ from pkgmgr.core.git.commands import GitDeleteRemoteBranchError
|
||||
|
||||
class TestDropBranch(unittest.TestCase):
|
||||
@patch("builtins.input", return_value="y")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.drop_branch.resolve_base_branch", return_value="main")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_local_branch")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_remote_branch")
|
||||
def test_drop_branch_happy_path(self, delete_remote, delete_local, _resolve, _current, _input_mock) -> None:
|
||||
def test_drop_branch_happy_path(
|
||||
self, delete_remote, delete_local, _resolve, _current, _input_mock
|
||||
) -> None:
|
||||
drop_branch(None, cwd=".")
|
||||
delete_local.assert_called_once_with("feature-x", cwd=".", force=False)
|
||||
delete_remote.assert_called_once_with("origin", "feature-x", cwd=".")
|
||||
@@ -24,15 +28,21 @@ class TestDropBranch(unittest.TestCase):
|
||||
drop_branch(None)
|
||||
|
||||
@patch("builtins.input", return_value="n")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.drop_branch.resolve_base_branch", return_value="main")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_local_branch")
|
||||
def test_drop_branch_aborts_on_no(self, delete_local, _resolve, _current, _input_mock) -> None:
|
||||
def test_drop_branch_aborts_on_no(
|
||||
self, delete_local, _resolve, _current, _input_mock
|
||||
) -> None:
|
||||
drop_branch(None, cwd=".")
|
||||
delete_local.assert_not_called()
|
||||
|
||||
@patch("builtins.input")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.drop_branch.resolve_base_branch", return_value="main")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_local_branch")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_remote_branch")
|
||||
@@ -50,13 +60,18 @@ class TestDropBranch(unittest.TestCase):
|
||||
delete_local.assert_called_once_with("feature-x", cwd=".", force=False)
|
||||
delete_remote.assert_called_once_with("origin", "feature-x", cwd=".")
|
||||
|
||||
@patch("pkgmgr.actions.branch.drop_branch.get_current_branch", side_effect=GitRunError("fail"))
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.drop_branch.get_current_branch",
|
||||
side_effect=GitRunError("fail"),
|
||||
)
|
||||
def test_drop_branch_errors_if_no_branch_detected(self, _current) -> None:
|
||||
with self.assertRaises(RuntimeError):
|
||||
drop_branch(None)
|
||||
|
||||
@patch("builtins.input", return_value="y")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x")
|
||||
@patch(
|
||||
"pkgmgr.actions.branch.drop_branch.get_current_branch", return_value="feature-x"
|
||||
)
|
||||
@patch("pkgmgr.actions.branch.drop_branch.resolve_base_branch", return_value="main")
|
||||
@patch("pkgmgr.actions.branch.drop_branch.delete_local_branch")
|
||||
@patch(
|
||||
|
||||
@@ -15,24 +15,31 @@ class TestNixConflictResolver(unittest.TestCase):
|
||||
ctx = DummyCtx()
|
||||
install_cmd = "nix profile install /repo#default"
|
||||
|
||||
stderr = '''
|
||||
stderr = """
|
||||
error: An existing package already provides the following file:
|
||||
/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr/bin/pkgmgr
|
||||
'''
|
||||
"""
|
||||
|
||||
runner = FakeRunner(mapping={
|
||||
"nix profile remove pkgmgr": FakeRunResult(0, "", ""),
|
||||
})
|
||||
runner = FakeRunner(
|
||||
mapping={
|
||||
"nix profile remove pkgmgr": FakeRunResult(0, "", ""),
|
||||
}
|
||||
)
|
||||
retry = FakeRetry(results=[FakeRunResult(0, "", "")])
|
||||
|
||||
class FakeProfile:
|
||||
def find_remove_tokens_for_store_prefixes(self, ctx, runner, prefixes):
|
||||
return []
|
||||
|
||||
def find_remove_tokens_for_output(self, ctx, runner, output):
|
||||
return ["pkgmgr"]
|
||||
|
||||
resolver = NixConflictResolver(runner=runner, retry=retry, profile=FakeProfile())
|
||||
ok = resolver.resolve(ctx, install_cmd, stdout="", stderr=stderr, output="pkgmgr", max_rounds=2)
|
||||
resolver = NixConflictResolver(
|
||||
runner=runner, retry=retry, profile=FakeProfile()
|
||||
)
|
||||
ok = resolver.resolve(
|
||||
ctx, install_cmd, stdout="", stderr=stderr, output="pkgmgr", max_rounds=2
|
||||
)
|
||||
self.assertTrue(ok)
|
||||
self.assertIn("nix profile remove pkgmgr", [c[1] for c in runner.calls])
|
||||
|
||||
@@ -41,18 +48,25 @@ class TestNixConflictResolver(unittest.TestCase):
|
||||
install_cmd = "nix profile install /repo#default"
|
||||
|
||||
stderr = "hint: try:\n nix profile remove 'pkgmgr-1'\n"
|
||||
runner = FakeRunner(mapping={
|
||||
"nix profile remove pkgmgr-1": FakeRunResult(0, "", ""),
|
||||
})
|
||||
runner = FakeRunner(
|
||||
mapping={
|
||||
"nix profile remove pkgmgr-1": FakeRunResult(0, "", ""),
|
||||
}
|
||||
)
|
||||
retry = FakeRetry(results=[FakeRunResult(0, "", "")])
|
||||
|
||||
class FakeProfile:
|
||||
def find_remove_tokens_for_store_prefixes(self, ctx, runner, prefixes):
|
||||
return []
|
||||
|
||||
def find_remove_tokens_for_output(self, ctx, runner, output):
|
||||
return []
|
||||
|
||||
resolver = NixConflictResolver(runner=runner, retry=retry, profile=FakeProfile())
|
||||
ok = resolver.resolve(ctx, install_cmd, stdout="", stderr=stderr, output="pkgmgr", max_rounds=2)
|
||||
resolver = NixConflictResolver(
|
||||
runner=runner, retry=retry, profile=FakeProfile()
|
||||
)
|
||||
ok = resolver.resolve(
|
||||
ctx, install_cmd, stdout="", stderr=stderr, output="pkgmgr", max_rounds=2
|
||||
)
|
||||
self.assertTrue(ok)
|
||||
self.assertIn("nix profile remove pkgmgr-1", [c[1] for c in runner.calls])
|
||||
|
||||
@@ -9,32 +9,48 @@ from ._fakes import FakeRunResult, FakeRunner
|
||||
|
||||
class TestNixProfileInspector(unittest.TestCase):
|
||||
def test_list_json_accepts_raw_string(self) -> None:
|
||||
payload = {"elements": {"pkgmgr-1": {"attrPath": "packages.x86_64-linux.pkgmgr"}}}
|
||||
payload = {
|
||||
"elements": {"pkgmgr-1": {"attrPath": "packages.x86_64-linux.pkgmgr"}}
|
||||
}
|
||||
raw = json.dumps(payload)
|
||||
runner = FakeRunner(default=raw)
|
||||
insp = NixProfileInspector()
|
||||
data = insp.list_json(ctx=None, runner=runner)
|
||||
self.assertEqual(data["elements"]["pkgmgr-1"]["attrPath"], "packages.x86_64-linux.pkgmgr")
|
||||
self.assertEqual(
|
||||
data["elements"]["pkgmgr-1"]["attrPath"], "packages.x86_64-linux.pkgmgr"
|
||||
)
|
||||
|
||||
def test_list_json_accepts_result_object(self) -> None:
|
||||
payload = {"elements": {"pkgmgr-1": {"attrPath": "packages.x86_64-linux.pkgmgr"}}}
|
||||
payload = {
|
||||
"elements": {"pkgmgr-1": {"attrPath": "packages.x86_64-linux.pkgmgr"}}
|
||||
}
|
||||
raw = json.dumps(payload)
|
||||
runner = FakeRunner(default=FakeRunResult(0, stdout=raw))
|
||||
insp = NixProfileInspector()
|
||||
data = insp.list_json(ctx=None, runner=runner)
|
||||
self.assertEqual(data["elements"]["pkgmgr-1"]["attrPath"], "packages.x86_64-linux.pkgmgr")
|
||||
self.assertEqual(
|
||||
data["elements"]["pkgmgr-1"]["attrPath"], "packages.x86_64-linux.pkgmgr"
|
||||
)
|
||||
|
||||
def test_find_remove_tokens_for_output_includes_output_first(self) -> None:
|
||||
payload = {
|
||||
"elements": {
|
||||
"pkgmgr-1": {"name": "pkgmgr-1", "attrPath": "packages.x86_64-linux.pkgmgr"},
|
||||
"default-1": {"name": "default-1", "attrPath": "packages.x86_64-linux.default"},
|
||||
"pkgmgr-1": {
|
||||
"name": "pkgmgr-1",
|
||||
"attrPath": "packages.x86_64-linux.pkgmgr",
|
||||
},
|
||||
"default-1": {
|
||||
"name": "default-1",
|
||||
"attrPath": "packages.x86_64-linux.default",
|
||||
},
|
||||
}
|
||||
}
|
||||
raw = json.dumps(payload)
|
||||
runner = FakeRunner(default=FakeRunResult(0, stdout=raw))
|
||||
insp = NixProfileInspector()
|
||||
tokens = insp.find_remove_tokens_for_output(ctx=None, runner=runner, output="pkgmgr")
|
||||
tokens = insp.find_remove_tokens_for_output(
|
||||
ctx=None, runner=runner, output="pkgmgr"
|
||||
)
|
||||
self.assertEqual(tokens[0], "pkgmgr")
|
||||
self.assertIn("pkgmgr-1", tokens)
|
||||
|
||||
@@ -44,7 +60,9 @@ class TestNixProfileInspector(unittest.TestCase):
|
||||
"pkgmgr-1": {
|
||||
"name": "pkgmgr-1",
|
||||
"attrPath": "packages.x86_64-linux.pkgmgr",
|
||||
"storePaths": ["/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"],
|
||||
"storePaths": [
|
||||
"/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"
|
||||
],
|
||||
},
|
||||
"something": {
|
||||
"name": "other",
|
||||
@@ -57,6 +75,8 @@ class TestNixProfileInspector(unittest.TestCase):
|
||||
runner = FakeRunner(default=FakeRunResult(0, stdout=raw))
|
||||
insp = NixProfileInspector()
|
||||
tokens = insp.find_remove_tokens_for_store_prefixes(
|
||||
ctx=None, runner=runner, prefixes=["/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"]
|
||||
ctx=None,
|
||||
runner=runner,
|
||||
prefixes=["/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"],
|
||||
)
|
||||
self.assertIn("pkgmgr-1", tokens)
|
||||
|
||||
@@ -8,7 +8,13 @@ from ._fakes import FakeRunResult
|
||||
|
||||
|
||||
class DummyCtx:
|
||||
def __init__(self, identifier: str = "x", repo_dir: str = "/repo", quiet: bool = True, force_update: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
identifier: str = "x",
|
||||
repo_dir: str = "/repo",
|
||||
quiet: bool = True,
|
||||
force_update: bool = False,
|
||||
):
|
||||
self.identifier = identifier
|
||||
self.repo_dir = repo_dir
|
||||
self.quiet = quiet
|
||||
|
||||
@@ -61,8 +61,12 @@ class TestNixFlakeInstaller(unittest.TestCase):
|
||||
shutil.rmtree(self._tmpdir, ignore_errors=True)
|
||||
|
||||
@staticmethod
|
||||
def _cp(code: int, stdout: str = "", stderr: str = "") -> subprocess.CompletedProcess:
|
||||
return subprocess.CompletedProcess(args=["nix"], returncode=code, stdout=stdout, stderr=stderr)
|
||||
def _cp(
|
||||
code: int, stdout: str = "", stderr: str = ""
|
||||
) -> subprocess.CompletedProcess:
|
||||
return subprocess.CompletedProcess(
|
||||
args=["nix"], returncode=code, stdout=stdout, stderr=stderr
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _enable_nix_in_module(which_patch) -> None:
|
||||
@@ -99,11 +103,20 @@ class TestNixFlakeInstaller(unittest.TestCase):
|
||||
return self._cp(0)
|
||||
|
||||
buf = io.StringIO()
|
||||
with patch("pkgmgr.actions.install.installers.nix.installer.shutil.which") as which_mock, patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.os.path.exists", return_value=True
|
||||
), patch(
|
||||
"pkgmgr.actions.install.installers.nix.runner.subprocess.run", side_effect=fake_subprocess_run
|
||||
) as subproc_mock, redirect_stdout(buf):
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.shutil.which"
|
||||
) as which_mock,
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.os.path.exists",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.runner.subprocess.run",
|
||||
side_effect=fake_subprocess_run,
|
||||
) as subproc_mock,
|
||||
redirect_stdout(buf),
|
||||
):
|
||||
self._enable_nix_in_module(which_mock)
|
||||
|
||||
self.assertTrue(installer.supports(ctx))
|
||||
@@ -115,7 +128,7 @@ class TestNixFlakeInstaller(unittest.TestCase):
|
||||
|
||||
install_cmds = self._install_cmds_from_calls(subproc_mock.call_args_list)
|
||||
self.assertEqual(install_cmds, [f"nix profile install {self.repo_dir}#default"])
|
||||
|
||||
|
||||
def test_nix_flake_supports_respects_disable_env(self) -> None:
|
||||
"""
|
||||
PKGMGR_DISABLE_NIX_FLAKE_INSTALLER=1 must disable the installer,
|
||||
@@ -124,8 +137,14 @@ class TestNixFlakeInstaller(unittest.TestCase):
|
||||
ctx = DummyCtx(identifier="pkgmgr", repo_dir=self.repo_dir, quiet=False)
|
||||
installer = NixFlakeInstaller()
|
||||
|
||||
with patch("pkgmgr.actions.install.installers.nix.installer.shutil.which") as which_mock, patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.os.path.exists", return_value=True
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.shutil.which"
|
||||
) as which_mock,
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.installer.os.path.exists",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
self._enable_nix_in_module(which_mock)
|
||||
os.environ["PKGMGR_DISABLE_NIX_FLAKE_INSTALLER"] = "1"
|
||||
|
||||
@@ -3,7 +3,10 @@ from __future__ import annotations
|
||||
import unittest
|
||||
|
||||
from pkgmgr.actions.install.installers.nix.profile.models import NixProfileEntry
|
||||
from pkgmgr.actions.install.installers.nix.profile.matcher import entry_matches_output, entry_matches_store_path
|
||||
from pkgmgr.actions.install.installers.nix.profile.matcher import (
|
||||
entry_matches_output,
|
||||
entry_matches_store_path,
|
||||
)
|
||||
|
||||
|
||||
class TestMatcher(unittest.TestCase):
|
||||
@@ -20,18 +23,32 @@ class TestMatcher(unittest.TestCase):
|
||||
self.assertTrue(entry_matches_output(self._e("pkgmgr", ""), "pkgmgr"))
|
||||
|
||||
def test_matches_attrpath_hash(self) -> None:
|
||||
self.assertTrue(entry_matches_output(self._e("", "github:me/repo#pkgmgr"), "pkgmgr"))
|
||||
self.assertTrue(
|
||||
entry_matches_output(self._e("", "github:me/repo#pkgmgr"), "pkgmgr")
|
||||
)
|
||||
|
||||
def test_matches_attrpath_dot_suffix(self) -> None:
|
||||
self.assertTrue(entry_matches_output(self._e("", "packages.x86_64-linux.pkgmgr"), "pkgmgr"))
|
||||
self.assertTrue(
|
||||
entry_matches_output(self._e("", "packages.x86_64-linux.pkgmgr"), "pkgmgr")
|
||||
)
|
||||
|
||||
def test_matches_name_with_suffix_number(self) -> None:
|
||||
self.assertTrue(entry_matches_output(self._e("pkgmgr-1", ""), "pkgmgr"))
|
||||
|
||||
def test_package_manager_special_case(self) -> None:
|
||||
self.assertTrue(entry_matches_output(self._e("package-manager-2", ""), "pkgmgr"))
|
||||
self.assertTrue(
|
||||
entry_matches_output(self._e("package-manager-2", ""), "pkgmgr")
|
||||
)
|
||||
|
||||
def test_store_path_match(self) -> None:
|
||||
entry = self._e("pkgmgr-1", "")
|
||||
self.assertTrue(entry_matches_store_path(entry, "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"))
|
||||
self.assertFalse(entry_matches_store_path(entry, "/nix/store/cccccccccccccccccccccccccccccccc-zzz"))
|
||||
self.assertTrue(
|
||||
entry_matches_store_path(
|
||||
entry, "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
entry_matches_store_path(
|
||||
entry, "/nix/store/cccccccccccccccccccccccccccccccc-zzz"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,7 +3,10 @@ from __future__ import annotations
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.actions.install.installers.nix.retry import GitHubRateLimitRetry, RetryPolicy
|
||||
from pkgmgr.actions.install.installers.nix.retry import (
|
||||
GitHubRateLimitRetry,
|
||||
RetryPolicy,
|
||||
)
|
||||
from pkgmgr.actions.install.installers.nix.types import RunResult
|
||||
|
||||
|
||||
@@ -46,8 +49,8 @@ class TestGitHub403Retry(unittest.TestCase):
|
||||
- Wait times follow Fibonacci(base=30) + jitter
|
||||
"""
|
||||
policy = RetryPolicy(
|
||||
max_attempts=3, # attempts: 1,2,3
|
||||
base_delay_seconds=30, # fibonacci delays: 30, 30, 60
|
||||
max_attempts=3, # attempts: 1,2,3
|
||||
base_delay_seconds=30, # fibonacci delays: 30, 30, 60
|
||||
jitter_seconds_min=0,
|
||||
jitter_seconds_max=60,
|
||||
)
|
||||
@@ -57,9 +60,15 @@ class TestGitHub403Retry(unittest.TestCase):
|
||||
runner = FakeRunner(fail_count=2) # fail twice (403), then succeed
|
||||
|
||||
# Make jitter deterministic and prevent real sleeping.
|
||||
with patch("pkgmgr.actions.install.installers.nix.retry.random.randint", return_value=5) as jitter_mock, patch(
|
||||
"pkgmgr.actions.install.installers.nix.retry.time.sleep"
|
||||
) as sleep_mock:
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.retry.random.randint",
|
||||
return_value=5,
|
||||
) as jitter_mock,
|
||||
patch(
|
||||
"pkgmgr.actions.install.installers.nix.retry.time.sleep"
|
||||
) as sleep_mock,
|
||||
):
|
||||
res = retry.run_with_retry(ctx, runner, "nix profile install /tmp#default")
|
||||
|
||||
# Result should be success on 3rd attempt.
|
||||
@@ -90,15 +99,19 @@ class TestGitHub403Retry(unittest.TestCase):
|
||||
|
||||
def run(self, ctx: DummyCtx, cmd: str, allow_failure: bool) -> RunResult:
|
||||
self.calls += 1
|
||||
return RunResult(returncode=1, stdout="", stderr="some other error (simulated)")
|
||||
return RunResult(
|
||||
returncode=1, stdout="", stderr="some other error (simulated)"
|
||||
)
|
||||
|
||||
runner = Non403Runner()
|
||||
|
||||
with patch("pkgmgr.actions.install.installers.nix.retry.time.sleep") as sleep_mock:
|
||||
with patch(
|
||||
"pkgmgr.actions.install.installers.nix.retry.time.sleep"
|
||||
) as sleep_mock:
|
||||
res = retry.run_with_retry(ctx, runner, "nix profile install /tmp#default")
|
||||
|
||||
self.assertEqual(res.returncode, 1)
|
||||
self.assertEqual(runner.calls, 1) # no retries
|
||||
self.assertEqual(runner.calls, 1) # no retries
|
||||
self.assertEqual(sleep_mock.call_count, 0)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from pkgmgr.actions.install.installers.nix.profile.normalizer import coerce_index, normalize_elements
|
||||
from pkgmgr.actions.install.installers.nix.profile.normalizer import (
|
||||
coerce_index,
|
||||
normalize_elements,
|
||||
)
|
||||
|
||||
|
||||
class TestNormalizer(unittest.TestCase):
|
||||
@@ -25,7 +28,9 @@ class TestNormalizer(unittest.TestCase):
|
||||
"pkgmgr-1": {
|
||||
"name": "pkgmgr-1",
|
||||
"attrPath": "packages.x86_64-linux.pkgmgr",
|
||||
"storePaths": ["/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"],
|
||||
"storePaths": [
|
||||
"/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr"
|
||||
],
|
||||
},
|
||||
"2": {
|
||||
"name": "foo",
|
||||
|
||||
@@ -10,7 +10,9 @@ class TestParseProfileListJson(unittest.TestCase):
|
||||
def test_parses_valid_json(self) -> None:
|
||||
payload = {"elements": {"0": {"name": "pkgmgr"}}}
|
||||
raw = json.dumps(payload)
|
||||
self.assertEqual(parse_profile_list_json(raw)["elements"]["0"]["name"], "pkgmgr")
|
||||
self.assertEqual(
|
||||
parse_profile_list_json(raw)["elements"]["0"]["name"], "pkgmgr"
|
||||
)
|
||||
|
||||
def test_raises_systemexit_on_invalid_json(self) -> None:
|
||||
with self.assertRaises(SystemExit) as cm:
|
||||
|
||||
@@ -8,10 +8,10 @@ from ._fakes import FakeRunResult, FakeRunner
|
||||
|
||||
class TestNixProfileListReader(unittest.TestCase):
|
||||
def test_entries_parses_indices_and_store_prefixes(self) -> None:
|
||||
out = '''
|
||||
out = """
|
||||
0 something /nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr
|
||||
1 something /nix/store/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-foo
|
||||
'''
|
||||
"""
|
||||
runner = FakeRunner(default=FakeRunResult(0, stdout=out))
|
||||
reader = NixProfileListReader(runner=runner)
|
||||
entries = reader.entries(ctx=None)
|
||||
|
||||
@@ -15,15 +15,18 @@ class TestExtractStdoutText(unittest.TestCase):
|
||||
def test_accepts_object_with_stdout_str(self) -> None:
|
||||
class R:
|
||||
stdout = "ok"
|
||||
|
||||
self.assertEqual(extract_stdout_text(R()), "ok")
|
||||
|
||||
def test_accepts_object_with_stdout_bytes(self) -> None:
|
||||
class R:
|
||||
stdout = b"ok"
|
||||
|
||||
self.assertEqual(extract_stdout_text(R()), "ok")
|
||||
|
||||
def test_fallback_str(self) -> None:
|
||||
class R:
|
||||
def __str__(self) -> str:
|
||||
return "repr"
|
||||
|
||||
self.assertEqual(extract_stdout_text(R()), "repr")
|
||||
|
||||
@@ -8,23 +8,23 @@ from pkgmgr.actions.install.installers.nix.textparse import NixConflictTextParse
|
||||
class TestNixConflictTextParser(unittest.TestCase):
|
||||
def test_remove_tokens_parses_unquoted_and_quoted(self) -> None:
|
||||
t = NixConflictTextParser()
|
||||
text = '''
|
||||
text = """
|
||||
nix profile remove pkgmgr
|
||||
nix profile remove 'pkgmgr-1'
|
||||
nix profile remove "default-2"
|
||||
'''
|
||||
"""
|
||||
tokens = t.remove_tokens(text)
|
||||
self.assertEqual(tokens, ["pkgmgr", "pkgmgr-1", "default-2"])
|
||||
|
||||
def test_existing_store_prefixes_extracts_existing_section_only(self) -> None:
|
||||
t = NixConflictTextParser()
|
||||
text = '''
|
||||
text = """
|
||||
error: An existing package already provides the following file:
|
||||
/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-pkgmgr/bin/pkgmgr
|
||||
/nix/store/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-pkgmgr/share/doc
|
||||
This is the conflicting file from the new package:
|
||||
/nix/store/cccccccccccccccccccccccccccccccc-pkgmgr/bin/pkgmgr
|
||||
'''
|
||||
"""
|
||||
prefixes = t.existing_store_prefixes(text)
|
||||
self.assertEqual(len(prefixes), 2)
|
||||
self.assertTrue(prefixes[0].startswith("/nix/store/"))
|
||||
|
||||
@@ -5,7 +5,9 @@ import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.actions.install.context import RepoContext
|
||||
from pkgmgr.actions.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller
|
||||
from pkgmgr.actions.install.installers.os_packages.arch_pkgbuild import (
|
||||
ArchPkgbuildInstaller,
|
||||
)
|
||||
|
||||
|
||||
class TestArchPkgbuildInstaller(unittest.TestCase):
|
||||
@@ -26,7 +28,10 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
|
||||
)
|
||||
self.installer = ArchPkgbuildInstaller()
|
||||
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
|
||||
@patch(
|
||||
"pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid",
|
||||
return_value=1000,
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which")
|
||||
def test_supports_true_when_tools_and_pkgbuild_exist(
|
||||
@@ -46,7 +51,10 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
|
||||
self.assertIn("makepkg", calls)
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "PKGBUILD"))
|
||||
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=0)
|
||||
@patch(
|
||||
"pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid",
|
||||
return_value=0,
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which")
|
||||
def test_supports_false_when_running_as_root(
|
||||
@@ -55,7 +63,10 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
|
||||
mock_which.return_value = "/usr/bin/pacman"
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
|
||||
@patch(
|
||||
"pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid",
|
||||
return_value=1000,
|
||||
)
|
||||
@patch("os.path.exists", return_value=False)
|
||||
@patch("shutil.which")
|
||||
def test_supports_false_when_pkgbuild_missing(
|
||||
@@ -65,7 +76,10 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.run_command")
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
|
||||
@patch(
|
||||
"pkgmgr.actions.install.installers.os_packages.arch_pkgbuild.os.geteuid",
|
||||
return_value=1000,
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which")
|
||||
def test_run_builds_and_installs_with_makepkg(
|
||||
|
||||
@@ -43,9 +43,7 @@ class TestDebianControlInstaller(unittest.TestCase):
|
||||
"""
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch(
|
||||
"pkgmgr.actions.install.installers.os_packages.debian_control.run_command"
|
||||
)
|
||||
@patch("pkgmgr.actions.install.installers.os_packages.debian_control.run_command")
|
||||
@patch("glob.glob", return_value=["/tmp/package-manager_0.1.1_all.deb"])
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which")
|
||||
@@ -88,9 +86,7 @@ class TestDebianControlInstaller(unittest.TestCase):
|
||||
# 2) apt-get build-dep -y ./ (with or without trailing space)
|
||||
self.assertTrue(
|
||||
any(
|
||||
"apt-get build-dep -y ./ " in cmd
|
||||
or "apt-get build-dep -y ./"
|
||||
in cmd
|
||||
"apt-get build-dep -y ./ " in cmd or "apt-get build-dep -y ./" in cmd
|
||||
for cmd in cmds
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,10 +26,10 @@ class TestMakefileInstaller(unittest.TestCase):
|
||||
)
|
||||
self.installer = MakefileInstaller()
|
||||
|
||||
# @patch("os.path.exists", return_value=True)
|
||||
# def test_supports_true_when_makefile_exists(self, mock_exists):
|
||||
# self.assertTrue(self.installer.supports(self.ctx))
|
||||
# mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "Makefile"))
|
||||
# @patch("os.path.exists", return_value=True)
|
||||
# def test_supports_true_when_makefile_exists(self, mock_exists):
|
||||
# self.assertTrue(self.installer.supports(self.ctx))
|
||||
# mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "Makefile"))
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_makefile_missing(self, mock_exists):
|
||||
|
||||
@@ -152,7 +152,9 @@ class TestDetectCapabilities(unittest.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
with patch("pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]):
|
||||
with patch(
|
||||
"pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]
|
||||
):
|
||||
caps = detect_capabilities(self.ctx, layers)
|
||||
|
||||
self.assertEqual(
|
||||
@@ -282,7 +284,9 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
with patch("pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [cap_only_make]):
|
||||
with patch(
|
||||
"pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [cap_only_make]
|
||||
):
|
||||
effective = resolve_effective_capabilities(self.ctx, layers)
|
||||
|
||||
self.assertEqual(effective["makefile"], {"make-install"})
|
||||
@@ -305,7 +309,9 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
with patch("pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]):
|
||||
with patch(
|
||||
"pkgmgr.actions.install.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]
|
||||
):
|
||||
effective = resolve_effective_capabilities(self.ctx, layers)
|
||||
|
||||
self.assertEqual(effective["makefile"], set())
|
||||
|
||||
@@ -33,4 +33,4 @@ class TestRepoContext(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
||||
@@ -36,8 +36,12 @@ class TestMirrorContext(unittest.TestCase):
|
||||
|
||||
self.assertEqual(ctx.identifier, "id")
|
||||
self.assertEqual(ctx.repo_dir, "/tmp/repo")
|
||||
self.assertEqual(ctx.config_mirrors, {"origin": "git@github.com:alice/repo.git"})
|
||||
self.assertEqual(ctx.file_mirrors, {"backup": "ssh://git@backup/alice/repo.git"})
|
||||
self.assertEqual(
|
||||
ctx.config_mirrors, {"origin": "git@github.com:alice/repo.git"}
|
||||
)
|
||||
self.assertEqual(
|
||||
ctx.file_mirrors, {"backup": "ssh://git@backup/alice/repo.git"}
|
||||
)
|
||||
self.assertEqual(
|
||||
ctx.resolved_mirrors,
|
||||
{
|
||||
|
||||
@@ -17,7 +17,9 @@ class TestDiffCmd(unittest.TestCase):
|
||||
"""
|
||||
|
||||
@patch("pkgmgr.actions.mirror.diff_cmd.build_context")
|
||||
def test_diff_mirrors_reports_only_in_config_and_only_in_file(self, mock_build_context) -> None:
|
||||
def test_diff_mirrors_reports_only_in_config_and_only_in_file(
|
||||
self, mock_build_context
|
||||
) -> None:
|
||||
ctx = MagicMock()
|
||||
ctx.identifier = "id"
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
@@ -30,7 +32,9 @@ class TestDiffCmd(unittest.TestCase):
|
||||
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
diff_mirrors(selected_repos=[{}], repositories_base_dir="/base", all_repos=[])
|
||||
diff_mirrors(
|
||||
selected_repos=[{}], repositories_base_dir="/base", all_repos=[]
|
||||
)
|
||||
|
||||
out = buf.getvalue()
|
||||
self.assertIn("[ONLY IN CONFIG] cfgonly: b", out)
|
||||
@@ -48,7 +52,9 @@ class TestDiffCmd(unittest.TestCase):
|
||||
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
diff_mirrors(selected_repos=[{}], repositories_base_dir="/base", all_repos=[])
|
||||
diff_mirrors(
|
||||
selected_repos=[{}], repositories_base_dir="/base", all_repos=[]
|
||||
)
|
||||
|
||||
out = buf.getvalue()
|
||||
self.assertIn("[URL MISMATCH]", out)
|
||||
@@ -67,7 +73,9 @@ class TestDiffCmd(unittest.TestCase):
|
||||
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
diff_mirrors(selected_repos=[{}], repositories_base_dir="/base", all_repos=[])
|
||||
diff_mirrors(
|
||||
selected_repos=[{}], repositories_base_dir="/base", all_repos=[]
|
||||
)
|
||||
|
||||
out = buf.getvalue()
|
||||
self.assertIn("[OK] Mirrors in config and MIRRORS file are in sync.", out)
|
||||
|
||||
@@ -28,7 +28,9 @@ class TestMirrorGitRemote(unittest.TestCase):
|
||||
# resolved_mirrors = config + file (file wins), so put origin in file.
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = self._ctx(file={"origin": "git@github.com:alice/repo.git"})
|
||||
self.assertEqual(determine_primary_remote_url(repo, ctx), "git@github.com:alice/repo.git")
|
||||
self.assertEqual(
|
||||
determine_primary_remote_url(repo, ctx), "git@github.com:alice/repo.git"
|
||||
)
|
||||
|
||||
def test_determine_primary_falls_back_to_file_order(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
@@ -43,9 +45,14 @@ class TestMirrorGitRemote(unittest.TestCase):
|
||||
def test_determine_primary_fallback_default(self) -> None:
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "repo"}
|
||||
ctx = self._ctx()
|
||||
self.assertEqual(determine_primary_remote_url(repo, ctx), "git@github.com:alice/repo.git")
|
||||
self.assertEqual(
|
||||
determine_primary_remote_url(repo, ctx), "git@github.com:alice/repo.git"
|
||||
)
|
||||
|
||||
@patch("pkgmgr.actions.mirror.git_remote.list_remotes", return_value=["origin", "backup"])
|
||||
@patch(
|
||||
"pkgmgr.actions.mirror.git_remote.list_remotes",
|
||||
return_value=["origin", "backup"],
|
||||
)
|
||||
def test_has_origin_remote_true(self, _m_list) -> None:
|
||||
self.assertTrue(has_origin_remote("/tmp/repo"))
|
||||
|
||||
|
||||
@@ -23,15 +23,23 @@ class TestGitRemotePrimaryPush(unittest.TestCase):
|
||||
)
|
||||
|
||||
with patch("os.path.isdir", return_value=True):
|
||||
with patch("pkgmgr.actions.mirror.git_remote.has_origin_remote", return_value=False), patch(
|
||||
"pkgmgr.actions.mirror.git_remote.add_remote"
|
||||
) as m_add_remote, patch(
|
||||
"pkgmgr.actions.mirror.git_remote.set_remote_url"
|
||||
) as m_set_remote_url, patch(
|
||||
"pkgmgr.actions.mirror.git_remote.get_remote_push_urls", return_value=set()
|
||||
), patch(
|
||||
"pkgmgr.actions.mirror.git_remote.add_remote_push_url"
|
||||
) as m_add_push:
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.mirror.git_remote.has_origin_remote",
|
||||
return_value=False,
|
||||
),
|
||||
patch("pkgmgr.actions.mirror.git_remote.add_remote") as m_add_remote,
|
||||
patch(
|
||||
"pkgmgr.actions.mirror.git_remote.set_remote_url"
|
||||
) as m_set_remote_url,
|
||||
patch(
|
||||
"pkgmgr.actions.mirror.git_remote.get_remote_push_urls",
|
||||
return_value=set(),
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.mirror.git_remote.add_remote_push_url"
|
||||
) as m_add_push,
|
||||
):
|
||||
ensure_origin_remote(repo, ctx, preview=False)
|
||||
|
||||
# determine_primary_remote_url falls back to file order (primary first)
|
||||
|
||||
@@ -7,7 +7,11 @@ import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from pkgmgr.actions.mirror.io import load_config_mirrors, read_mirrors_file, write_mirrors_file
|
||||
from pkgmgr.actions.mirror.io import (
|
||||
load_config_mirrors,
|
||||
read_mirrors_file,
|
||||
write_mirrors_file,
|
||||
)
|
||||
|
||||
|
||||
class TestMirrorIO(unittest.TestCase):
|
||||
@@ -76,7 +80,10 @@ class TestMirrorIO(unittest.TestCase):
|
||||
self.assertEqual(mirrors["github.com2"], "https://github.com/alice/repo2")
|
||||
|
||||
self.assertIn("git@git.veen.world", mirrors)
|
||||
self.assertEqual(mirrors["git@git.veen.world"], "ssh://git@git.veen.world:2201/alice/repo3.git")
|
||||
self.assertEqual(
|
||||
mirrors["git@git.veen.world"],
|
||||
"ssh://git@git.veen.world:2201/alice/repo3.git",
|
||||
)
|
||||
|
||||
def test_read_mirrors_file_missing_returns_empty(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
@@ -96,7 +103,9 @@ class TestMirrorIO(unittest.TestCase):
|
||||
with open(p, "r", encoding="utf-8") as fh:
|
||||
content = fh.read()
|
||||
|
||||
self.assertEqual(content, "a ssh://a.example/repo.git\nb ssh://b.example/repo.git\n")
|
||||
self.assertEqual(
|
||||
content, "a ssh://a.example/repo.git\nb ssh://b.example/repo.git\n"
|
||||
)
|
||||
|
||||
def test_write_mirrors_file_preview_does_not_create_file(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
|
||||
@@ -23,7 +23,9 @@ class TestListCmd(unittest.TestCase):
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
ctx.config_mirrors = {"origin": "a"}
|
||||
ctx.file_mirrors = {"backup": "b"}
|
||||
type(ctx).resolved_mirrors = PropertyMock(return_value={"origin": "a", "backup": "b"})
|
||||
type(ctx).resolved_mirrors = PropertyMock(
|
||||
return_value={"origin": "a", "backup": "b"}
|
||||
)
|
||||
mock_build_context.return_value = ctx
|
||||
|
||||
buf = io.StringIO()
|
||||
@@ -49,7 +51,9 @@ class TestListCmd(unittest.TestCase):
|
||||
ctx.repo_dir = "/tmp/repo"
|
||||
ctx.config_mirrors = {"origin": "a"}
|
||||
ctx.file_mirrors = {"backup": "b"}
|
||||
type(ctx).resolved_mirrors = PropertyMock(return_value={"origin": "a", "backup": "b"})
|
||||
type(ctx).resolved_mirrors = PropertyMock(
|
||||
return_value={"origin": "a", "backup": "b"}
|
||||
)
|
||||
mock_build_context.return_value = ctx
|
||||
|
||||
buf = io.StringIO()
|
||||
|
||||
@@ -30,7 +30,9 @@ class TestRemoteProvision(unittest.TestCase):
|
||||
ctx.identifier = "repo-id"
|
||||
mock_build_context.return_value = ctx
|
||||
|
||||
mock_determine_primary.return_value = "ssh://git@git.veen.world:2201/alice/repo.git"
|
||||
mock_determine_primary.return_value = (
|
||||
"ssh://git@git.veen.world:2201/alice/repo.git"
|
||||
)
|
||||
|
||||
result = MagicMock()
|
||||
result.status = "created"
|
||||
|
||||
@@ -8,7 +8,9 @@ from pkgmgr.actions.mirror.types import RepoMirrorContext
|
||||
|
||||
|
||||
class TestMirrorSetupCmd(unittest.TestCase):
|
||||
def _ctx(self, *, repo_dir: str = "/tmp/repo", resolved: dict[str, str] | None = None) -> RepoMirrorContext:
|
||||
def _ctx(
|
||||
self, *, repo_dir: str = "/tmp/repo", resolved: dict[str, str] | None = None
|
||||
) -> RepoMirrorContext:
|
||||
# resolved_mirrors is a @property combining config+file. Put it into file_mirrors.
|
||||
return RepoMirrorContext(
|
||||
identifier="repo",
|
||||
@@ -19,7 +21,9 @@ class TestMirrorSetupCmd(unittest.TestCase):
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.ensure_origin_remote")
|
||||
def test_setup_mirrors_local_calls_ensure_origin_remote(self, m_ensure, m_ctx) -> None:
|
||||
def test_setup_mirrors_local_calls_ensure_origin_remote(
|
||||
self, m_ensure, m_ctx
|
||||
) -> None:
|
||||
ctx = self._ctx(repo_dir="/tmp/repo", resolved={"primary": "git@x/y.git"})
|
||||
m_ctx.return_value = ctx
|
||||
|
||||
@@ -40,12 +44,16 @@ class TestMirrorSetupCmd(unittest.TestCase):
|
||||
|
||||
self.assertEqual(args[0], repos[0])
|
||||
self.assertIs(args[1], ctx)
|
||||
self.assertEqual(kwargs.get("preview", args[2] if len(args) >= 3 else None), True)
|
||||
self.assertEqual(
|
||||
kwargs.get("preview", args[2] if len(args) >= 3 else None), True
|
||||
)
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.determine_primary_remote_url")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.probe_remote_reachable")
|
||||
def test_setup_mirrors_remote_no_mirrors_probes_primary(self, m_probe, m_primary, m_ctx) -> None:
|
||||
def test_setup_mirrors_remote_no_mirrors_probes_primary(
|
||||
self, m_probe, m_primary, m_ctx
|
||||
) -> None:
|
||||
m_ctx.return_value = self._ctx(repo_dir="/tmp/repo", resolved={})
|
||||
m_primary.return_value = "git@github.com:alice/repo.git"
|
||||
m_probe.return_value = True
|
||||
@@ -62,11 +70,15 @@ class TestMirrorSetupCmd(unittest.TestCase):
|
||||
)
|
||||
|
||||
m_primary.assert_called()
|
||||
m_probe.assert_called_once_with("git@github.com:alice/repo.git", cwd="/tmp/repo")
|
||||
m_probe.assert_called_once_with(
|
||||
"git@github.com:alice/repo.git", cwd="/tmp/repo"
|
||||
)
|
||||
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.build_context")
|
||||
@patch("pkgmgr.actions.mirror.setup_cmd.probe_remote_reachable")
|
||||
def test_setup_mirrors_remote_with_mirrors_probes_each(self, m_probe, m_ctx) -> None:
|
||||
def test_setup_mirrors_remote_with_mirrors_probes_each(
|
||||
self, m_probe, m_ctx
|
||||
) -> None:
|
||||
m_ctx.return_value = self._ctx(
|
||||
repo_dir="/tmp/repo",
|
||||
resolved={
|
||||
@@ -89,7 +101,9 @@ class TestMirrorSetupCmd(unittest.TestCase):
|
||||
|
||||
self.assertEqual(m_probe.call_count, 2)
|
||||
m_probe.assert_any_call("git@github.com:alice/repo.git", cwd="/tmp/repo")
|
||||
m_probe.assert_any_call("ssh://git@git.veen.world:2201/alice/repo.git", cwd="/tmp/repo")
|
||||
m_probe.assert_any_call(
|
||||
"ssh://git@git.veen.world:2201/alice/repo.git", cwd="/tmp/repo"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -5,7 +5,11 @@ from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from pkgmgr.actions.mirror.url_utils import hostport_from_git_url, normalize_provider_host, parse_repo_from_git_url
|
||||
from pkgmgr.actions.mirror.url_utils import (
|
||||
hostport_from_git_url,
|
||||
normalize_provider_host,
|
||||
parse_repo_from_git_url,
|
||||
)
|
||||
|
||||
|
||||
class TestUrlUtils(unittest.TestCase):
|
||||
@@ -14,7 +18,9 @@ class TestUrlUtils(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def test_hostport_from_git_url_ssh_url_with_port(self) -> None:
|
||||
host, port = hostport_from_git_url("ssh://git@code.example.org:2201/alice/repo.git")
|
||||
host, port = hostport_from_git_url(
|
||||
"ssh://git@code.example.org:2201/alice/repo.git"
|
||||
)
|
||||
self.assertEqual(host, "code.example.org")
|
||||
self.assertEqual(port, "2201")
|
||||
|
||||
@@ -34,7 +40,9 @@ class TestUrlUtils(unittest.TestCase):
|
||||
self.assertIsNone(port)
|
||||
|
||||
def test_normalize_provider_host_strips_port_and_lowercases(self) -> None:
|
||||
self.assertEqual(normalize_provider_host("GIT.VEEN.WORLD:2201"), "git.veen.world")
|
||||
self.assertEqual(
|
||||
normalize_provider_host("GIT.VEEN.WORLD:2201"), "git.veen.world"
|
||||
)
|
||||
|
||||
def test_normalize_provider_host_ipv6_brackets(self) -> None:
|
||||
self.assertEqual(normalize_provider_host("[::1]"), "::1")
|
||||
@@ -43,7 +51,9 @@ class TestUrlUtils(unittest.TestCase):
|
||||
self.assertEqual(normalize_provider_host(""), "")
|
||||
|
||||
def test_parse_repo_from_git_url_ssh_url(self) -> None:
|
||||
host, owner, name = parse_repo_from_git_url("ssh://git@code.example.org:2201/alice/repo.git")
|
||||
host, owner, name = parse_repo_from_git_url(
|
||||
"ssh://git@code.example.org:2201/alice/repo.git"
|
||||
)
|
||||
self.assertEqual(host, "code.example.org")
|
||||
self.assertEqual(owner, "alice")
|
||||
self.assertEqual(name, "repo")
|
||||
|
||||
@@ -9,6 +9,9 @@ class TestHeadSemverTags(unittest.TestCase):
|
||||
def test_no_tags(self, _mock_get_tags_at_ref) -> None:
|
||||
self.assertEqual(head_semver_tags(), [])
|
||||
|
||||
@patch("pkgmgr.actions.publish.git_tags.get_tags_at_ref", return_value=["v2.0.0", "nope", "v1.0.0", "v1.2.0"])
|
||||
@patch(
|
||||
"pkgmgr.actions.publish.git_tags.get_tags_at_ref",
|
||||
return_value=["v2.0.0", "nope", "v1.0.0", "v1.2.0"],
|
||||
)
|
||||
def test_filters_and_sorts_semver(self, _mock_get_tags_at_ref) -> None:
|
||||
self.assertEqual(head_semver_tags(), ["v1.0.0", "v1.2.0", "v2.0.0"])
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import unittest
|
||||
from pkgmgr.actions.publish.pypi_url import parse_pypi_project_url
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -9,9 +8,7 @@ class TestPublishWorkflowPreview(unittest.TestCase):
|
||||
@patch("pkgmgr.actions.publish.workflow.read_mirrors_file")
|
||||
@patch("pkgmgr.actions.publish.workflow.head_semver_tags")
|
||||
def test_preview_does_not_build(self, mock_tags, mock_mirrors):
|
||||
mock_mirrors.return_value = {
|
||||
"pypi": "https://pypi.org/project/example/"
|
||||
}
|
||||
mock_mirrors.return_value = {"pypi": "https://pypi.org/project/example/"}
|
||||
mock_tags.return_value = ["v1.0.0"]
|
||||
|
||||
publish(
|
||||
|
||||
@@ -19,13 +19,16 @@ from pkgmgr.actions.release.files import (
|
||||
|
||||
class TestUpdatePyprojectVersion(unittest.TestCase):
|
||||
def test_update_pyproject_version_replaces_version_line(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "pyproject.toml")
|
||||
@@ -41,13 +44,16 @@ class TestUpdatePyprojectVersion(unittest.TestCase):
|
||||
self.assertNotIn('version = "0.1.0"', content)
|
||||
|
||||
def test_update_pyproject_version_preview_does_not_write(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "pyproject.toml")
|
||||
@@ -63,12 +69,15 @@ class TestUpdatePyprojectVersion(unittest.TestCase):
|
||||
self.assertEqual(content, original)
|
||||
|
||||
def test_update_pyproject_version_exits_when_no_version_line_found(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[project]
|
||||
name = "example"
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "pyproject.toml")
|
||||
@@ -129,13 +138,16 @@ class TestUpdateFlakeVersion(unittest.TestCase):
|
||||
|
||||
class TestUpdatePkgbuildVersion(unittest.TestCase):
|
||||
def test_update_pkgbuild_version_normal(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
pkgname=example
|
||||
pkgver=0.1.0
|
||||
pkgrel=5
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "PKGBUILD")
|
||||
@@ -152,13 +164,16 @@ class TestUpdatePkgbuildVersion(unittest.TestCase):
|
||||
self.assertNotIn("pkgver=0.1.0", content)
|
||||
|
||||
def test_update_pkgbuild_version_preview(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
pkgname=example
|
||||
pkgver=0.1.0
|
||||
pkgrel=5
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "PKGBUILD")
|
||||
@@ -175,13 +190,16 @@ class TestUpdatePkgbuildVersion(unittest.TestCase):
|
||||
|
||||
class TestUpdateSpecVersion(unittest.TestCase):
|
||||
def test_update_spec_version_normal(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Name: package-manager
|
||||
Version: 0.1.0
|
||||
Release: 5%{?dist}
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "package-manager.spec")
|
||||
@@ -199,13 +217,16 @@ class TestUpdateSpecVersion(unittest.TestCase):
|
||||
self.assertNotIn("Release: 5%{?dist}", content)
|
||||
|
||||
def test_update_spec_version_preview(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Name: package-manager
|
||||
Version: 0.1.0
|
||||
Release: 5%{?dist}
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "package-manager.spec")
|
||||
@@ -328,8 +349,9 @@ class TestUpdateDebianChangelog(unittest.TestCase):
|
||||
|
||||
class TestUpdateSpecChangelog(unittest.TestCase):
|
||||
def test_update_spec_changelog_inserts_stanza_after_changelog_marker(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Name: package-manager
|
||||
Version: 0.1.0
|
||||
Release: 5%{?dist}
|
||||
@@ -341,7 +363,9 @@ class TestUpdateSpecChangelog(unittest.TestCase):
|
||||
* Mon Jan 01 2024 Old Maintainer <old@example.com> - 0.1.0-1
|
||||
- Old entry
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "package-manager.spec")
|
||||
@@ -375,8 +399,9 @@ class TestUpdateSpecChangelog(unittest.TestCase):
|
||||
self.assertIn("Old Maintainer <old@example.com>", content)
|
||||
|
||||
def test_update_spec_changelog_preview_does_not_write(self) -> None:
|
||||
original = textwrap.dedent(
|
||||
"""
|
||||
original = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Name: package-manager
|
||||
Version: 0.1.0
|
||||
Release: 5%{?dist}
|
||||
@@ -385,7 +410,9 @@ class TestUpdateSpecChangelog(unittest.TestCase):
|
||||
* Mon Jan 01 2024 Old Maintainer <old@example.com> - 0.1.0-1
|
||||
- Old entry
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "package-manager.spec")
|
||||
|
||||
@@ -23,18 +23,20 @@ class TestIsHighestVersionTag(unittest.TestCase):
|
||||
self.assertFalse(is_highest_version_tag("v1.0.0"))
|
||||
|
||||
@patch("pkgmgr.actions.release.git_ops.list_tags")
|
||||
def test_ignores_non_parseable_v_tags_for_semver_compare(self, mock_list_tags) -> None:
|
||||
def test_ignores_non_parseable_v_tags_for_semver_compare(
|
||||
self, mock_list_tags
|
||||
) -> None:
|
||||
mock_list_tags.return_value = ["v1.2.0", "v1.10.0", "v1.2.0-rc1", "vfoo"]
|
||||
|
||||
self.assertTrue(is_highest_version_tag("v1.10.0"))
|
||||
self.assertFalse(is_highest_version_tag("v1.2.0"))
|
||||
|
||||
@patch("pkgmgr.actions.release.git_ops.list_tags")
|
||||
def test_current_tag_not_parseable_falls_back_to_lex_compare(self, mock_list_tags) -> None:
|
||||
def test_current_tag_not_parseable_falls_back_to_lex_compare(
|
||||
self, mock_list_tags
|
||||
) -> None:
|
||||
mock_list_tags.return_value = ["v1.9.0", "v1.10.0"]
|
||||
|
||||
# prerelease must NOT outrank the final release
|
||||
self.assertFalse(is_highest_version_tag("v1.10.0-rc1"))
|
||||
self.assertFalse(is_highest_version_tag("v1.0.0-rc1"))
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ class TestWorkflowReleaseEntryPoint(unittest.TestCase):
|
||||
|
||||
@patch("pkgmgr.actions.release.workflow._release_impl")
|
||||
@patch("pkgmgr.actions.release.workflow.sys.stdin.isatty", return_value=False)
|
||||
def test_release_non_interactive_runs_real_without_confirmation(self, _mock_isatty, mock_impl) -> None:
|
||||
def test_release_non_interactive_runs_real_without_confirmation(
|
||||
self, _mock_isatty, mock_impl
|
||||
) -> None:
|
||||
release(preview=False, force=False, close=False)
|
||||
|
||||
mock_impl.assert_called_once()
|
||||
@@ -35,9 +37,13 @@ class TestWorkflowReleaseEntryPoint(unittest.TestCase):
|
||||
self.assertTrue(kwargs["force"])
|
||||
|
||||
@patch("pkgmgr.actions.release.workflow._release_impl")
|
||||
@patch("pkgmgr.actions.release.workflow.confirm_proceed_release", return_value=False)
|
||||
@patch(
|
||||
"pkgmgr.actions.release.workflow.confirm_proceed_release", return_value=False
|
||||
)
|
||||
@patch("pkgmgr.actions.release.workflow.sys.stdin.isatty", return_value=True)
|
||||
def test_release_interactive_decline_runs_only_preview(self, _mock_isatty, _mock_confirm, mock_impl) -> None:
|
||||
def test_release_interactive_decline_runs_only_preview(
|
||||
self, _mock_isatty, _mock_confirm, mock_impl
|
||||
) -> None:
|
||||
release(preview=False, force=False, close=False)
|
||||
|
||||
# interactive path: preview first, then decline => only one call
|
||||
@@ -47,7 +53,9 @@ class TestWorkflowReleaseEntryPoint(unittest.TestCase):
|
||||
@patch("pkgmgr.actions.release.workflow._release_impl")
|
||||
@patch("pkgmgr.actions.release.workflow.confirm_proceed_release", return_value=True)
|
||||
@patch("pkgmgr.actions.release.workflow.sys.stdin.isatty", return_value=True)
|
||||
def test_release_interactive_accept_runs_preview_then_real(self, _mock_isatty, _mock_confirm, mock_impl) -> None:
|
||||
def test_release_interactive_accept_runs_preview_then_real(
|
||||
self, _mock_isatty, _mock_confirm, mock_impl
|
||||
) -> None:
|
||||
release(preview=False, force=False, close=False)
|
||||
|
||||
self.assertEqual(mock_impl.call_count, 2)
|
||||
|
||||
@@ -40,4 +40,4 @@ class TestTemplateRendererPreview(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
||||
@@ -6,17 +6,32 @@ from pkgmgr.actions.repository.deinstall import deinstall_repos
|
||||
|
||||
class TestDeinstallRepos(unittest.TestCase):
|
||||
def test_preview_removes_nothing_but_runs_make_if_makefile_exists(self):
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "demo", "alias": "demo"}
|
||||
repo = {
|
||||
"provider": "github.com",
|
||||
"account": "alice",
|
||||
"repository": "demo",
|
||||
"alias": "demo",
|
||||
}
|
||||
selected = [repo]
|
||||
|
||||
with patch("pkgmgr.actions.repository.deinstall.get_repo_identifier", return_value="demo"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.get_repo_dir", return_value="/repos/github.com/alice/demo"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.expanduser", return_value="/home/u/.local/bin"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.exists") as mock_exists, \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.remove") as mock_remove, \
|
||||
patch("pkgmgr.actions.repository.deinstall.run_command") as mock_run, \
|
||||
patch("builtins.input", return_value="y"):
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.get_repo_identifier",
|
||||
return_value="demo",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.get_repo_dir",
|
||||
return_value="/repos/github.com/alice/demo",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.os.path.expanduser",
|
||||
return_value="/home/u/.local/bin",
|
||||
),
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.exists") as mock_exists,
|
||||
patch("pkgmgr.actions.repository.deinstall.os.remove") as mock_remove,
|
||||
patch("pkgmgr.actions.repository.deinstall.run_command") as mock_run,
|
||||
patch("builtins.input", return_value="y"),
|
||||
):
|
||||
# alias exists, Makefile exists
|
||||
def exists_side_effect(path):
|
||||
if path == "/home/u/.local/bin/demo":
|
||||
@@ -46,17 +61,32 @@ class TestDeinstallRepos(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_non_preview_removes_alias_when_confirmed(self):
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "demo", "alias": "demo"}
|
||||
repo = {
|
||||
"provider": "github.com",
|
||||
"account": "alice",
|
||||
"repository": "demo",
|
||||
"alias": "demo",
|
||||
}
|
||||
selected = [repo]
|
||||
|
||||
with patch("pkgmgr.actions.repository.deinstall.get_repo_identifier", return_value="demo"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.get_repo_dir", return_value="/repos/github.com/alice/demo"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.expanduser", return_value="/home/u/.local/bin"), \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.exists") as mock_exists, \
|
||||
patch("pkgmgr.actions.repository.deinstall.os.remove") as mock_remove, \
|
||||
patch("pkgmgr.actions.repository.deinstall.run_command") as mock_run, \
|
||||
patch("builtins.input", return_value="y"):
|
||||
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.get_repo_identifier",
|
||||
return_value="demo",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.get_repo_dir",
|
||||
return_value="/repos/github.com/alice/demo",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.actions.repository.deinstall.os.path.expanduser",
|
||||
return_value="/home/u/.local/bin",
|
||||
),
|
||||
patch("pkgmgr.actions.repository.deinstall.os.path.exists") as mock_exists,
|
||||
patch("pkgmgr.actions.repository.deinstall.os.remove") as mock_remove,
|
||||
patch("pkgmgr.actions.repository.deinstall.run_command") as mock_run,
|
||||
patch("builtins.input", return_value="y"),
|
||||
):
|
||||
# alias exists, Makefile does NOT exist
|
||||
def exists_side_effect(path):
|
||||
if path == "/home/u/.local/bin/demo":
|
||||
|
||||
@@ -10,7 +10,9 @@ from pkgmgr.cli.commands.changelog import _find_previous_and_current_tag
|
||||
|
||||
class TestGenerateChangelog(unittest.TestCase):
|
||||
@patch("pkgmgr.actions.changelog.get_changelog")
|
||||
def test_generate_changelog_default_range_no_merges(self, mock_get_changelog) -> None:
|
||||
def test_generate_changelog_default_range_no_merges(
|
||||
self, mock_get_changelog
|
||||
) -> None:
|
||||
mock_get_changelog.return_value = "abc123 (HEAD -> main) Initial commit"
|
||||
|
||||
output = generate_changelog(cwd="/repo")
|
||||
@@ -43,7 +45,9 @@ class TestGenerateChangelog(unittest.TestCase):
|
||||
)
|
||||
|
||||
@patch("pkgmgr.actions.changelog.get_changelog")
|
||||
def test_generate_changelog_giterror_returns_error_message(self, mock_get_changelog) -> None:
|
||||
def test_generate_changelog_giterror_returns_error_message(
|
||||
self, mock_get_changelog
|
||||
) -> None:
|
||||
mock_get_changelog.side_effect = GitChangelogQueryError("simulated git failure")
|
||||
|
||||
result = generate_changelog(cwd="/repo", from_ref="v0.1.0", to_ref="v0.2.0")
|
||||
@@ -53,7 +57,9 @@ class TestGenerateChangelog(unittest.TestCase):
|
||||
self.assertIn("v0.1.0..v0.2.0", result)
|
||||
|
||||
@patch("pkgmgr.actions.changelog.get_changelog")
|
||||
def test_generate_changelog_empty_output_returns_info(self, mock_get_changelog) -> None:
|
||||
def test_generate_changelog_empty_output_returns_info(
|
||||
self, mock_get_changelog
|
||||
) -> None:
|
||||
mock_get_changelog.return_value = " \n "
|
||||
|
||||
result = generate_changelog(cwd="/repo", from_ref=None, to_ref="HEAD")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
@@ -101,9 +101,7 @@ class TestReleaseCommand(unittest.TestCase):
|
||||
handle_release(args, ctx, selected)
|
||||
|
||||
# We should have changed into the repo dir and then back.
|
||||
mock_chdir.assert_has_calls(
|
||||
[call("/repos/dummy"), call("/cwd")]
|
||||
)
|
||||
mock_chdir.assert_has_calls([call("/repos/dummy"), call("/cwd")])
|
||||
|
||||
# And run_release should be invoked once with the expected parameters.
|
||||
mock_run_release.assert_called_once_with(
|
||||
|
||||
@@ -30,10 +30,12 @@ class TestCLIReleasePublishHook(unittest.TestCase):
|
||||
no_publish=False,
|
||||
)
|
||||
|
||||
with patch("pkgmgr.cli.commands.release.run_release") as m_release, patch(
|
||||
"pkgmgr.cli.commands.release.run_publish"
|
||||
) as m_publish, patch(
|
||||
"pkgmgr.cli.commands.release.sys.stdin.isatty", return_value=False
|
||||
with (
|
||||
patch("pkgmgr.cli.commands.release.run_release") as m_release,
|
||||
patch("pkgmgr.cli.commands.release.run_publish") as m_publish,
|
||||
patch(
|
||||
"pkgmgr.cli.commands.release.sys.stdin.isatty", return_value=False
|
||||
),
|
||||
):
|
||||
handle_release(args=args, ctx=self._ctx(), selected=[repo])
|
||||
|
||||
@@ -62,9 +64,10 @@ class TestCLIReleasePublishHook(unittest.TestCase):
|
||||
no_publish=True,
|
||||
)
|
||||
|
||||
with patch("pkgmgr.cli.commands.release.run_release") as m_release, patch(
|
||||
"pkgmgr.cli.commands.release.run_publish"
|
||||
) as m_publish:
|
||||
with (
|
||||
patch("pkgmgr.cli.commands.release.run_release") as m_release,
|
||||
patch("pkgmgr.cli.commands.release.run_publish") as m_publish,
|
||||
):
|
||||
handle_release(args=args, ctx=self._ctx(), selected=[repo])
|
||||
|
||||
m_release.assert_called_once()
|
||||
|
||||
@@ -79,9 +79,10 @@ class TestReposCommand(unittest.TestCase):
|
||||
|
||||
buf = io.StringIO()
|
||||
|
||||
with patch(
|
||||
"pkgmgr.cli.commands.repos.get_repo_dir"
|
||||
) as mock_get_repo_dir, redirect_stdout(buf):
|
||||
with (
|
||||
patch("pkgmgr.cli.commands.repos.get_repo_dir") as mock_get_repo_dir,
|
||||
redirect_stdout(buf),
|
||||
):
|
||||
handle_repos_command(args, ctx, selected=repos)
|
||||
|
||||
output = buf.getvalue().strip().splitlines()
|
||||
@@ -113,10 +114,13 @@ class TestReposCommand(unittest.TestCase):
|
||||
|
||||
buf = io.StringIO()
|
||||
|
||||
with patch(
|
||||
"pkgmgr.cli.commands.repos.get_repo_dir",
|
||||
return_value="/resolved/from/get_repo_dir",
|
||||
) as mock_get_repo_dir, redirect_stdout(buf):
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.cli.commands.repos.get_repo_dir",
|
||||
return_value="/resolved/from/get_repo_dir",
|
||||
) as mock_get_repo_dir,
|
||||
redirect_stdout(buf),
|
||||
):
|
||||
handle_repos_command(args, ctx, selected=repos)
|
||||
|
||||
output = buf.getvalue().strip().splitlines()
|
||||
@@ -168,12 +172,13 @@ class TestReposCommand(unittest.TestCase):
|
||||
shell_command=["echo", "hello"],
|
||||
)
|
||||
|
||||
with patch(
|
||||
"pkgmgr.cli.commands.repos.get_repo_dir",
|
||||
return_value="/resolved/for/shell",
|
||||
) as mock_get_repo_dir, patch(
|
||||
"pkgmgr.cli.commands.repos.run_command"
|
||||
) as mock_run_command:
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.cli.commands.repos.get_repo_dir",
|
||||
return_value="/resolved/for/shell",
|
||||
) as mock_get_repo_dir,
|
||||
patch("pkgmgr.cli.commands.repos.run_command") as mock_run_command,
|
||||
):
|
||||
buf = io.StringIO()
|
||||
with redirect_stdout(buf):
|
||||
handle_repos_command(args, ctx, selected=repos)
|
||||
@@ -185,7 +190,7 @@ class TestReposCommand(unittest.TestCase):
|
||||
mock_run_command.assert_called_once()
|
||||
called_args, called_kwargs = mock_run_command.call_args
|
||||
|
||||
self.assertEqual("echo hello", called_args[0]) # command string
|
||||
self.assertEqual("echo hello", called_args[0]) # command string
|
||||
self.assertEqual("/resolved/for/shell", called_kwargs["cwd"])
|
||||
self.assertFalse(called_kwargs["preview"])
|
||||
|
||||
|
||||
@@ -115,13 +115,16 @@ class TestCliVersion(unittest.TestCase):
|
||||
"""
|
||||
Write a minimal PEP 621-style pyproject.toml into the temp directory.
|
||||
"""
|
||||
content = textwrap.dedent(
|
||||
f"""
|
||||
content = (
|
||||
textwrap.dedent(
|
||||
f"""
|
||||
[project]
|
||||
name = "pkgmgr-test"
|
||||
version = "{version}"
|
||||
"""
|
||||
).strip() + "\n"
|
||||
).strip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
path = os.path.join(self._tmp_dir.name, "pyproject.toml")
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
|
||||
@@ -27,7 +27,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.open_branch")
|
||||
def test_handle_branch_open_forwards_args_to_open_branch(self, mock_open_branch) -> None:
|
||||
def test_handle_branch_open_forwards_args_to_open_branch(
|
||||
self, mock_open_branch
|
||||
) -> None:
|
||||
"""
|
||||
handle_branch('open') should call open_branch with name, base and cwd='.'.
|
||||
"""
|
||||
@@ -49,7 +51,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
self.assertEqual(call_kwargs.get("cwd"), ".")
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.open_branch")
|
||||
def test_handle_branch_open_uses_default_base_when_not_set(self, mock_open_branch) -> None:
|
||||
def test_handle_branch_open_uses_default_base_when_not_set(
|
||||
self, mock_open_branch
|
||||
) -> None:
|
||||
"""
|
||||
If --base is not passed, argparse gives base='main' (default),
|
||||
and handle_branch should propagate that to open_branch.
|
||||
@@ -75,7 +79,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.close_branch")
|
||||
def test_handle_branch_close_forwards_args_to_close_branch(self, mock_close_branch) -> None:
|
||||
def test_handle_branch_close_forwards_args_to_close_branch(
|
||||
self, mock_close_branch
|
||||
) -> None:
|
||||
"""
|
||||
handle_branch('close') should call close_branch with name, base,
|
||||
cwd='.' and force=False by default.
|
||||
@@ -100,7 +106,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
self.assertFalse(call_kwargs.get("force"))
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.close_branch")
|
||||
def test_handle_branch_close_uses_default_base_when_not_set(self, mock_close_branch) -> None:
|
||||
def test_handle_branch_close_uses_default_base_when_not_set(
|
||||
self, mock_close_branch
|
||||
) -> None:
|
||||
"""
|
||||
If --base is not passed for 'close', argparse gives base='main'
|
||||
(default), and handle_branch should propagate that to close_branch.
|
||||
@@ -153,7 +161,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.drop_branch")
|
||||
def test_handle_branch_drop_forwards_args_to_drop_branch(self, mock_drop_branch) -> None:
|
||||
def test_handle_branch_drop_forwards_args_to_drop_branch(
|
||||
self, mock_drop_branch
|
||||
) -> None:
|
||||
"""
|
||||
handle_branch('drop') should call drop_branch with name, base,
|
||||
cwd='.' and force=False by default.
|
||||
@@ -178,7 +188,9 @@ class TestCliBranch(unittest.TestCase):
|
||||
self.assertFalse(call_kwargs.get("force"))
|
||||
|
||||
@patch("pkgmgr.cli.commands.branch.drop_branch")
|
||||
def test_handle_branch_drop_uses_default_base_when_not_set(self, mock_drop_branch) -> None:
|
||||
def test_handle_branch_drop_uses_default_base_when_not_set(
|
||||
self, mock_drop_branch
|
||||
) -> None:
|
||||
"""
|
||||
If --base is not passed for 'drop', argparse gives base='main'
|
||||
(default), and handle_branch should propagate that to drop_branch.
|
||||
|
||||
@@ -20,7 +20,9 @@ class TestResolveRepositoryPath(unittest.TestCase):
|
||||
ctx = SimpleNamespace(repositories_base_dir="/base", repositories_dir="/base2")
|
||||
repo = {"provider": "github.com", "account": "acme", "repository": "demo"}
|
||||
|
||||
with patch("pkgmgr.cli.tools.paths.get_repo_dir", return_value="/computed/repo") as m:
|
||||
with patch(
|
||||
"pkgmgr.cli.tools.paths.get_repo_dir", return_value="/computed/repo"
|
||||
) as m:
|
||||
out = resolve_repository_path(repo, ctx)
|
||||
|
||||
self.assertEqual(out, "/computed/repo")
|
||||
|
||||
@@ -27,7 +27,9 @@ class TestOpenVSCodeWorkspace(unittest.TestCase):
|
||||
from pkgmgr.cli.tools.vscode import open_vscode_workspace
|
||||
|
||||
ctx = SimpleNamespace(config_merged={}, all_repositories=[])
|
||||
selected: List[Repository] = [{"provider": "github.com", "account": "x", "repository": "y"}]
|
||||
selected: List[Repository] = [
|
||||
{"provider": "github.com", "account": "x", "repository": "y"}
|
||||
]
|
||||
|
||||
with patch("pkgmgr.cli.tools.vscode.shutil.which", return_value=None):
|
||||
with self.assertRaises(RuntimeError) as cm:
|
||||
@@ -42,11 +44,16 @@ class TestOpenVSCodeWorkspace(unittest.TestCase):
|
||||
config_merged={"directories": {"workspaces": "~/Workspaces"}},
|
||||
all_repositories=[],
|
||||
)
|
||||
selected: List[Repository] = [{"provider": "github.com", "account": "x", "repository": "y"}]
|
||||
selected: List[Repository] = [
|
||||
{"provider": "github.com", "account": "x", "repository": "y"}
|
||||
]
|
||||
|
||||
with patch("pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"), patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="github.com/x/y",
|
||||
with (
|
||||
patch("pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"),
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="github.com/x/y",
|
||||
),
|
||||
):
|
||||
with self.assertRaises(RuntimeError) as cm:
|
||||
open_vscode_workspace(ctx, selected)
|
||||
@@ -68,18 +75,27 @@ class TestOpenVSCodeWorkspace(unittest.TestCase):
|
||||
repositories_base_dir=os.path.join(tmp, "Repos"),
|
||||
)
|
||||
selected: List[Repository] = [
|
||||
{"provider": "github.com", "account": "kevin", "repository": "dotlinker"}
|
||||
{
|
||||
"provider": "github.com",
|
||||
"account": "kevin",
|
||||
"repository": "dotlinker",
|
||||
}
|
||||
]
|
||||
|
||||
with patch("pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"), patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="dotlinker",
|
||||
), patch(
|
||||
"pkgmgr.cli.tools.vscode.resolve_repository_path",
|
||||
return_value=repo_path,
|
||||
), patch(
|
||||
"pkgmgr.cli.tools.vscode.run_command"
|
||||
) as run_cmd:
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="dotlinker",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.resolve_repository_path",
|
||||
return_value=repo_path,
|
||||
),
|
||||
patch("pkgmgr.cli.tools.vscode.run_command") as run_cmd,
|
||||
):
|
||||
open_vscode_workspace(ctx, selected)
|
||||
|
||||
workspace_file = os.path.join(workspaces_dir, "dotlinker.code-workspace")
|
||||
@@ -110,18 +126,27 @@ class TestOpenVSCodeWorkspace(unittest.TestCase):
|
||||
all_repositories=[],
|
||||
)
|
||||
selected: List[Repository] = [
|
||||
{"provider": "github.com", "account": "kevin", "repository": "dotlinker"}
|
||||
{
|
||||
"provider": "github.com",
|
||||
"account": "kevin",
|
||||
"repository": "dotlinker",
|
||||
}
|
||||
]
|
||||
|
||||
with patch("pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"), patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="dotlinker",
|
||||
), patch(
|
||||
"pkgmgr.cli.tools.vscode.resolve_repository_path",
|
||||
return_value="/new/path",
|
||||
), patch(
|
||||
"pkgmgr.cli.tools.vscode.run_command"
|
||||
) as run_cmd:
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.shutil.which", return_value="/usr/bin/code"
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.get_repo_identifier",
|
||||
return_value="dotlinker",
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.cli.tools.vscode.resolve_repository_path",
|
||||
return_value="/new/path",
|
||||
),
|
||||
patch("pkgmgr.cli.tools.vscode.run_command") as run_cmd,
|
||||
):
|
||||
open_vscode_workspace(ctx, selected)
|
||||
|
||||
with open(workspace_file, "r", encoding="utf-8") as f:
|
||||
|
||||
@@ -69,7 +69,10 @@ class TestCreateInk(unittest.TestCase):
|
||||
"""
|
||||
mock_get_repo_identifier.return_value = "mytool"
|
||||
|
||||
with tempfile.TemporaryDirectory() as repo_dir, tempfile.TemporaryDirectory() as bin_dir:
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as repo_dir,
|
||||
tempfile.TemporaryDirectory() as bin_dir,
|
||||
):
|
||||
mock_get_repo_dir.return_value = repo_dir
|
||||
|
||||
# Create a fake executable inside the repository.
|
||||
|
||||
@@ -155,13 +155,14 @@ class TestResolveCommandForRepo(unittest.TestCase):
|
||||
If no CLI is found via PATH or Nix, resolve_command_for_repo()
|
||||
should fall back to an executable main.sh in the repo root.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir, patch(
|
||||
"pkgmgr.core.command.resolve.shutil.which", return_value=None
|
||||
), patch(
|
||||
"pkgmgr.core.command.resolve._nix_binary_candidates", return_value=[]
|
||||
), patch(
|
||||
"pkgmgr.core.command.resolve._is_executable"
|
||||
) as mock_is_executable:
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tmpdir,
|
||||
patch("pkgmgr.core.command.resolve.shutil.which", return_value=None),
|
||||
patch(
|
||||
"pkgmgr.core.command.resolve._nix_binary_candidates", return_value=[]
|
||||
),
|
||||
patch("pkgmgr.core.command.resolve._is_executable") as mock_is_executable,
|
||||
):
|
||||
main_sh = os.path.join(tmpdir, "main.sh")
|
||||
with open(main_sh, "w", encoding="utf-8") as f:
|
||||
f.write("#!/bin/sh\nexit 0\n")
|
||||
@@ -186,12 +187,13 @@ class TestResolveCommandForRepo(unittest.TestCase):
|
||||
but there is no CLI entry point or main.sh/main.py, the result
|
||||
should be None.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir, patch(
|
||||
"pkgmgr.core.command.resolve.shutil.which", return_value=None
|
||||
), patch(
|
||||
"pkgmgr.core.command.resolve._nix_binary_candidates", return_value=[]
|
||||
), patch(
|
||||
"pkgmgr.core.command.resolve._is_executable", return_value=False
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tmpdir,
|
||||
patch("pkgmgr.core.command.resolve.shutil.which", return_value=None),
|
||||
patch(
|
||||
"pkgmgr.core.command.resolve._nix_binary_candidates", return_value=[]
|
||||
),
|
||||
patch("pkgmgr.core.command.resolve._is_executable", return_value=False),
|
||||
):
|
||||
src_dir = os.path.join(tmpdir, "src", "mypkg")
|
||||
os.makedirs(src_dir, exist_ok=True)
|
||||
|
||||
@@ -12,7 +12,11 @@ class TestRunCommand(unittest.TestCase):
|
||||
popen_mock.assert_not_called()
|
||||
|
||||
def test_success_streams_and_returns_completed_process(self) -> None:
|
||||
cmd = ["python3", "-c", "print('out'); import sys; print('err', file=sys.stderr)"]
|
||||
cmd = [
|
||||
"python3",
|
||||
"-c",
|
||||
"print('out'); import sys; print('err', file=sys.stderr)",
|
||||
]
|
||||
|
||||
with patch.object(run_mod.sys, "exit") as exit_mock:
|
||||
result = run_mod.run_command(cmd, allow_failure=False)
|
||||
@@ -23,7 +27,11 @@ class TestRunCommand(unittest.TestCase):
|
||||
exit_mock.assert_not_called()
|
||||
|
||||
def test_failure_exits_when_not_allowed(self) -> None:
|
||||
cmd = ["python3", "-c", "import sys; print('oops', file=sys.stderr); sys.exit(2)"]
|
||||
cmd = [
|
||||
"python3",
|
||||
"-c",
|
||||
"import sys; print('oops', file=sys.stderr); sys.exit(2)",
|
||||
]
|
||||
|
||||
with patch.object(run_mod.sys, "exit", side_effect=SystemExit(2)) as exit_mock:
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
@@ -33,7 +41,11 @@ class TestRunCommand(unittest.TestCase):
|
||||
exit_mock.assert_called_once_with(2)
|
||||
|
||||
def test_failure_does_not_exit_when_allowed(self) -> None:
|
||||
cmd = ["python3", "-c", "import sys; print('oops', file=sys.stderr); sys.exit(3)"]
|
||||
cmd = [
|
||||
"python3",
|
||||
"-c",
|
||||
"import sys; print('oops', file=sys.stderr); sys.exit(3)",
|
||||
]
|
||||
|
||||
with patch.object(run_mod.sys, "exit") as exit_mock:
|
||||
result = run_mod.run_command(cmd, allow_failure=True)
|
||||
|
||||
@@ -9,17 +9,26 @@ 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")
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
return_value="ABCDEF1234567890\n",
|
||||
)
|
||||
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"))
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
side_effect=GitRunError("boom"),
|
||||
)
|
||||
def test_wraps_git_run_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitLatestSigningKeyQueryError):
|
||||
get_latest_signing_key(cwd="/tmp/repo")
|
||||
|
||||
@patch("pkgmgr.core.git.queries.get_latest_signing_key.run", side_effect=GitNotRepositoryError("no repo"))
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_latest_signing_key.run",
|
||||
side_effect=GitNotRepositoryError("no repo"),
|
||||
)
|
||||
def test_does_not_catch_not_repository_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitNotRepositoryError):
|
||||
get_latest_signing_key(cwd="/tmp/no-repo")
|
||||
|
||||
@@ -9,7 +9,10 @@ from pkgmgr.core.git.queries.get_remote_head_commit import (
|
||||
|
||||
|
||||
class TestGetRemoteHeadCommit(unittest.TestCase):
|
||||
@patch("pkgmgr.core.git.queries.get_remote_head_commit.run", return_value="abc123\tHEAD\n")
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_remote_head_commit.run",
|
||||
return_value="abc123\tHEAD\n",
|
||||
)
|
||||
def test_parses_first_token_as_hash(self, mock_run) -> None:
|
||||
out = get_remote_head_commit(cwd="/tmp/repo")
|
||||
self.assertEqual(out, "abc123")
|
||||
@@ -20,13 +23,19 @@ class TestGetRemoteHeadCommit(unittest.TestCase):
|
||||
out = get_remote_head_commit(cwd="/tmp/repo")
|
||||
self.assertEqual(out, "")
|
||||
|
||||
@patch("pkgmgr.core.git.queries.get_remote_head_commit.run", side_effect=GitRunError("boom"))
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_remote_head_commit.run",
|
||||
side_effect=GitRunError("boom"),
|
||||
)
|
||||
def test_wraps_git_run_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitRemoteHeadCommitQueryError) as ctx:
|
||||
get_remote_head_commit(cwd="/tmp/repo")
|
||||
self.assertIn("Failed to query remote head commit", str(ctx.exception))
|
||||
|
||||
@patch("pkgmgr.core.git.queries.get_remote_head_commit.run", side_effect=GitNotRepositoryError("no repo"))
|
||||
@patch(
|
||||
"pkgmgr.core.git.queries.get_remote_head_commit.run",
|
||||
side_effect=GitNotRepositoryError("no repo"),
|
||||
)
|
||||
def test_does_not_catch_not_repository_error(self, _mock_run) -> None:
|
||||
with self.assertRaises(GitNotRepositoryError):
|
||||
get_remote_head_commit(cwd="/tmp/no-repo")
|
||||
|
||||
@@ -26,7 +26,11 @@ class TestProbeRemoteReachable(unittest.TestCase):
|
||||
|
||||
self.assertTrue(ok)
|
||||
mock_run.assert_called_once_with(
|
||||
["ls-remote", "--exit-code", "ssh://git@code.example.org:2201/alice/repo.git"],
|
||||
[
|
||||
"ls-remote",
|
||||
"--exit-code",
|
||||
"ssh://git@code.example.org:2201/alice/repo.git",
|
||||
],
|
||||
cwd="/tmp/some-repo",
|
||||
)
|
||||
|
||||
@@ -41,7 +45,11 @@ class TestProbeRemoteReachable(unittest.TestCase):
|
||||
|
||||
self.assertFalse(ok)
|
||||
mock_run.assert_called_once_with(
|
||||
["ls-remote", "--exit-code", "ssh://git@code.example.org:2201/alice/repo.git"],
|
||||
[
|
||||
"ls-remote",
|
||||
"--exit-code",
|
||||
"ssh://git@code.example.org:2201/alice/repo.git",
|
||||
],
|
||||
cwd="/tmp/some-repo",
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ from pkgmgr.core.git.run import run
|
||||
|
||||
class TestGitRun(unittest.TestCase):
|
||||
def test_preview_mode_prints_and_does_not_execute(self) -> None:
|
||||
with patch("pkgmgr.core.git.run.subprocess.run") as mock_run, patch(
|
||||
"builtins.print"
|
||||
) as mock_print:
|
||||
with (
|
||||
patch("pkgmgr.core.git.run.subprocess.run") as mock_run,
|
||||
patch("builtins.print") as mock_print,
|
||||
):
|
||||
out = run(["status"], cwd="/tmp/repo", preview=True)
|
||||
|
||||
self.assertEqual(out, "")
|
||||
@@ -24,7 +25,9 @@ class TestGitRun(unittest.TestCase):
|
||||
completed.stderr = ""
|
||||
completed.returncode = 0
|
||||
|
||||
with patch("pkgmgr.core.git.run.subprocess.run", return_value=completed) as mock_run:
|
||||
with patch(
|
||||
"pkgmgr.core.git.run.subprocess.run", return_value=completed
|
||||
) as mock_run:
|
||||
out = run(["rev-parse", "HEAD"], cwd="/repo", preview=False)
|
||||
|
||||
self.assertEqual(out, "hello world")
|
||||
|
||||
@@ -7,7 +7,10 @@ from pkgmgr.core.repository.dir import get_repo_dir
|
||||
class TestGetRepoDir(unittest.TestCase):
|
||||
def test_builds_path_with_expanded_base_dir(self):
|
||||
repo = {"provider": "github.com", "account": "alice", "repository": "demo"}
|
||||
with patch("pkgmgr.core.repository.dir.os.path.expanduser", return_value="/home/u/repos"):
|
||||
with patch(
|
||||
"pkgmgr.core.repository.dir.os.path.expanduser",
|
||||
return_value="/home/u/repos",
|
||||
):
|
||||
result = get_repo_dir("~/repos", repo)
|
||||
|
||||
self.assertEqual(result, "/home/u/repos/github.com/alice/demo")
|
||||
|
||||
@@ -9,8 +9,18 @@ from pkgmgr.core.repository.ignored import filter_ignored
|
||||
class TestFilterIgnored(unittest.TestCase):
|
||||
def test_filter_ignored_removes_repos_with_ignore_true(self) -> None:
|
||||
repos = [
|
||||
{"provider": "github.com", "account": "user", "repository": "a", "ignore": True},
|
||||
{"provider": "github.com", "account": "user", "repository": "b", "ignore": False},
|
||||
{
|
||||
"provider": "github.com",
|
||||
"account": "user",
|
||||
"repository": "a",
|
||||
"ignore": True,
|
||||
},
|
||||
{
|
||||
"provider": "github.com",
|
||||
"account": "user",
|
||||
"repository": "b",
|
||||
"ignore": False,
|
||||
},
|
||||
{"provider": "github.com", "account": "user", "repository": "c"},
|
||||
]
|
||||
|
||||
|
||||
@@ -96,7 +96,9 @@ class TestGetSelectedRepos(unittest.TestCase):
|
||||
selected = get_selected_repos(args, self.all_repos)
|
||||
|
||||
# Beide Repos sollten erscheinen, weil include_ignored=True
|
||||
self.assertEqual({r["repository"] for r in selected}, {"ignored-repo", "visible-repo"})
|
||||
self.assertEqual(
|
||||
{r["repository"] for r in selected}, {"ignored-repo", "visible-repo"}
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 3) --all Modus – ignorierte Repos werden per Default entfernt
|
||||
|
||||
@@ -10,9 +10,14 @@ from pkgmgr.core.repository.verify import verify_repository
|
||||
class TestVerifyRepository(unittest.TestCase):
|
||||
def test_no_verified_info_returns_ok_and_best_effort_values(self) -> None:
|
||||
repo = {"id": "demo"} # no "verified"
|
||||
with patch("pkgmgr.core.repository.verify.get_head_commit", return_value="deadbeef"), patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="KEYID",
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_head_commit", return_value="deadbeef"
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="KEYID",
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
|
||||
self.assertTrue(ok)
|
||||
@@ -22,12 +27,15 @@ class TestVerifyRepository(unittest.TestCase):
|
||||
|
||||
def test_best_effort_swallows_query_errors_when_no_verified_info(self) -> None:
|
||||
repo = {"id": "demo"}
|
||||
with patch(
|
||||
"pkgmgr.core.repository.verify.get_head_commit",
|
||||
return_value=None,
|
||||
), patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
side_effect=GitLatestSigningKeyQueryError("fail signing key"),
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_head_commit",
|
||||
return_value=None,
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
side_effect=GitLatestSigningKeyQueryError("fail signing key"),
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
|
||||
self.assertTrue(ok)
|
||||
@@ -37,9 +45,14 @@ class TestVerifyRepository(unittest.TestCase):
|
||||
|
||||
def test_verified_commit_mismatch_fails(self) -> None:
|
||||
repo = {"verified": {"commit": "expected", "gpg_keys": None}}
|
||||
with patch("pkgmgr.core.repository.verify.get_head_commit", return_value="actual"), patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="",
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_head_commit", return_value="actual"
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="",
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
|
||||
|
||||
@@ -50,9 +63,12 @@ class TestVerifyRepository(unittest.TestCase):
|
||||
|
||||
def test_verified_gpg_key_missing_fails(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",
|
||||
return_value="",
|
||||
with (
|
||||
patch("pkgmgr.core.repository.verify.get_head_commit", return_value=""),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="",
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="local")
|
||||
|
||||
@@ -63,12 +79,15 @@ class TestVerifyRepository(unittest.TestCase):
|
||||
|
||||
def test_strict_pull_collects_remote_error_message(self) -> None:
|
||||
repo = {"verified": {"commit": "expected", "gpg_keys": None}}
|
||||
with patch(
|
||||
"pkgmgr.core.repository.verify.get_remote_head_commit",
|
||||
side_effect=GitRemoteHeadCommitQueryError("remote fail"),
|
||||
), patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="",
|
||||
with (
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_remote_head_commit",
|
||||
side_effect=GitRemoteHeadCommitQueryError("remote fail"),
|
||||
),
|
||||
patch(
|
||||
"pkgmgr.core.repository.verify.get_latest_signing_key",
|
||||
return_value="",
|
||||
),
|
||||
):
|
||||
ok, errors, commit, key = verify_repository(repo, "/tmp/repo", mode="pull")
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ class TestCreateInk(unittest.TestCase):
|
||||
mock_get_repo_identifier.return_value = "test-id"
|
||||
mock_get_repo_dir.return_value = "/repos/test-id"
|
||||
|
||||
with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod:
|
||||
with (
|
||||
patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs,
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink,
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod,
|
||||
):
|
||||
create_ink_module.create_ink(
|
||||
repo=repo,
|
||||
repositories_base_dir="/repos",
|
||||
@@ -45,9 +47,11 @@ class TestCreateInk(unittest.TestCase):
|
||||
mock_get_repo_identifier.return_value = "test-id"
|
||||
mock_get_repo_dir.return_value = "/repos/test-id"
|
||||
|
||||
with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod:
|
||||
with (
|
||||
patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs,
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink,
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod,
|
||||
):
|
||||
create_ink_module.create_ink(
|
||||
repo=repo,
|
||||
repositories_base_dir="/repos",
|
||||
@@ -74,12 +78,14 @@ class TestCreateInk(unittest.TestCase):
|
||||
mock_get_repo_identifier.return_value = "test-id"
|
||||
mock_get_repo_dir.return_value = "/repos/test-id"
|
||||
|
||||
with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod, \
|
||||
patch("pkgmgr.core.command.ink.os.path.exists", return_value=False), \
|
||||
patch("pkgmgr.core.command.ink.os.path.islink", return_value=False), \
|
||||
patch("pkgmgr.core.command.ink.os.path.realpath", side_effect=lambda p: p):
|
||||
with (
|
||||
patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs,
|
||||
patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink,
|
||||
patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod,
|
||||
patch("pkgmgr.core.command.ink.os.path.exists", return_value=False),
|
||||
patch("pkgmgr.core.command.ink.os.path.islink", return_value=False),
|
||||
patch("pkgmgr.core.command.ink.os.path.realpath", side_effect=lambda p: p),
|
||||
):
|
||||
create_ink_module.create_ink(
|
||||
repo=repo,
|
||||
repositories_base_dir="/repos",
|
||||
@@ -93,5 +99,6 @@ class TestCreateInk(unittest.TestCase):
|
||||
mock_makedirs.assert_called_once()
|
||||
mock_chmod.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user