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

View File

@@ -6,14 +6,14 @@
High-level helpers for branch-related operations. High-level helpers for branch-related operations.
This module encapsulates the actual Git logic so the CLI layer This module encapsulates the actual Git logic so the CLI layer
(pkgmgr.cli_core.commands.branch) stays thin and testable. (pkgmgr.cli.commands.branch) stays thin and testable.
""" """
from __future__ import annotations from __future__ import annotations
from typing import Optional from typing import Optional
from pkgmgr.git_utils import run_git, GitError, get_current_branch from pkgmgr.core.git import run_git, GitError, get_current_branch
def open_branch( def open_branch(

View File

@@ -13,7 +13,7 @@ from __future__ import annotations
from typing import Optional from typing import Optional
from pkgmgr.git_utils import run_git, GitError from pkgmgr.core.git import run_git, GitError
def generate_changelog( def generate_changelog(

View File

View File

@@ -26,8 +26,8 @@ import os
import subprocess import subprocess
from typing import Any, Dict from typing import Any, Dict
from pkgmgr.generate_alias import generate_alias from pkgmgr.core.command.alias import generate_alias
from pkgmgr.save_user_config import save_user_config from pkgmgr.core.config.save import save_user_config
def config_init( def config_init(

View File

@@ -1,5 +1,5 @@
import yaml import yaml
from .load_config import load_config from pkgmgr.core.config.load import load_config
def show_config(selected_repos, user_config_path, full_config=False): def show_config(selected_repos, user_config_path, full_config=False):
"""Display configuration for one or more repositories, or the entire merged config.""" """Display configuration for one or more repositories, or the entire merged config."""

View File

@@ -1,7 +1,7 @@
import os import os
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
import sys import sys
def exec_proxy_command(proxy_prefix: str, selected_repos, repositories_base_dir, all_repos, proxy_command: str, extra_args, preview: bool): def exec_proxy_command(proxy_prefix: str, selected_repos, repositories_base_dir, all_repos, proxy_command: str, extra_args, preview: bool):

View File

@@ -39,9 +39,9 @@ import tempfile
from datetime import date, datetime from datetime import date, datetime
from typing import Optional, Tuple from typing import Optional, Tuple
from pkgmgr.git_utils import get_tags, get_current_branch, GitError from pkgmgr.core.git import get_tags, get_current_branch, GitError
from pkgmgr.branch_commands import close_branch from pkgmgr.actions.branch import close_branch
from pkgmgr.versioning import ( from pkgmgr.core.version.semver import (
SemVer, SemVer,
find_latest_version, find_latest_version,
bump_major, bump_major,

View File

View File

@@ -1,8 +1,8 @@
import subprocess import subprocess
import os import os
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.verify import verify_repository from pkgmgr.core.repository.verify import verify_repository
def clone_repos( def clone_repos(
selected_repos, selected_repos,

View File

@@ -2,8 +2,8 @@ import os
import subprocess import subprocess
import sys import sys
import yaml import yaml
from pkgmgr.generate_alias import generate_alias from pkgmgr.core.command.alias import generate_alias
from pkgmgr.save_user_config import save_user_config from pkgmgr.core.config.save import save_user_config
def create_repo(identifier, config_merged, user_config_path, bin_dir, remote=False, preview=False): def create_repo(identifier, config_merged, user_config_path, bin_dir, remote=False, preview=False):
""" """

View File

@@ -1,7 +1,7 @@
import os import os
import sys import sys
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
def deinstall_repos(selected_repos, repositories_base_dir, bin_dir, all_repos, preview=False): def deinstall_repos(selected_repos, repositories_base_dir, bin_dir, all_repos, preview=False):
for repo in selected_repos: for repo in selected_repos:

View File

@@ -1,7 +1,7 @@
import shutil import shutil
import os import os
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
def delete_repos(selected_repos, repositories_base_dir, all_repos, preview=False): def delete_repos(selected_repos, repositories_base_dir, all_repos, preview=False):
for repo in selected_repos: for repo in selected_repos:

View File

@@ -21,23 +21,23 @@ focused installer classes.
import os import os
from typing import List, Dict, Any from typing import List, Dict, Any
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.create_ink import create_ink from pkgmgr.core.command.ink import create_ink
from pkgmgr.verify import verify_repository from pkgmgr.core.repository.verify import verify_repository
from pkgmgr.clone_repos import clone_repos from pkgmgr.actions.repository.clone import clone_repos
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.resolve_command import resolve_command_for_repo from pkgmgr.core.command.resolve import resolve_command_for_repo
# Installer implementations # Installer implementations
from pkgmgr.installers.os_packages import ( from pkgmgr.actions.repository.install.installers.os_packages import (
ArchPkgbuildInstaller, ArchPkgbuildInstaller,
DebianControlInstaller, DebianControlInstaller,
RpmSpecInstaller, RpmSpecInstaller,
) )
from pkgmgr.installers.nix_flake import NixFlakeInstaller from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller
from pkgmgr.installers.python import PythonInstaller from pkgmgr.actions.repository.install.installers.python import PythonInstaller
from pkgmgr.installers.makefile import MakefileInstaller from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller
# Layering: # Layering:

View File

@@ -38,7 +38,7 @@ from abc import ABC, abstractmethod
from typing import Iterable, TYPE_CHECKING from typing import Iterable, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Installer package for pkgmgr.
This exposes all installer classes so users can import them directly from
pkgmgr.actions.repository.install.installers.
"""
from pkgmgr.actions.repository.install.installers.base import BaseInstaller # noqa: F401
from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller # noqa: F401
from pkgmgr.actions.repository.install.installers.python import PythonInstaller # noqa: F401
from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller # noqa: F401
# OS-specific installers
from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller # noqa: F401
from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller # noqa: F401
from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import RpmSpecInstaller # noqa: F401

View File

@@ -8,8 +8,8 @@ Base interface for all installer components in the pkgmgr installation pipeline.
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Set from typing import Set
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.capabilities import CAPABILITY_MATCHERS from pkgmgr.actions.repository.install.capabilities import CAPABILITY_MATCHERS
class BaseInstaller(ABC): class BaseInstaller(ABC):

View File

@@ -12,9 +12,9 @@ installation step.
import os import os
import re import re
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
class MakefileInstaller(BaseInstaller): class MakefileInstaller(BaseInstaller):

View File

@@ -19,12 +19,12 @@ import os
import shutil import shutil
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
if TYPE_CHECKING: if TYPE_CHECKING:
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.install_repos import InstallContext from pkgmgr.actions.repository.install import InstallContext
class NixFlakeInstaller(BaseInstaller): class NixFlakeInstaller(BaseInstaller):

View File

@@ -3,9 +3,9 @@
import os import os
import shutil import shutil
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
class ArchPkgbuildInstaller(BaseInstaller): class ArchPkgbuildInstaller(BaseInstaller):

View File

@@ -20,9 +20,9 @@ import shutil
from typing import List from typing import List
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
class DebianControlInstaller(BaseInstaller): class DebianControlInstaller(BaseInstaller):

View File

@@ -19,9 +19,9 @@ import shutil
from typing import List, Optional from typing import List, Optional
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
class RpmSpecInstaller(BaseInstaller): class RpmSpecInstaller(BaseInstaller):

View File

@@ -17,8 +17,8 @@ All installation failures are treated as fatal errors (SystemExit).
import os import os
import sys import sys
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
class PythonInstaller(BaseInstaller): class PythonInstaller(BaseInstaller):

View File

@@ -1,9 +1,9 @@
import os import os
import subprocess import subprocess
import sys import sys
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.verify import verify_repository from pkgmgr.core.repository.verify import verify_repository
def pull_with_verification( def pull_with_verification(
selected_repos, selected_repos,

View File

@@ -1,9 +1,9 @@
import sys import sys
import shutil import shutil
from .exec_proxy_command import exec_proxy_command from pkgmgr.actions.proxy import exec_proxy_command
from .run_command import run_command from pkgmgr.core.command.run import run_command
from .get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
def status_repos( def status_repos(

View File

@@ -1,8 +1,8 @@
import sys import sys
import shutil import shutil
from pkgmgr.pull_with_verification import pull_with_verification from pkgmgr.actions.repository.pull import pull_with_verification
from pkgmgr.install_repos import install_repos from pkgmgr.actions.repository.install import install_repos
def update_repos( def update_repos(
@@ -54,7 +54,7 @@ def update_repos(
) )
if system_update: if system_update:
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
# Nix: upgrade all profile entries (if Nix is available) # Nix: upgrade all profile entries (if Nix is available)
if shutil.which("nix") is not None: if shutil.which("nix") is not None:

13
pkgmgr/cli.py → pkgmgr/cli/__init__.py Executable file → Normal file
View File

@@ -1,13 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import annotations from __future__ import annotations
import os import os
import sys
from pkgmgr.load_config import load_config from pkgmgr.core.config.load import load_config
from pkgmgr.cli_core import CLIContext, create_parser, dispatch_command
from .context import CLIContext
from .parser import create_parser
from .dispatch import dispatch_command
__all__ = ["CLIContext", "create_parser", "dispatch_command", "main"]
# User config lives in the home directory: # User config lives in the home directory:
# ~/.config/pkgmgr/config.yaml # ~/.config/pkgmgr/config.yaml

View File

@@ -3,8 +3,8 @@ from __future__ import annotations
import sys import sys
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.branch_commands import open_branch, close_branch from pkgmgr.actions.branch import open_branch, close_branch
def handle_branch(args, ctx: CLIContext) -> None: def handle_branch(args, ctx: CLIContext) -> None:

View File

@@ -4,12 +4,12 @@ import os
import sys import sys
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.git_utils import get_tags from pkgmgr.core.git import get_tags
from pkgmgr.versioning import SemVer, extract_semver_from_tags from pkgmgr.core.version.semver import SemVer, extract_semver_from_tags
from pkgmgr.changelog import generate_changelog from pkgmgr.actions.changelog import generate_changelog
Repository = Dict[str, Any] Repository = Dict[str, Any]

View File

@@ -11,13 +11,13 @@ from typing import Any, Dict
import yaml import yaml
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.config_init import config_init from pkgmgr.actions.config.init import config_init
from pkgmgr.interactive_add import interactive_add from pkgmgr.actions.config.add import interactive_add
from pkgmgr.resolve_repos import resolve_repos from pkgmgr.core.repository.resolve import resolve_repos
from pkgmgr.save_user_config import save_user_config from pkgmgr.core.config.save import save_user_config
from pkgmgr.show_config import show_config from pkgmgr.actions.config.show import show_config
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
def _load_user_config(user_config_path: str) -> Dict[str, Any]: def _load_user_config(user_config_path: str) -> Dict[str, Any]:

View File

@@ -3,8 +3,8 @@ from __future__ import annotations
import sys import sys
from typing import Any, Dict, List from typing import Any, Dict, List
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.exec_proxy_command import exec_proxy_command from pkgmgr.actions.proxy import exec_proxy_command
Repository = Dict[str, Any] Repository = Dict[str, Any]

View File

@@ -13,7 +13,7 @@ Responsibilities:
- Take the parsed argparse.Namespace for the `release` command. - Take the parsed argparse.Namespace for the `release` command.
- Use the list of selected repositories provided by dispatch_command(). - Use the list of selected repositories provided by dispatch_command().
- Optionally list affected repositories when --list is set. - Optionally list affected repositories when --list is set.
- For each selected repository, run pkgmgr.release.release(...) in - For each selected repository, run pkgmgr.actions.release.release(...) in
the context of that repository directory. the context of that repository directory.
""" """
@@ -22,10 +22,10 @@ from __future__ import annotations
import os import os
from typing import Any, Dict, List from typing import Any, Dict, List
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.release import release as run_release from pkgmgr.actions.release import release as run_release
Repository = Dict[str, Any] Repository = Dict[str, Any]
@@ -46,7 +46,7 @@ def handle_release(
3) For each selected repository: 3) For each selected repository:
- Resolve its identifier and local directory. - Resolve its identifier and local directory.
- Change into that directory. - Change into that directory.
- Call pkgmgr.release.release(...) with the parsed options. - Call pkgmgr.actions.release.release(...) with the parsed options.
""" """
if not selected: if not selected:
print("[pkgmgr] No repositories selected for release.") print("[pkgmgr] No repositories selected for release.")

View File

@@ -6,16 +6,16 @@ from __future__ import annotations
import sys import sys
from typing import Any, Dict, List from typing import Any, Dict, List
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.install_repos import install_repos from pkgmgr.actions.repository.install import install_repos
from pkgmgr.deinstall_repos import deinstall_repos from pkgmgr.actions.repository.deinstall import deinstall_repos
from pkgmgr.delete_repos import delete_repos from pkgmgr.actions.repository.delete import delete_repos
from pkgmgr.update_repos import update_repos from pkgmgr.actions.repository.update import update_repos
from pkgmgr.status_repos import status_repos from pkgmgr.actions.repository.status import status_repos
from pkgmgr.list_repositories import list_repositories from pkgmgr.actions.repository.list import list_repositories
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
from pkgmgr.create_repo import create_repo from pkgmgr.actions.repository.create import create_repo
from pkgmgr.get_selected_repos import get_selected_repos from pkgmgr.core.repository.selected import get_selected_repos
Repository = Dict[str, Any] Repository = Dict[str, Any]

View File

@@ -5,9 +5,9 @@ import os
from typing import Any, Dict, List from typing import Any, Dict, List
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.run_command import run_command from pkgmgr.core.command.run import run_command
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
Repository = Dict[str, Any] Repository = Dict[str, Any]

View File

@@ -4,12 +4,12 @@ import os
import sys import sys
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.git_utils import get_tags from pkgmgr.core.git import get_tags
from pkgmgr.versioning import SemVer, find_latest_version from pkgmgr.core.version.semver import SemVer, find_latest_version
from pkgmgr.version_sources import ( from pkgmgr.core.version.source import (
read_pyproject_version, read_pyproject_version,
read_flake_version, read_flake_version,
read_pkgbuild_version, read_pkgbuild_version,

View File

@@ -7,12 +7,12 @@ import os
import sys import sys
from typing import List, Dict, Any from typing import List, Dict, Any
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.cli_core.proxy import maybe_handle_proxy from pkgmgr.cli.proxy import maybe_handle_proxy
from pkgmgr.get_selected_repos import get_selected_repos from pkgmgr.core.repository.selected import get_selected_repos
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.cli_core.commands import ( from pkgmgr.cli.commands import (
handle_repos_command, handle_repos_command,
handle_tools_command, handle_tools_command,
handle_release, handle_release,

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
import argparse import argparse
from pkgmgr.cli_core.proxy import register_proxy_commands from pkgmgr.cli.proxy import register_proxy_commands
class SortedSubParsersAction(argparse._SubParsersAction): class SortedSubParsersAction(argparse._SubParsersAction):

View File

@@ -8,12 +8,12 @@ import os
import sys import sys
from typing import Dict, List, Any from typing import Dict, List, Any
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
from pkgmgr.clone_repos import clone_repos from pkgmgr.actions.repository.clone import clone_repos
from pkgmgr.exec_proxy_command import exec_proxy_command from pkgmgr.actions.proxy import exec_proxy_command
from pkgmgr.pull_with_verification import pull_with_verification from pkgmgr.actions.repository.pull import pull_with_verification
from pkgmgr.get_selected_repos import get_selected_repos from pkgmgr.core.repository.selected import get_selected_repos
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
PROXY_COMMANDS: Dict[str, List[str]] = { PROXY_COMMANDS: Dict[str, List[str]] = {

View File

@@ -1,5 +0,0 @@
from .context import CLIContext
from .parser import create_parser
from .dispatch import dispatch_command
__all__ = ["CLIContext", "create_parser", "dispatch_command"]

View File

View File

@@ -2,8 +2,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.core.repository.dir import get_repo_dir
def create_ink(repo, repositories_base_dir, bin_dir, all_repos, def create_ink(repo, repositories_base_dir, bin_dir, all_repos,

View File

View File

View File

@@ -7,7 +7,7 @@ import os
import re import re
from typing import Any, Dict, List, Sequence from typing import Any, Dict, List, Sequence
from pkgmgr.resolve_repos import resolve_repos from pkgmgr.core.repository.resolve import resolve_repos
Repository = Dict[str, Any] Repository = Dict[str, Any]

View File

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Installer package for pkgmgr.
This exposes all installer classes so users can import them directly from
pkgmgr.installers.
"""
from pkgmgr.installers.base import BaseInstaller # noqa: F401
from pkgmgr.installers.nix_flake import NixFlakeInstaller # noqa: F401
from pkgmgr.installers.python import PythonInstaller # noqa: F401
from pkgmgr.installers.makefile import MakefileInstaller # noqa: F401
# OS-specific installers
from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller # noqa: F401
from pkgmgr.installers.os_packages.debian_control import DebianControlInstaller # noqa: F401
from pkgmgr.installers.os_packages.rpm_spec import RpmSpecInstaller # noqa: F401

View File

@@ -31,7 +31,7 @@ class TestIntegrationBranchCommands(unittest.TestCase):
finally: finally:
sys.argv = original_argv sys.argv = original_argv
@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) -> None: def test_branch_open_with_name_and_base(self, mock_open_branch) -> None:
""" """
`pkgmgr branch open feature/test --base develop` must forward `pkgmgr branch open feature/test --base develop` must forward
@@ -47,7 +47,7 @@ class TestIntegrationBranchCommands(unittest.TestCase):
self.assertEqual(kwargs.get("base_branch"), "develop") self.assertEqual(kwargs.get("base_branch"), "develop")
self.assertEqual(kwargs.get("cwd"), ".") self.assertEqual(kwargs.get("cwd"), ".")
@patch("pkgmgr.cli_core.commands.branch.open_branch") @patch("pkgmgr.cli.commands.branch.open_branch")
def test_branch_open_without_name_uses_default_base( def test_branch_open_without_name_uses_default_base(
self, self,
mock_open_branch, mock_open_branch,
@@ -68,7 +68,7 @@ class TestIntegrationBranchCommands(unittest.TestCase):
# close subcommand # close subcommand
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@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) -> None: def test_branch_close_with_name_and_base(self, mock_close_branch) -> None:
""" """
`pkgmgr branch close feature/test --base develop` must forward `pkgmgr branch close feature/test --base develop` must forward
@@ -84,7 +84,7 @@ class TestIntegrationBranchCommands(unittest.TestCase):
self.assertEqual(kwargs.get("base_branch"), "develop") self.assertEqual(kwargs.get("base_branch"), "develop")
self.assertEqual(kwargs.get("cwd"), ".") 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_default_base( def test_branch_close_without_name_uses_default_base(
self, self,
mock_close_branch, mock_close_branch,

View File

@@ -6,7 +6,7 @@ End-to-end style integration tests for the `pkgmgr release` CLI command.
These tests exercise the real top-level entry point (main.py) and mock These tests exercise the real top-level entry point (main.py) and mock
the high-level helper used by the CLI wiring the high-level helper used by the CLI wiring
(pkgmgr.cli_core.commands.release.run_release) to ensure that argument (pkgmgr.cli.commands.release.run_release) to ensure that argument
parsing and dispatch behave as expected, in particular the new `close` parsing and dispatch behave as expected, in particular the new `close`
flag. flag.
@@ -52,8 +52,8 @@ class TestIntegrationReleaseCommand(unittest.TestCase):
# Behaviour without --close # Behaviour without --close
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@patch("pkgmgr.cli_core.commands.release.run_release") @patch("pkgmgr.cli.commands.release.run_release")
@patch("pkgmgr.cli_core.dispatch._select_repo_for_current_directory") @patch("pkgmgr.cli.dispatch._select_repo_for_current_directory")
def test_release_without_close_flag( def test_release_without_close_flag(
self, self,
mock_select_repo, mock_select_repo,
@@ -95,8 +95,8 @@ class TestIntegrationReleaseCommand(unittest.TestCase):
# Behaviour with --close # Behaviour with --close
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@patch("pkgmgr.cli_core.commands.release.run_release") @patch("pkgmgr.cli.commands.release.run_release")
@patch("pkgmgr.cli_core.dispatch._select_repo_for_current_directory") @patch("pkgmgr.cli.dispatch._select_repo_for_current_directory")
def test_release_with_close_flag( def test_release_with_close_flag(
self, self,
mock_select_repo, mock_select_repo,

View File

@@ -28,7 +28,7 @@ import sys
import unittest import unittest
from typing import List from typing import List
from pkgmgr.load_config import load_config from pkgmgr.core.config.load import load_config
# Resolve project root (the repo where main.py lives, e.g. /src) # Resolve project root (the repo where main.py lives, e.g. /src)
PROJECT_ROOT = os.path.abspath( PROJECT_ROOT = os.path.abspath(

View File

@@ -5,9 +5,9 @@ import tempfile
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
import pkgmgr.install_repos as install_module import pkgmgr.actions.repository.install as install_module
from pkgmgr.install_repos import install_repos from pkgmgr.actions.repository.install import install_repos
from pkgmgr.installers.base import BaseInstaller from pkgmgr.actions.repository.install.installers.base import BaseInstaller
class DummyInstaller(BaseInstaller): class DummyInstaller(BaseInstaller):
@@ -26,10 +26,10 @@ class DummyInstaller(BaseInstaller):
class TestInstallReposIntegration(unittest.TestCase): class TestInstallReposIntegration(unittest.TestCase):
@patch("pkgmgr.install_repos.verify_repository") @patch("pkgmgr.actions.repository.install.verify_repository")
@patch("pkgmgr.install_repos.clone_repos") @patch("pkgmgr.actions.repository.install.clone_repos")
@patch("pkgmgr.install_repos.get_repo_dir") @patch("pkgmgr.actions.repository.install.get_repo_dir")
@patch("pkgmgr.install_repos.get_repo_identifier") @patch("pkgmgr.actions.repository.install.get_repo_identifier")
def test_system_binary_vs_nix_binary( def test_system_binary_vs_nix_binary(
self, self,
mock_get_repo_identifier, mock_get_repo_identifier,
@@ -100,8 +100,8 @@ class TestInstallReposIntegration(unittest.TestCase):
nix_tool_path = "/nix/profile/bin/repo-nix" nix_tool_path = "/nix/profile/bin/repo-nix"
# Patch resolve_command_for_repo at the install_repos module level # Patch resolve_command_for_repo at the install_repos module level
with patch("pkgmgr.install_repos.resolve_command_for_repo") as mock_resolve, \ with patch("pkgmgr.actions.repository.install.resolve_command_for_repo") as mock_resolve, \
patch("pkgmgr.install_repos.os.path.exists") as mock_exists_install: patch("pkgmgr.actions.repository.install.os.path.exists") as mock_exists_install:
def fake_resolve_command(repo, repo_identifier: str, repo_dir: str): def fake_resolve_command(repo, repo_identifier: str, repo_dir: str):
""" """

View File

@@ -61,12 +61,12 @@ import tempfile
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
import pkgmgr.install_repos as install_mod import pkgmgr.actions.repository.install as install_mod
from pkgmgr.install_repos import install_repos from pkgmgr.actions.repository.install import install_repos
from pkgmgr.installers.nix_flake import NixFlakeInstaller from pkgmgr.actions.repository.install.installers.nix_flake import NixFlakeInstaller
from pkgmgr.installers.python import PythonInstaller from pkgmgr.actions.repository.install.installers.python import PythonInstaller
from pkgmgr.installers.makefile import MakefileInstaller from pkgmgr.actions.repository.install.installers.makefile import MakefileInstaller
from pkgmgr.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller from pkgmgr.actions.repository.install.installers.os_packages.arch_pkgbuild import ArchPkgbuildInstaller
class TestRecursiveCapabilitiesIntegration(unittest.TestCase): class TestRecursiveCapabilitiesIntegration(unittest.TestCase):

View File

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

View File

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

View File

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

View File

@@ -4,8 +4,8 @@ import os
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.os_packages.debian_control import DebianControlInstaller from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller
class TestDebianControlInstaller(unittest.TestCase): class TestDebianControlInstaller(unittest.TestCase):
@@ -36,7 +36,7 @@ class TestDebianControlInstaller(unittest.TestCase):
def test_supports_false_without_dpkg_buildpackage(self, mock_which, mock_exists): def test_supports_false_without_dpkg_buildpackage(self, mock_which, mock_exists):
self.assertFalse(self.installer.supports(self.ctx)) 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("glob.glob", return_value=["/tmp/package-manager_0.1.1_all.deb"])
@patch("os.path.exists", return_value=True) @patch("os.path.exists", return_value=True)
@patch("shutil.which") @patch("shutil.which")

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,8 +4,8 @@ import os
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
from pkgmgr.installers.python import PythonInstaller from pkgmgr.actions.repository.install.installers.python import PythonInstaller
class TestPythonInstaller(unittest.TestCase): class TestPythonInstaller(unittest.TestCase):
@@ -34,7 +34,7 @@ class TestPythonInstaller(unittest.TestCase):
def test_supports_false_when_no_pyproject(self, mock_exists): def test_supports_false_when_no_pyproject(self, mock_exists):
self.assertFalse(self.installer.supports(self.ctx)) 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")) @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): def test_run_installs_project_from_pyproject(self, mock_exists, mock_run_command):
self.installer.run(self.ctx) self.installer.run(self.ctx)

View File

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

View File

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

View File

@@ -3,13 +3,12 @@ from __future__ import annotations
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from pkgmgr.changelog import generate_changelog from pkgmgr.actions.changelog import generate_changelog
from pkgmgr.git_utils import GitError from pkgmgr.core.git import GitError
from pkgmgr.cli_core.commands.changelog import _find_previous_and_current_tag from pkgmgr.cli.commands.changelog import _find_previous_and_current_tag
class TestGenerateChangelog(unittest.TestCase): 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: def test_generate_changelog_default_range_no_merges(self, mock_run_git) -> None:
""" """
Default behaviour: Default behaviour:
@@ -35,7 +34,7 @@ class TestGenerateChangelog(unittest.TestCase):
self.assertIn("HEAD", args[0]) self.assertIn("HEAD", args[0])
self.assertEqual(kwargs.get("cwd"), "/repo") 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: def test_generate_changelog_with_range_and_merges(self, mock_run_git) -> None:
""" """
Explicit range and include_merges=True: Explicit range and include_merges=True:
@@ -64,7 +63,7 @@ class TestGenerateChangelog(unittest.TestCase):
self.assertIn("v1.0.0..v1.1.0", cmd) self.assertIn("v1.0.0..v1.1.0", cmd)
self.assertEqual(kwargs.get("cwd"), "/repo") 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: 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. 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("simulated git failure", result)
self.assertIn("v0.1.0..v0.2.0", 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: def test_generate_changelog_empty_output_returns_info(self, mock_run_git) -> None:
""" """
Empty git log output -> informational message instead of empty string. 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, # This matches the new behaviour: without explicit identifiers,
# version uses _select_repo_for_current_directory(ctx). # version uses _select_repo_for_current_directory(ctx).
self._patch_select_repo_for_current_directory = mock.patch( 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], return_value=[self._fake_repo],
) )
self.mock_select_repo_for_current_directory = ( self.mock_select_repo_for_current_directory = (
@@ -166,7 +166,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: mock git tags used by handle_version # Arrange: mock git tags used by handle_version
with mock.patch( 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"], return_value=["v1.2.0", "v1.2.3", "v1.0.0"],
): ):
# Act # Act
@@ -198,7 +198,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: mock git tags (latest is 1.2.3) # Arrange: mock git tags (latest is 1.2.3)
with mock.patch( with mock.patch(
"pkgmgr.cli_core.commands.version.get_tags", "pkgmgr.cli.commands.version.get_tags",
return_value=["v1.2.3"], return_value=["v1.2.3"],
): ):
stdout = self._run_cli_version_and_capture() stdout = self._run_cli_version_and_capture()
@@ -226,7 +226,7 @@ class TestCliVersion(unittest.TestCase):
# Arrange: no tags returned # Arrange: no tags returned
with mock.patch( with mock.patch(
"pkgmgr.cli_core.commands.version.get_tags", "pkgmgr.cli.commands.version.get_tags",
return_value=[], return_value=[],
): ):
stdout = self._run_cli_version_and_capture() stdout = self._run_cli_version_and_capture()

View File

@@ -4,8 +4,8 @@ import unittest
from types import SimpleNamespace from types import SimpleNamespace
from unittest.mock import patch from unittest.mock import patch
from pkgmgr.cli_core.commands.branch import handle_branch from pkgmgr.cli.commands.branch import handle_branch
from pkgmgr.cli_core.context import CLIContext from pkgmgr.cli.context import CLIContext
class TestCliBranch(unittest.TestCase): class TestCliBranch(unittest.TestCase):
@@ -22,7 +22,7 @@ class TestCliBranch(unittest.TestCase):
user_config_path="/tmp/config.yaml", 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: 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='.'. 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("base_branch"), "develop")
self.assertEqual(call_kwargs.get("cwd"), ".") 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: 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), If --base is not passed, argparse gives base='main' (default),
@@ -70,7 +70,7 @@ class TestCliBranch(unittest.TestCase):
# close subcommand # 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: 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='.'. 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("base_branch"), "develop")
self.assertEqual(call_kwargs.get("cwd"), ".") 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: 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' If --base is not passed for 'close', argparse gives base='main'

View File

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

View File

@@ -1,5 +1,5 @@
import unittest import unittest
from pkgmgr.context import RepoContext from pkgmgr.actions.repository.install.context import RepoContext
class TestRepoContext(unittest.TestCase): 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 import unittest
from unittest.mock import patch 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): class TestCreateInk(unittest.TestCase):
@patch("pkgmgr.create_ink.get_repo_dir") @patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier") @patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_skips_when_no_command( def test_create_ink_skips_when_no_command(
self, self,
mock_get_repo_identifier, mock_get_repo_identifier,
mock_get_repo_dir, 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_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id" mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_ink.os.chmod") as mock_chmod: patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod:
create_ink_module.create_ink( create_ink_module.create_ink(
repo=repo, repo=repo,
repositories_base_dir="/repos", repositories_base_dir="/repos",
bin_dir="/bin", bin_dir="/bin",
all_repos=[repo], all_repos=[repo],
quiet=True,
preview=False, preview=False,
) )
@@ -34,26 +34,25 @@ class TestCreateInk(unittest.TestCase):
mock_symlink.assert_not_called() mock_symlink.assert_not_called()
mock_chmod.assert_not_called() mock_chmod.assert_not_called()
@patch("pkgmgr.create_ink.get_repo_dir") @patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier") @patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_preview_only( def test_create_ink_preview_only(
self, self,
mock_get_repo_identifier, mock_get_repo_identifier,
mock_get_repo_dir, 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_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id" mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_ink.os.chmod") as mock_chmod: patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod:
create_ink_module.create_ink( create_ink_module.create_ink(
repo=repo, repo=repo,
repositories_base_dir="/repos", repositories_base_dir="/repos",
bin_dir="/bin", bin_dir="/bin",
all_repos=[repo], all_repos=[repo],
quiet=True,
preview=True, preview=True,
) )
@@ -61,8 +60,8 @@ class TestCreateInk(unittest.TestCase):
mock_symlink.assert_not_called() mock_symlink.assert_not_called()
mock_chmod.assert_not_called() mock_chmod.assert_not_called()
@patch("pkgmgr.create_ink.get_repo_dir") @patch("pkgmgr.core.command.ink.get_repo_dir")
@patch("pkgmgr.create_ink.get_repo_identifier") @patch("pkgmgr.core.command.ink.get_repo_identifier")
def test_create_ink_creates_symlink_and_alias( def test_create_ink_creates_symlink_and_alias(
self, self,
mock_get_repo_identifier, mock_get_repo_identifier,
@@ -75,19 +74,18 @@ class TestCreateInk(unittest.TestCase):
mock_get_repo_identifier.return_value = "test-id" mock_get_repo_identifier.return_value = "test-id"
mock_get_repo_dir.return_value = "/repos/test-id" mock_get_repo_dir.return_value = "/repos/test-id"
with patch("pkgmgr.create_ink.os.makedirs") as mock_makedirs, \ with patch("pkgmgr.core.command.ink.os.makedirs") as mock_makedirs, \
patch("pkgmgr.create_ink.os.symlink") as mock_symlink, \ patch("pkgmgr.core.command.ink.os.symlink") as mock_symlink, \
patch("pkgmgr.create_ink.os.chmod") as mock_chmod, \ patch("pkgmgr.core.command.ink.os.chmod") as mock_chmod, \
patch("pkgmgr.create_ink.os.path.exists", return_value=False), \ patch("pkgmgr.core.command.ink.os.path.exists", return_value=False), \
patch("pkgmgr.create_ink.os.path.islink", return_value=False), \ patch("pkgmgr.core.command.ink.os.path.islink", return_value=False), \
patch("pkgmgr.create_ink.os.remove") as mock_remove, \ patch("pkgmgr.core.command.ink.os.remove") as mock_remove, \
patch("pkgmgr.create_ink.os.path.realpath", side_effect=lambda p: p): patch("pkgmgr.core.command.ink.os.path.realpath", side_effect=lambda p: p):
create_ink_module.create_ink( create_ink_module.create_ink(
repo=repo, repo=repo,
repositories_base_dir="/repos", repositories_base_dir="/repos",
bin_dir="/bin", bin_dir="/bin",
all_repos=[repo], all_repos=[repo],
quiet=True,
preview=False, preview=False,
) )
@@ -95,8 +93,6 @@ class TestCreateInk(unittest.TestCase):
self.assertEqual(mock_symlink.call_count, 2) self.assertEqual(mock_symlink.call_count, 2)
mock_makedirs.assert_called_once() mock_makedirs.assert_called_once()
mock_chmod.assert_called_once() mock_chmod.assert_called_once()
mock_remove.assert_not_called()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

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

View File

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

View File

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

View File

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

View File

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