Refactor: Restructure pkgmgr into actions/, core/, and cli/ (full module breakup)

This commit introduces a large-scale structural refactor of the pkgmgr
codebase. All functionality has been moved from the previous flat
top-level layout into three clearly separated namespaces:

  • pkgmgr.actions      – high-level operations invoked by the CLI
  • pkgmgr.core         – pure logic, helpers, repository utilities,
                          versioning, git helpers, config IO, and
                          command resolution
  • pkgmgr.cli          – parser, dispatch, context, and command
                          handlers

Key improvements:
  - Moved all “branch”, “release”, “changelog”, repo-management
    actions, installer pipelines, and proxy execution logic into
    pkgmgr.actions.<domain>.
  - Reworked installer structure under
        pkgmgr.actions.repository.install.installers
    including OS-package installers, Nix, Python, and Makefile.
  - Consolidated all low-level functionality under pkgmgr.core:
        • git helpers → core/git
        • config load/save → core/config
        • repository helpers → core/repository
        • versioning & semver → core/version
        • command helpers (alias, resolve, run, ink) → core/command
  - Replaced pkgmgr.cli_core with pkgmgr.cli and updated all imports.
  - Added minimal __init__.py files for clean package exposure.
  - Updated all E2E, integration, and unit tests with new module paths.
  - Fixed patch targets so mocks point to the new structure.
  - Ensured backward compatibility at the CLI boundary (pkgmgr entry point unchanged).

This refactor produces a cleaner, layered architecture:
  - `core` = logic
  - `actions` = orchestrated behaviour
  - `cli` = user interface

Reference: ChatGPT-assisted refactor discussion
https://chatgpt.com/share/6938221c-e24c-800f-8317-7732cedf39b9
This commit is contained in:
Kevin Veen-Birkenbach
2025-12-09 14:20:19 +01:00
parent 59d0355b91
commit d50891dfe5
92 changed files with 381 additions and 388 deletions

View File

@@ -2,12 +2,12 @@
# -*- coding: utf-8 -*-
"""
Unit tests for pkgmgr.cli_core.commands.release.
Unit tests for pkgmgr.cli.commands.release.
These tests focus on the wiring layer:
- Argument handling for the release command as defined by the
top-level parser (cli_core.parser.create_parser).
- Correct invocation of pkgmgr.release.release(...) for the
- Correct invocation of pkgmgr.actions.release.release(...) for the
selected repositories.
- Behaviour of --preview, --list, --close, and -f/--force.
"""
@@ -46,19 +46,19 @@ class TestReleaseCommand(unittest.TestCase):
Build a real top-level parser and parse the given argv list
to obtain the Namespace for the `release` command.
"""
from pkgmgr.cli_core.parser import create_parser
from pkgmgr.cli.parser import create_parser
parser = create_parser("test parser")
args = parser.parse_args(argv)
self.assertEqual(args.command, "release")
return args
@patch("pkgmgr.cli_core.commands.release.os.path.isdir", return_value=True)
@patch("pkgmgr.cli_core.commands.release.run_release")
@patch("pkgmgr.cli_core.commands.release.get_repo_dir")
@patch("pkgmgr.cli_core.commands.release.get_repo_identifier")
@patch("pkgmgr.cli_core.commands.release.os.chdir")
@patch("pkgmgr.cli_core.commands.release.os.getcwd", return_value="/cwd")
@patch("pkgmgr.cli.commands.release.os.path.isdir", return_value=True)
@patch("pkgmgr.cli.commands.release.run_release")
@patch("pkgmgr.cli.commands.release.get_repo_dir")
@patch("pkgmgr.cli.commands.release.get_repo_identifier")
@patch("pkgmgr.cli.commands.release.os.chdir")
@patch("pkgmgr.cli.commands.release.os.getcwd", return_value="/cwd")
def test_release_with_close_and_message(
self,
mock_getcwd,
@@ -69,7 +69,7 @@ class TestReleaseCommand(unittest.TestCase):
mock_isdir,
) -> None:
"""
The release handler should call pkgmgr.release.release() with:
The release handler should call pkgmgr.actions.release.release() with:
- release_type (e.g. minor)
- provided message
- preview flag
@@ -78,7 +78,7 @@ class TestReleaseCommand(unittest.TestCase):
It must change into the repository directory and then back.
"""
from pkgmgr.cli_core.commands.release import handle_release
from pkgmgr.cli.commands.release import handle_release
repo = {"name": "dummy-repo"}
selected = [repo]
@@ -116,12 +116,12 @@ class TestReleaseCommand(unittest.TestCase):
close=True,
)
@patch("pkgmgr.cli_core.commands.release.os.path.isdir", return_value=True)
@patch("pkgmgr.cli_core.commands.release.run_release")
@patch("pkgmgr.cli_core.commands.release.get_repo_dir")
@patch("pkgmgr.cli_core.commands.release.get_repo_identifier")
@patch("pkgmgr.cli_core.commands.release.os.chdir")
@patch("pkgmgr.cli_core.commands.release.os.getcwd", return_value="/cwd")
@patch("pkgmgr.cli.commands.release.os.path.isdir", return_value=True)
@patch("pkgmgr.cli.commands.release.run_release")
@patch("pkgmgr.cli.commands.release.get_repo_dir")
@patch("pkgmgr.cli.commands.release.get_repo_identifier")
@patch("pkgmgr.cli.commands.release.os.chdir")
@patch("pkgmgr.cli.commands.release.os.getcwd", return_value="/cwd")
def test_release_preview_mode(
self,
mock_getcwd,
@@ -135,7 +135,7 @@ class TestReleaseCommand(unittest.TestCase):
In preview mode, the handler should pass preview=True to the
release helper and force=False by default.
"""
from pkgmgr.cli_core.commands.release import handle_release
from pkgmgr.cli.commands.release import handle_release
repo = {"name": "dummy-repo"}
selected = [repo]
@@ -164,9 +164,9 @@ class TestReleaseCommand(unittest.TestCase):
close=False,
)
@patch("pkgmgr.cli_core.commands.release.run_release")
@patch("pkgmgr.cli_core.commands.release.get_repo_dir")
@patch("pkgmgr.cli_core.commands.release.get_repo_identifier")
@patch("pkgmgr.cli.commands.release.run_release")
@patch("pkgmgr.cli.commands.release.get_repo_dir")
@patch("pkgmgr.cli.commands.release.get_repo_identifier")
def test_release_list_mode_does_not_invoke_helper(
self,
mock_get_repo_identifier,
@@ -177,7 +177,7 @@ class TestReleaseCommand(unittest.TestCase):
When --list is provided, the handler should print the list of affected
repositories and must NOT invoke run_release().
"""
from pkgmgr.cli_core.commands.release import handle_release
from pkgmgr.cli.commands.release import handle_release
repo1 = {"name": "repo-1"}
repo2 = {"name": "repo-2"}

View File

@@ -16,8 +16,8 @@ from __future__ import annotations
import unittest
from unittest.mock import patch
from pkgmgr.cli_core.parser import create_parser
from pkgmgr.cli_core.commands.branch import handle_branch
from pkgmgr.cli.parser import create_parser
from pkgmgr.cli.commands.branch import handle_branch
class TestBranchCLI(unittest.TestCase):
@@ -31,7 +31,7 @@ class TestBranchCLI(unittest.TestCase):
"""
return create_parser("pkgmgr test parser")
@patch("pkgmgr.cli_core.commands.branch.open_branch")
@patch("pkgmgr.cli.commands.branch.open_branch")
def test_branch_open_with_name_and_base(self, mock_open_branch):
"""
Ensure that `pkgmgr branch open <name> --base <branch>` calls
@@ -58,7 +58,7 @@ class TestBranchCLI(unittest.TestCase):
self.assertEqual(kwargs.get("base_branch"), "develop")
self.assertEqual(kwargs.get("cwd"), ".")
@patch("pkgmgr.cli_core.commands.branch.close_branch")
@patch("pkgmgr.cli.commands.branch.close_branch")
def test_branch_close_with_name_and_base(self, mock_close_branch):
"""
Ensure that `pkgmgr branch close <name> --base <branch>` calls
@@ -84,7 +84,7 @@ class TestBranchCLI(unittest.TestCase):
self.assertEqual(kwargs.get("base_branch"), "main")
self.assertEqual(kwargs.get("cwd"), ".")
@patch("pkgmgr.cli_core.commands.branch.close_branch")
@patch("pkgmgr.cli.commands.branch.close_branch")
def test_branch_close_without_name_uses_none(self, mock_close_branch):
"""
Ensure that `pkgmgr branch close` without a name passes name=None

View File

@@ -4,8 +4,8 @@ import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller
class TestArchPkgbuildInstaller(unittest.TestCase):
@@ -26,7 +26,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
)
self.installer = ArchPkgbuildInstaller()
@patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
@patch("pkgmgr.actions.repository.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 +46,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
self.assertIn("makepkg", calls)
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "PKGBUILD"))
@patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=0)
@patch("pkgmgr.actions.repository.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 +55,7 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
mock_which.return_value = "/usr/bin/pacman"
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
@patch("pkgmgr.actions.repository.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(
@@ -64,8 +64,8 @@ class TestArchPkgbuildInstaller(unittest.TestCase):
mock_which.return_value = "/usr/bin/pacman"
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.os_packages.arch_pkgbuild.run_command")
@patch("pkgmgr.installers.os_packages.arch_pkgbuild.os.geteuid", return_value=1000)
@patch("pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild.run_command")
@patch("pkgmgr.actions.repository.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(

View File

@@ -4,8 +4,8 @@ import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.os_packages.debian_control import DebianControlInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller
class TestDebianControlInstaller(unittest.TestCase):
@@ -36,7 +36,7 @@ class TestDebianControlInstaller(unittest.TestCase):
def test_supports_false_without_dpkg_buildpackage(self, mock_which, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.os_packages.debian_control.run_command")
@patch("pkgmgr.actions.repository.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")

View File

@@ -3,8 +3,8 @@
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.os_packages.rpm_spec import RpmSpecInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import RpmSpecInstaller
class TestRpmSpecInstaller(unittest.TestCase):
@@ -45,7 +45,7 @@ class TestRpmSpecInstaller(unittest.TestCase):
mock_which.return_value = "/usr/bin/rpmbuild"
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.os_packages.rpm_spec.run_command")
@patch("pkgmgr.actions.repository.install.installers.os_packages.rpm_spec.run_command")
@patch("glob.glob")
@patch("shutil.which")
def test_run_builds_and_installs_rpms(

View File

@@ -1,8 +1,8 @@
# tests/unit/pkgmgr/installers/test_base.py
import unittest
from pkgmgr.installers.base import BaseInstaller
from pkgmgr.context import RepoContext
from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.actions.repository.install.context import RepoContext
class DummyInstaller(BaseInstaller):

View File

@@ -4,8 +4,8 @@ import os
import unittest
from unittest.mock import patch, mock_open
from pkgmgr.context import RepoContext
from pkgmgr.installers.makefile import MakefileInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller
class TestMakefileInstaller(unittest.TestCase):
@@ -35,7 +35,7 @@ class TestMakefileInstaller(unittest.TestCase):
def test_supports_false_when_makefile_missing(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.makefile.run_command")
@patch("pkgmgr.actions.repository.install.installers.makefile.run_command")
@patch(
"builtins.open",
new_callable=mock_open,
@@ -62,7 +62,7 @@ class TestMakefileInstaller(unittest.TestCase):
self.ctx.repo_dir,
)
@patch("pkgmgr.installers.makefile.run_command")
@patch("pkgmgr.actions.repository.install.installers.makefile.run_command")
@patch(
"builtins.open",
new_callable=mock_open,

View File

@@ -3,8 +3,8 @@ import unittest
from unittest import mock
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.nix_flake import NixFlakeInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller
class TestNixFlakeInstaller(unittest.TestCase):
@@ -39,7 +39,7 @@ class TestNixFlakeInstaller(unittest.TestCase):
@patch("os.path.exists", return_value=True)
@patch("shutil.which", return_value="/usr/bin/nix")
@mock.patch("pkgmgr.installers.nix_flake.run_command")
@mock.patch("pkgmgr.actions.repository.install.installers.nix_flake.run_command")
def test_run_removes_old_profile_and_installs_outputs(
self,
mock_run_command,
@@ -74,7 +74,7 @@ class TestNixFlakeInstaller(unittest.TestCase):
self.assertEqual(cmds[0], remove_cmd)
@patch("shutil.which", return_value="/usr/bin/nix")
@mock.patch("pkgmgr.installers.nix_flake.run_command")
@mock.patch("pkgmgr.actions.repository.install.installers.nix_flake.run_command")
def test_ensure_old_profile_removed_ignores_systemexit(
self,
mock_run_command,

View File

@@ -4,8 +4,8 @@ import os
import unittest
from unittest.mock import patch
from pkgmgr.context import RepoContext
from pkgmgr.installers.python import PythonInstaller
from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.actions.repository.install.installers.python import PythonInstaller
class TestPythonInstaller(unittest.TestCase):
@@ -34,7 +34,7 @@ class TestPythonInstaller(unittest.TestCase):
def test_supports_false_when_no_pyproject(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx))
@patch("pkgmgr.installers.python.run_command")
@patch("pkgmgr.actions.repository.install.installers.python.run_command")
@patch("os.path.exists", side_effect=lambda path: path.endswith("pyproject.toml"))
def test_run_installs_project_from_pyproject(self, mock_exists, mock_run_command):
self.installer.run(self.ctx)

View File

@@ -4,12 +4,12 @@ import unittest
from types import SimpleNamespace
from unittest.mock import patch
from pkgmgr.branch_commands import open_branch
from pkgmgr.git_utils import GitError
from pkgmgr.actions.branch import open_branch
from pkgmgr.core.git import GitError
class TestOpenBranch(unittest.TestCase):
@patch("pkgmgr.branch_commands.run_git")
@patch("pkgmgr.actions.branch.run_git")
def test_open_branch_with_explicit_name_and_default_base(self, mock_run_git) -> None:
"""
open_branch(name, base='main') should:
@@ -42,7 +42,7 @@ class TestOpenBranch(unittest.TestCase):
self.assertEqual(kwargs.get("cwd"), cwd_expected)
@patch("builtins.input", return_value="feature/interactive")
@patch("pkgmgr.branch_commands.run_git")
@patch("pkgmgr.actions.branch.run_git")
def test_open_branch_prompts_for_name_if_missing(
self,
mock_run_git,
@@ -75,7 +75,7 @@ class TestOpenBranch(unittest.TestCase):
self.assertEqual(args[0], args_expected)
self.assertEqual(kwargs.get("cwd"), cwd_expected)
@patch("pkgmgr.branch_commands.run_git")
@patch("pkgmgr.actions.branch.run_git")
def test_open_branch_raises_runtimeerror_on_git_failure(self, mock_run_git) -> None:
"""
If a GitError occurs (e.g. fetch fails), open_branch should

View File

@@ -4,7 +4,7 @@ import os
import unittest
from unittest.mock import patch, mock_open
from pkgmgr.capabilities import (
from pkgmgr.actions.repository.install.capabilities import (
PythonRuntimeCapability,
MakeInstallCapability,
NixFlakeCapability,
@@ -31,7 +31,7 @@ class TestCapabilitiesDetectors(unittest.TestCase):
def setUp(self):
self.ctx = DummyCtx("/tmp/repo")
@patch("pkgmgr.capabilities.os.path.exists")
@patch("pkgmgr.actions.repository.install.capabilities.os.path.exists")
def test_python_runtime_python_layer_pyproject(self, mock_exists):
"""PythonRuntimeCapability: python layer is provided if pyproject.toml exists."""
cap = PythonRuntimeCapability()
@@ -47,8 +47,8 @@ class TestCapabilitiesDetectors(unittest.TestCase):
self.assertFalse(cap.is_provided(self.ctx, "nix"))
self.assertFalse(cap.is_provided(self.ctx, "os-packages"))
@patch("pkgmgr.capabilities._read_text_if_exists")
@patch("pkgmgr.capabilities.os.path.exists")
@patch("pkgmgr.actions.repository.install.capabilities._read_text_if_exists")
@patch("pkgmgr.actions.repository.install.capabilities.os.path.exists")
def test_python_runtime_nix_layer_flake(self, mock_exists, mock_read):
"""
PythonRuntimeCapability: nix layer is provided if flake.nix contains
@@ -65,7 +65,7 @@ class TestCapabilitiesDetectors(unittest.TestCase):
self.assertTrue(cap.applies_to_layer("nix"))
self.assertTrue(cap.is_provided(self.ctx, "nix"))
@patch("pkgmgr.capabilities.os.path.exists", return_value=True)
@patch("pkgmgr.actions.repository.install.capabilities.os.path.exists", return_value=True)
@patch(
"builtins.open",
new_callable=mock_open,
@@ -78,7 +78,7 @@ class TestCapabilitiesDetectors(unittest.TestCase):
self.assertTrue(cap.applies_to_layer("makefile"))
self.assertTrue(cap.is_provided(self.ctx, "makefile"))
@patch("pkgmgr.capabilities.os.path.exists")
@patch("pkgmgr.actions.repository.install.capabilities.os.path.exists")
def test_nix_flake_capability_on_nix_layer(self, mock_exists):
"""NixFlakeCapability: nix layer is provided if flake.nix exists."""
cap = NixFlakeCapability()
@@ -153,7 +153,7 @@ class TestDetectCapabilities(unittest.TestCase):
},
)
with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]):
with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [dummy1, dummy2]):
caps = detect_capabilities(self.ctx, layers)
self.assertEqual(
@@ -221,7 +221,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
)
with patch(
"pkgmgr.capabilities.CAPABILITY_MATCHERS",
"pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS",
[cap_make_install, cap_python_runtime, cap_nix_flake],
):
effective = resolve_effective_capabilities(self.ctx, layers)
@@ -258,7 +258,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
)
with patch(
"pkgmgr.capabilities.CAPABILITY_MATCHERS",
"pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS",
[cap_python_runtime],
):
effective = resolve_effective_capabilities(self.ctx, layers)
@@ -283,7 +283,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
},
)
with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [cap_only_make]):
with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_only_make]):
effective = resolve_effective_capabilities(self.ctx, layers)
self.assertEqual(effective["makefile"], {"make-install"})
@@ -306,7 +306,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
},
)
with patch("pkgmgr.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]):
with patch("pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS", [cap_only_nix]):
effective = resolve_effective_capabilities(self.ctx, layers)
self.assertEqual(effective["makefile"], set())
@@ -337,7 +337,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
)
with patch(
"pkgmgr.capabilities.CAPABILITY_MATCHERS",
"pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS",
[cap_python_runtime],
):
effective = resolve_effective_capabilities(self.ctx, layers)
@@ -359,7 +359,7 @@ class TestResolveEffectiveCapabilities(unittest.TestCase):
)
with patch(
"pkgmgr.capabilities.CAPABILITY_MATCHERS",
"pkgmgr.actions.repository.install.capabilities.CAPABILITY_MATCHERS",
[cap_dummy],
):
effective = resolve_effective_capabilities(self.ctx)

View File

@@ -3,13 +3,12 @@ from __future__ import annotations
import unittest
from unittest.mock import patch
from pkgmgr.changelog import generate_changelog
from pkgmgr.git_utils import GitError
from pkgmgr.cli_core.commands.changelog import _find_previous_and_current_tag
from pkgmgr.actions.changelog import generate_changelog
from pkgmgr.core.git import GitError
from pkgmgr.cli.commands.changelog import _find_previous_and_current_tag
class TestGenerateChangelog(unittest.TestCase):
@patch("pkgmgr.changelog.run_git")
@patch("pkgmgr.actions.changelog.run_git")
def test_generate_changelog_default_range_no_merges(self, mock_run_git) -> None:
"""
Default behaviour:
@@ -35,7 +34,7 @@ class TestGenerateChangelog(unittest.TestCase):
self.assertIn("HEAD", args[0])
self.assertEqual(kwargs.get("cwd"), "/repo")
@patch("pkgmgr.changelog.run_git")
@patch("pkgmgr.actions.changelog.run_git")
def test_generate_changelog_with_range_and_merges(self, mock_run_git) -> None:
"""
Explicit range and include_merges=True:
@@ -64,7 +63,7 @@ class TestGenerateChangelog(unittest.TestCase):
self.assertIn("v1.0.0..v1.1.0", cmd)
self.assertEqual(kwargs.get("cwd"), "/repo")
@patch("pkgmgr.changelog.run_git")
@patch("pkgmgr.actions.changelog.run_git")
def test_generate_changelog_giterror_returns_error_message(self, mock_run_git) -> None:
"""
If Git fails, we do NOT raise; instead we return a human readable error string.
@@ -77,7 +76,7 @@ class TestGenerateChangelog(unittest.TestCase):
self.assertIn("simulated git failure", result)
self.assertIn("v0.1.0..v0.2.0", result)
@patch("pkgmgr.changelog.run_git")
@patch("pkgmgr.actions.changelog.run_git")
def test_generate_changelog_empty_output_returns_info(self, mock_run_git) -> None:
"""
Empty git log output -> informational message instead of empty string.

View File

@@ -83,7 +83,7 @@ class TestCliVersion(unittest.TestCase):
# This matches the new behaviour: without explicit identifiers,
# version uses _select_repo_for_current_directory(ctx).
self._patch_select_repo_for_current_directory = mock.patch(
"pkgmgr.cli_core.dispatch._select_repo_for_current_directory",
"pkgmgr.cli.dispatch._select_repo_for_current_directory",
return_value=[self._fake_repo],
)
self.mock_select_repo_for_current_directory = (
@@ -166,7 +166,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: mock git tags used by handle_version
with mock.patch(
"pkgmgr.cli_core.commands.version.get_tags",
"pkgmgr.cli.commands.version.get_tags",
return_value=["v1.2.0", "v1.2.3", "v1.0.0"],
):
# Act
@@ -198,7 +198,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: mock git tags (latest is 1.2.3)
with mock.patch(
"pkgmgr.cli_core.commands.version.get_tags",
"pkgmgr.cli.commands.version.get_tags",
return_value=["v1.2.3"],
):
stdout = self._run_cli_version_and_capture()
@@ -226,7 +226,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: no tags returned
with mock.patch(
"pkgmgr.cli_core.commands.version.get_tags",
"pkgmgr.cli.commands.version.get_tags",
return_value=[],
):
stdout = self._run_cli_version_and_capture()

View File

@@ -4,8 +4,8 @@ import unittest
from types import SimpleNamespace
from unittest.mock import patch
from pkgmgr.cli_core.commands.branch import handle_branch
from pkgmgr.cli_core.context import CLIContext
from pkgmgr.cli.commands.branch import handle_branch
from pkgmgr.cli.context import CLIContext
class TestCliBranch(unittest.TestCase):
@@ -22,7 +22,7 @@ class TestCliBranch(unittest.TestCase):
user_config_path="/tmp/config.yaml",
)
@patch("pkgmgr.cli_core.commands.branch.open_branch")
@patch("pkgmgr.cli.commands.branch.open_branch")
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='.'.
@@ -44,7 +44,7 @@ class TestCliBranch(unittest.TestCase):
self.assertEqual(call_kwargs.get("base_branch"), "develop")
self.assertEqual(call_kwargs.get("cwd"), ".")
@patch("pkgmgr.cli_core.commands.branch.open_branch")
@patch("pkgmgr.cli.commands.branch.open_branch")
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),
@@ -70,7 +70,7 @@ class TestCliBranch(unittest.TestCase):
# close subcommand
# ------------------------------------------------------------------
@patch("pkgmgr.cli_core.commands.branch.close_branch")
@patch("pkgmgr.cli.commands.branch.close_branch")
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 and cwd='.'.
@@ -92,7 +92,7 @@ class TestCliBranch(unittest.TestCase):
self.assertEqual(call_kwargs.get("base_branch"), "develop")
self.assertEqual(call_kwargs.get("cwd"), ".")
@patch("pkgmgr.cli_core.commands.branch.close_branch")
@patch("pkgmgr.cli.commands.branch.close_branch")
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'

View File

@@ -2,7 +2,7 @@
import unittest
from unittest.mock import patch, MagicMock
from pkgmgr.clone_repos import clone_repos
from pkgmgr.actions.repository.clone import clone_repos
class TestCloneRepos(unittest.TestCase):
@@ -16,12 +16,12 @@ class TestCloneRepos(unittest.TestCase):
self.base_dir = "/tmp/repos"
self.all_repos = self.selected
@patch("pkgmgr.clone_repos.verify_repository")
@patch("pkgmgr.clone_repos.subprocess.run")
@patch("pkgmgr.clone_repos.os.makedirs")
@patch("pkgmgr.clone_repos.os.path.exists")
@patch("pkgmgr.clone_repos.get_repo_dir")
@patch("pkgmgr.clone_repos.get_repo_identifier")
@patch("pkgmgr.actions.repository.clone.verify_repository")
@patch("pkgmgr.actions.repository.clone.subprocess.run")
@patch("pkgmgr.actions.repository.clone.os.makedirs")
@patch("pkgmgr.actions.repository.clone.os.path.exists")
@patch("pkgmgr.actions.repository.clone.get_repo_dir")
@patch("pkgmgr.actions.repository.clone.get_repo_identifier")
def test_clone_ssh_mode_uses_ssh_url(
self,
mock_get_repo_identifier,
@@ -55,12 +55,12 @@ class TestCloneRepos(unittest.TestCase):
self.assertIn("git@github.com:user/repo.git", cmd)
self.assertEqual(cwd, "/tmp/repos/user")
@patch("pkgmgr.clone_repos.verify_repository")
@patch("pkgmgr.clone_repos.subprocess.run")
@patch("pkgmgr.clone_repos.os.makedirs")
@patch("pkgmgr.clone_repos.os.path.exists")
@patch("pkgmgr.clone_repos.get_repo_dir")
@patch("pkgmgr.clone_repos.get_repo_identifier")
@patch("pkgmgr.actions.repository.clone.verify_repository")
@patch("pkgmgr.actions.repository.clone.subprocess.run")
@patch("pkgmgr.actions.repository.clone.os.makedirs")
@patch("pkgmgr.actions.repository.clone.os.path.exists")
@patch("pkgmgr.actions.repository.clone.get_repo_dir")
@patch("pkgmgr.actions.repository.clone.get_repo_identifier")
def test_clone_https_mode_uses_https_url(
self,
mock_get_repo_identifier,
@@ -93,12 +93,12 @@ class TestCloneRepos(unittest.TestCase):
self.assertIn("https://github.com/user/repo.git", cmd)
self.assertEqual(cwd, "/tmp/repos/user")
@patch("pkgmgr.clone_repos.verify_repository")
@patch("pkgmgr.clone_repos.subprocess.run")
@patch("pkgmgr.clone_repos.os.makedirs")
@patch("pkgmgr.clone_repos.os.path.exists")
@patch("pkgmgr.clone_repos.get_repo_dir")
@patch("pkgmgr.clone_repos.get_repo_identifier")
@patch("pkgmgr.actions.repository.clone.verify_repository")
@patch("pkgmgr.actions.repository.clone.subprocess.run")
@patch("pkgmgr.actions.repository.clone.os.makedirs")
@patch("pkgmgr.actions.repository.clone.os.path.exists")
@patch("pkgmgr.actions.repository.clone.get_repo_dir")
@patch("pkgmgr.actions.repository.clone.get_repo_identifier")
def test_clone_shallow_mode_uses_https_with_depth(
self,
mock_get_repo_identifier,
@@ -131,12 +131,12 @@ class TestCloneRepos(unittest.TestCase):
self.assertIn("https://github.com/user/repo.git", cmd)
self.assertEqual(cwd, "/tmp/repos/user")
@patch("pkgmgr.clone_repos.verify_repository")
@patch("pkgmgr.clone_repos.subprocess.run")
@patch("pkgmgr.clone_repos.os.makedirs")
@patch("pkgmgr.clone_repos.os.path.exists")
@patch("pkgmgr.clone_repos.get_repo_dir")
@patch("pkgmgr.clone_repos.get_repo_identifier")
@patch("pkgmgr.actions.repository.clone.verify_repository")
@patch("pkgmgr.actions.repository.clone.subprocess.run")
@patch("pkgmgr.actions.repository.clone.os.makedirs")
@patch("pkgmgr.actions.repository.clone.os.path.exists")
@patch("pkgmgr.actions.repository.clone.get_repo_dir")
@patch("pkgmgr.actions.repository.clone.get_repo_identifier")
def test_preview_mode_does_not_call_subprocess_run(
self,
mock_get_repo_identifier,

View File

@@ -1,5 +1,5 @@
import unittest
from pkgmgr.context import RepoContext
from pkgmgr.actions.repository.install.context import RepoContext
class TestRepoContext(unittest.TestCase):

View File

@@ -1,32 +1,32 @@
# tests/unit/pkgmgr/test_create_ink.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from unittest.mock import patch
import pkgmgr.create_ink as create_ink_module
import pkgmgr.core.command.ink as create_ink_module
class TestCreateInk(unittest.TestCase):
@patch("pkgmgr.create_ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier")
@patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_skips_when_no_command(
self,
mock_get_repo_identifier,
mock_get_repo_dir,
):
repo = {} # no 'command' key
repo = {"name": "test-repo", "command": None}
mock_get_repo_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_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",
bin_dir="/bin",
all_repos=[repo],
quiet=True,
preview=False,
)
@@ -34,26 +34,25 @@ class TestCreateInk(unittest.TestCase):
mock_symlink.assert_not_called()
mock_chmod.assert_not_called()
@patch("pkgmgr.create_ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier")
@patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_preview_only(
self,
mock_get_repo_identifier,
mock_get_repo_dir,
):
repo = {"command": "/repos/test-id/main.py"}
repo = {"name": "test-repo", "command": "repo-cmd"}
mock_get_repo_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_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",
bin_dir="/bin",
all_repos=[repo],
quiet=True,
preview=True,
)
@@ -61,8 +60,8 @@ class TestCreateInk(unittest.TestCase):
mock_symlink.assert_not_called()
mock_chmod.assert_not_called()
@patch("pkgmgr.create_ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier")
@patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_creates_symlink_and_alias(
self,
mock_get_repo_identifier,
@@ -75,19 +74,18 @@ class TestCreateInk(unittest.TestCase):
mock_get_repo_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_ink.os.chmod") as mock_chmod, \
patch("pkgmgr.create_ink.os.path.exists", return_value=False), \
patch("pkgmgr.create_ink.os.path.islink", return_value=False), \
patch("pkgmgr.create_ink.os.remove") as mock_remove, \
patch("pkgmgr.create_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.remove") as mock_remove, \
patch("pkgmgr.core.command.ink.os.path.realpath", side_effect=lambda p: p):
create_ink_module.create_ink(
repo=repo,
repositories_base_dir="/repos",
bin_dir="/bin",
all_repos=[repo],
quiet=True,
preview=False,
)
@@ -95,8 +93,6 @@ class TestCreateInk(unittest.TestCase):
self.assertEqual(mock_symlink.call_count, 2)
mock_makedirs.assert_called_once()
mock_chmod.assert_called_once()
mock_remove.assert_not_called()
if __name__ == "__main__":
unittest.main()

View File

@@ -6,7 +6,7 @@ import unittest
from types import SimpleNamespace
from unittest.mock import patch
from pkgmgr.git_utils import (
from pkgmgr.core.git import (
GitError,
run_git,
get_tags,
@@ -16,7 +16,7 @@ from pkgmgr.git_utils import (
class TestGitUtils(unittest.TestCase):
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_run_git_success(self, mock_run):
mock_run.return_value = SimpleNamespace(
stdout="ok\n",
@@ -33,7 +33,7 @@ class TestGitUtils(unittest.TestCase):
self.assertEqual(args[0][0], "git")
self.assertEqual(kwargs.get("cwd"), "/tmp/repo")
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_run_git_failure_raises_giterror(self, mock_run):
mock_run.side_effect = subprocess.CalledProcessError(
returncode=1,
@@ -51,7 +51,7 @@ class TestGitUtils(unittest.TestCase):
self.assertIn("bad", msg)
self.assertIn("error", msg)
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_tags_empty(self, mock_run):
mock_run.return_value = SimpleNamespace(
stdout="",
@@ -62,7 +62,7 @@ class TestGitUtils(unittest.TestCase):
tags = get_tags(cwd="/tmp/repo")
self.assertEqual(tags, [])
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_tags_non_empty(self, mock_run):
mock_run.return_value = SimpleNamespace(
stdout="v1.0.0\nv1.1.0\n",
@@ -73,7 +73,7 @@ class TestGitUtils(unittest.TestCase):
tags = get_tags(cwd="/tmp/repo")
self.assertEqual(tags, ["v1.0.0", "v1.1.0"])
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_head_commit_success(self, mock_run):
mock_run.return_value = SimpleNamespace(
stdout="abc123\n",
@@ -84,7 +84,7 @@ class TestGitUtils(unittest.TestCase):
commit = get_head_commit(cwd="/tmp/repo")
self.assertEqual(commit, "abc123")
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_head_commit_failure_returns_none(self, mock_run):
mock_run.side_effect = subprocess.CalledProcessError(
returncode=1,
@@ -96,7 +96,7 @@ class TestGitUtils(unittest.TestCase):
commit = get_head_commit(cwd="/tmp/repo")
self.assertIsNone(commit)
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_current_branch_success(self, mock_run):
mock_run.return_value = SimpleNamespace(
stdout="main\n",
@@ -107,7 +107,7 @@ class TestGitUtils(unittest.TestCase):
branch = get_current_branch(cwd="/tmp/repo")
self.assertEqual(branch, "main")
@patch("pkgmgr.git_utils.subprocess.run")
@patch("pkgmgr.core.git.subprocess.run")
def test_get_current_branch_failure_returns_none(self, mock_run):
mock_run.side_effect = subprocess.CalledProcessError(
returncode=1,

View File

@@ -3,9 +3,9 @@
import unittest
from unittest.mock import patch, MagicMock
from pkgmgr.context import RepoContext
import pkgmgr.install_repos as install_module
from pkgmgr.installers.base import BaseInstaller
from pkgmgr.actions.repository.install.context import RepoContext
import pkgmgr.actions.repository.install as install_module
from pkgmgr.actions.repository.install.installers.base import BaseInstaller
class DummyInstaller(BaseInstaller):
@@ -25,12 +25,12 @@ class DummyInstaller(BaseInstaller):
class TestInstallReposOrchestration(unittest.TestCase):
@patch("pkgmgr.install_repos.create_ink")
@patch("pkgmgr.install_repos.resolve_command_for_repo")
@patch("pkgmgr.install_repos.verify_repository")
@patch("pkgmgr.install_repos.get_repo_dir")
@patch("pkgmgr.install_repos.get_repo_identifier")
@patch("pkgmgr.install_repos.clone_repos")
@patch("pkgmgr.actions.repository.install.create_ink")
@patch("pkgmgr.actions.repository.install.resolve_command_for_repo")
@patch("pkgmgr.actions.repository.install.verify_repository")
@patch("pkgmgr.actions.repository.install.get_repo_dir")
@patch("pkgmgr.actions.repository.install.get_repo_identifier")
@patch("pkgmgr.actions.repository.install.clone_repos")
def test_install_repos_runs_pipeline_for_each_repo(
self,
mock_clone_repos,
@@ -82,10 +82,10 @@ class TestInstallReposOrchestration(unittest.TestCase):
self.assertEqual(mock_verify_repository.call_count, 2)
self.assertEqual(mock_resolve_command_for_repo.call_count, 2)
@patch("pkgmgr.install_repos.verify_repository")
@patch("pkgmgr.install_repos.get_repo_dir")
@patch("pkgmgr.install_repos.get_repo_identifier")
@patch("pkgmgr.install_repos.clone_repos")
@patch("pkgmgr.actions.repository.install.verify_repository")
@patch("pkgmgr.actions.repository.install.get_repo_dir")
@patch("pkgmgr.actions.repository.install.get_repo_identifier")
@patch("pkgmgr.actions.repository.install.clone_repos")
def test_install_repos_skips_on_failed_verification(
self,
mock_clone_repos,
@@ -104,10 +104,10 @@ class TestInstallReposOrchestration(unittest.TestCase):
mock_verify_repository.return_value = (False, ["sig error"], None, None)
dummy_installer = DummyInstaller()
with patch("os.path.exists", return_value=True), \
patch("pkgmgr.install_repos.create_ink") as mock_create_ink, \
patch("pkgmgr.install_repos.resolve_command_for_repo") as mock_resolve_cmd, \
patch("builtins.input", return_value="n"):
with patch("pkgmgr.actions.repository.install.create_ink") as mock_create_ink, \
patch("pkgmgr.actions.repository.install.resolve_command_for_repo") as mock_resolve_cmd, \
patch("os.path.exists", return_value=True), \
patch("builtins.input", return_value="n"):
old_installers = install_module.INSTALLERS
install_module.INSTALLERS = [dummy_installer]
try:

View File

@@ -6,8 +6,8 @@ import textwrap
import unittest
from unittest.mock import patch
from pkgmgr.versioning import SemVer
from pkgmgr.release import (
from pkgmgr.core.version.semver import SemVer
from pkgmgr.actions.release import (
_determine_current_version,
_bump_semver,
update_pyproject_version,
@@ -21,7 +21,7 @@ from pkgmgr.release import (
class TestDetermineCurrentVersion(unittest.TestCase):
@patch("pkgmgr.release.get_tags", return_value=[])
@patch("pkgmgr.actions.release.get_tags", return_value=[])
def test_determine_current_version_no_tags_returns_zero(
self,
mock_get_tags,
@@ -31,8 +31,8 @@ class TestDetermineCurrentVersion(unittest.TestCase):
self.assertEqual((ver.major, ver.minor, ver.patch), (0, 0, 0))
mock_get_tags.assert_called_once()
@patch("pkgmgr.release.find_latest_version")
@patch("pkgmgr.release.get_tags")
@patch("pkgmgr.actions.release.find_latest_version")
@patch("pkgmgr.actions.release.get_tags")
def test_determine_current_version_uses_latest_semver_tag(
self,
mock_get_tags,
@@ -365,17 +365,17 @@ class TestUpdateDebianChangelog(unittest.TestCase):
class TestReleaseOrchestration(unittest.TestCase):
@patch("pkgmgr.release.sys.stdin.isatty", return_value=False)
@patch("pkgmgr.release._run_git_command")
@patch("pkgmgr.release.update_debian_changelog")
@patch("pkgmgr.release.update_spec_version")
@patch("pkgmgr.release.update_pkgbuild_version")
@patch("pkgmgr.release.update_flake_version")
@patch("pkgmgr.release.get_current_branch", return_value="develop")
@patch("pkgmgr.release.update_changelog")
@patch("pkgmgr.release.update_pyproject_version")
@patch("pkgmgr.release._bump_semver")
@patch("pkgmgr.release._determine_current_version")
@patch("pkgmgr.actions.release.sys.stdin.isatty", return_value=False)
@patch("pkgmgr.actions.release._run_git_command")
@patch("pkgmgr.actions.release.update_debian_changelog")
@patch("pkgmgr.actions.release.update_spec_version")
@patch("pkgmgr.actions.release.update_pkgbuild_version")
@patch("pkgmgr.actions.release.update_flake_version")
@patch("pkgmgr.actions.release.get_current_branch", return_value="develop")
@patch("pkgmgr.actions.release.update_changelog")
@patch("pkgmgr.actions.release.update_pyproject_version")
@patch("pkgmgr.actions.release._bump_semver")
@patch("pkgmgr.actions.release._determine_current_version")
def test_release_happy_path_uses_helpers_and_git(
self,
mock_determine_current_version,
@@ -451,17 +451,17 @@ class TestReleaseOrchestration(unittest.TestCase):
self.assertIn("git push origin develop", git_calls)
self.assertIn("git push origin --tags", git_calls)
@patch("pkgmgr.release.sys.stdin.isatty", return_value=False)
@patch("pkgmgr.release._run_git_command")
@patch("pkgmgr.release.update_debian_changelog")
@patch("pkgmgr.release.update_spec_version")
@patch("pkgmgr.release.update_pkgbuild_version")
@patch("pkgmgr.release.update_flake_version")
@patch("pkgmgr.release.get_current_branch", return_value="develop")
@patch("pkgmgr.release.update_changelog")
@patch("pkgmgr.release.update_pyproject_version")
@patch("pkgmgr.release._bump_semver")
@patch("pkgmgr.release._determine_current_version")
@patch("pkgmgr.actions.release.sys.stdin.isatty", return_value=False)
@patch("pkgmgr.actions.release._run_git_command")
@patch("pkgmgr.actions.release.update_debian_changelog")
@patch("pkgmgr.actions.release.update_spec_version")
@patch("pkgmgr.actions.release.update_pkgbuild_version")
@patch("pkgmgr.actions.release.update_flake_version")
@patch("pkgmgr.actions.release.get_current_branch", return_value="develop")
@patch("pkgmgr.actions.release.update_changelog")
@patch("pkgmgr.actions.release.update_pyproject_version")
@patch("pkgmgr.actions.release._bump_semver")
@patch("pkgmgr.actions.release._determine_current_version")
def test_release_preview_mode_skips_git_and_uses_preview_flag(
self,
mock_determine_current_version,

View File

@@ -3,7 +3,7 @@
import unittest
from unittest.mock import patch
import pkgmgr.resolve_command as resolve_command_module
import pkgmgr.core.command.resolve as resolve_command_module
class TestResolveCommandForRepo(unittest.TestCase):
@@ -16,7 +16,7 @@ class TestResolveCommandForRepo(unittest.TestCase):
)
self.assertEqual(result, "/custom/cmd")
@patch("pkgmgr.resolve_command.shutil.which", return_value="/usr/bin/tool")
@patch("pkgmgr.core.command.resolve.shutil.which", return_value="/usr/bin/tool")
def test_system_binary_returns_none_and_no_error(self, mock_which):
repo = {}
result = resolve_command_module.resolve_command_for_repo(
@@ -27,10 +27,10 @@ class TestResolveCommandForRepo(unittest.TestCase):
# System binary → no link
self.assertIsNone(result)
@patch("pkgmgr.resolve_command.os.access")
@patch("pkgmgr.resolve_command.os.path.exists")
@patch("pkgmgr.resolve_command.shutil.which", return_value=None)
@patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome")
@patch("pkgmgr.core.command.resolve.os.access")
@patch("pkgmgr.core.command.resolve.os.path.exists")
@patch("pkgmgr.core.command.resolve.shutil.which", return_value=None)
@patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome")
def test_nix_profile_binary(
self,
mock_expanduser,
@@ -64,10 +64,10 @@ class TestResolveCommandForRepo(unittest.TestCase):
)
self.assertEqual(result, nix_path)
@patch("pkgmgr.resolve_command.os.access")
@patch("pkgmgr.resolve_command.os.path.exists")
@patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/home/user")
@patch("pkgmgr.resolve_command.shutil.which", return_value="/home/user/.local/bin/tool")
@patch("pkgmgr.core.command.resolve.os.access")
@patch("pkgmgr.core.command.resolve.os.path.exists")
@patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/home/user")
@patch("pkgmgr.core.command.resolve.shutil.which", return_value="/home/user/.local/bin/tool")
def test_non_system_binary_on_path(
self,
mock_which,
@@ -102,10 +102,10 @@ class TestResolveCommandForRepo(unittest.TestCase):
)
self.assertEqual(result, non_system_path)
@patch("pkgmgr.resolve_command.os.access")
@patch("pkgmgr.resolve_command.os.path.exists")
@patch("pkgmgr.resolve_command.shutil.which", return_value=None)
@patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome")
@patch("pkgmgr.core.command.resolve.os.access")
@patch("pkgmgr.core.command.resolve.os.path.exists")
@patch("pkgmgr.core.command.resolve.shutil.which", return_value=None)
@patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome")
def test_fallback_to_main_py(
self,
mock_expanduser,
@@ -136,10 +136,10 @@ class TestResolveCommandForRepo(unittest.TestCase):
)
self.assertEqual(result, main_py)
@patch("pkgmgr.resolve_command.os.access", return_value=False)
@patch("pkgmgr.resolve_command.os.path.exists", return_value=False)
@patch("pkgmgr.resolve_command.shutil.which", return_value=None)
@patch("pkgmgr.resolve_command.os.path.expanduser", return_value="/fakehome")
@patch("pkgmgr.core.command.resolve.os.access", return_value=False)
@patch("pkgmgr.core.command.resolve.os.path.exists", return_value=False)
@patch("pkgmgr.core.command.resolve.shutil.which", return_value=None)
@patch("pkgmgr.core.command.resolve.os.path.expanduser", return_value="/fakehome")
def test_no_command_results_in_system_exit(
self,
mock_expanduser,

View File

@@ -3,7 +3,7 @@
import unittest
from pkgmgr.versioning import (
from pkgmgr.core.version.semver import (
SemVer,
is_semver_tag,
extract_semver_from_tags,