Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tests/unit/artifact/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tmt.steps.prepare.artifact import get_artifact_provider
from tmt.steps.prepare.artifact.providers import koji as koji_module
from tmt.steps.prepare.artifact.providers.brew import BrewArtifactProvider
from tmt.steps.prepare.artifact.providers.copr_build import CoprBuildArtifactProvider
from tmt.steps.prepare.artifact.providers.koji import KojiArtifactProvider


Expand Down Expand Up @@ -50,6 +51,13 @@ def mock_initialize_session(self, api_url=None, top_url=None):
yield mock_koji


@pytest.fixture
def mock_copr():
mock_session = MagicMock()
with patch.object(CoprBuildArtifactProvider, '_initialize_session', return_value=mock_session):
yield mock_session


@pytest.fixture
def artifact_provider(root_logger, _load_plugins):
def get_provider(provider_id: str, repository_priority: int = 50):
Expand Down
16 changes: 13 additions & 3 deletions tests/unit/artifact/test_copr_repository.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch

import pytest

Expand Down Expand Up @@ -28,13 +28,23 @@ def test_invalid_repository_id(raw_id, error, artifact_provider):
artifact_provider(raw_id)


def test_fetch_contents_enables_repository(mock_package_manager, artifact_provider, tmppath):
def test_fetch_contents_enables_repository(
mock_copr, mock_package_manager, artifact_provider, tmppath
):
mock_repo = MagicMock()
mock_guest = MagicMock()
mock_guest.package_manager = mock_package_manager

provider = artifact_provider("copr.repository:@teemtee/stable")

result = provider.fetch_contents(mock_guest, tmppath / "artifacts")
with patch(
'tmt.steps.prepare.artifact.providers.copr_repository.Repository.from_file_path',
return_value=mock_repo,
) as mock_from_file:
result = provider.fetch_contents(mock_guest, tmppath / "artifacts")

mock_package_manager.enable_copr.assert_called_once_with('@teemtee/stable')
mock_guest.pull.assert_called_once()
mock_from_file.assert_called_once()
assert result == []
assert provider.repository is mock_repo
48 changes: 43 additions & 5 deletions tmt/steps/prepare/artifact/providers/copr_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,44 @@
from typing import Optional

import tmt.log
import tmt.utils
from tmt.container import container
from tmt.guest import Guest
from tmt.steps.prepare.artifact.providers import (
ArtifactInfo,
ArtifactProvider,
ArtifactProviderId,
Repository,
UnsupportedOperationError,
provides_artifact_provider,
)
from tmt.utils import Path

COPR_REPO_PATTERN = re.compile(r'^(?P<group>@)?(?P<name>[^/]+)/(?P<project>[^/]+)$')
COPR_REPOSITORY_PATTERN = re.compile(r'^(?:@[^/]+/[^/]+|[^@/]+/[^/]+)$')


@container(frozen=True)
class CoprRepo:
is_group: bool
name: str
project: str


def parse_copr_repo(copr_repo: str) -> CoprRepo:
"""
Parse a COPR repository identifier into its components.
"""
matched = COPR_REPO_PATTERN.match(copr_repo)
if not matched:
raise tmt.utils.PrepareError(f"Invalid copr repository '{copr_repo}'.")
return CoprRepo(
is_group=bool(matched.group('group')),
name=matched.group('name'),
project=matched.group('project'),
)


@provides_artifact_provider('copr.repository')
class CoprRepositoryProvider(ArtifactProvider):
"""
Expand All @@ -41,10 +66,12 @@ class CoprRepositoryProvider(ArtifactProvider):
"""

copr_repo: str # Parsed Copr repository name (e.g. 'packit/packit-dev')
repository: Optional[Repository]

def __init__(self, raw_id: str, repository_priority: int, logger: tmt.log.Logger):
super().__init__(raw_id, repository_priority, logger)
self.copr_repo = self.id
self.repository = None

@classmethod
def _extract_provider_id(cls, raw_id: str) -> ArtifactProviderId:
Expand Down Expand Up @@ -83,11 +110,22 @@ def fetch_contents(
exclude_patterns: Optional[list[Pattern[str]]] = None,
) -> list[Path]:
"""
Enable the Copr repository on the guest.
:return: Empty list as no files are downloaded.
:raises tmt.utils.PrepareError: If the package manager does not support
enabling Copr repositories.
Enable the Copr repository on the guest and retrieve the resulting
``.repo`` file content.
"""
guest.package_manager.enable_copr(self.copr_repo)

repo = parse_copr_repo(self.copr_repo)
owner = f'group_{repo.name}' if repo.is_group else repo.name
repo_filename = f"_copr:copr.fedorainfracloud.org:{owner}:{repo.project}.repo"

# pull from guest to build Repository object with populated repo_ids
guest.pull(
source=Path(f"/etc/yum.repos.d/{repo_filename}"),
destination=download_path,
)

self.repository = Repository.from_file_path(
download_path / repo_filename, self.logger, name=self.copr_repo
)
return []
Loading