Skip to content

Commit

Permalink
chore(api/tests): added test for repo cloning and parsing deps
Browse files Browse the repository at this point in the history
  • Loading branch information
c0rydoras committed Nov 3, 2023
1 parent 02114e3 commit 866bd9a
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 6 deletions.
8 changes: 8 additions & 0 deletions api/outdated/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

if TYPE_CHECKING:
from collections.abc import Callable
from pathlib import Path
from unittest.mock import MagicMock

from pytest_mock import MockerFixture
Expand Down Expand Up @@ -94,3 +95,10 @@ def _tracker_mock(target: str) -> MagicMock:
def tracker_init_mock(mocker: MockerFixture) -> MagicMock:
"""Mock for the Trackers __init__ method."""
return mocker.patch.object(Tracker, "__init__", return_value=None)


@pytest.fixture
def tmp_repo_root(settings, tmp_path) -> Path: # noqa: ANN001
"""Change settings.REPOSITORY_ROOT to a temporary directory."""
settings.REPOSITORY_ROOT = str(tmp_path.absolute())
return tmp_path
2 changes: 1 addition & 1 deletion api/outdated/outdated/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def parse(self) -> list[models.Version]:
elif name == "pnpm-lock.yaml":
lockfile_data = safe_load(data)
regex = r"\/(@?[^\s@]+)@([^()]+).*"
if float(lockfile_data["lockfileVersion"]) < 6.0:
if float(lockfile_data["lockfileVersion"]) < 6.0: # pragma: no cover
raise NotImplementedError(
"Outdated does not support pnpm-lock.yaml lockfiles with a version lower than 6",
)
Expand Down
66 changes: 66 additions & 0 deletions api/outdated/outdated/tests/test_tracking.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from unittest.mock import ANY, MagicMock

from django.urls import reverse
from rest_framework import status

from outdated.outdated.models import Project
from outdated.outdated.parser import LockfileParser
from outdated.outdated.tracking import Tracker


def test_serializer(client, project_factory, tracker_init_mock, tracker_mock):
Expand Down Expand Up @@ -62,3 +66,65 @@ def test_serializer(client, project_factory, tracker_init_mock, tracker_mock):
assert resp.status_code == status.HTTP_204_NO_CONTENT

assert delete_mock.call_count == 2


def test_clone_and_parse(db, project_factory, settings, mocker, tmp_repo_root):
data = {
"poetry": {"lockfile": "poetry.lock", "version": "1.6.1"},
"yarn": {"lockfile": "yarn.lock", "version": "1.22.19"},
"pnpm": {"lockfile": "pnpm-lock.yaml", "version": "8.10.0"},
}
settings.TRACKED_DEPENDENCIES = list(data.keys())
assert list(tmp_repo_root.iterdir()) == []

project: Project = project_factory(repo="github.com/c0rydoras/lockfile-test")

tracker = Tracker(project)
tracker.clone()
path = tracker.repository_path
assert path != tmp_repo_root.absolute()
assert list(path.iterdir()) == [path / ".git"]
tracker.checkout()

directories = []
lockfiles = []

for k, v in data.items():
directory = path / k
assert directory.exists()
assert directory.is_dir()
lockfile = directory / v["lockfile"]
assert lockfile.exists()
assert lockfile.is_file()
directories.append(directory)
lockfiles.append(lockfile)

lockfiles.sort()

dirs = list(path.iterdir())
expected_dirs = [path / ".git", *directories]
dirs.sort()
expected_dirs.sort()

assert expected_dirs == dirs

tracker_lockfiles = tracker.lockfiles

assert len(tracker_lockfiles) == len(data)

_lockfiles = tracker_lockfiles.copy()
_lockfiles.sort()

assert _lockfiles == lockfiles

lockfile_parser_mock: MagicMock = mocker.spy(LockfileParser, "__init__")

tracker.sync()

lockfile_parser_mock.assert_called_once_with(ANY, tracker_lockfiles)

versions = project.versioned_dependencies.all()

for k, v in data.items():
version = versions.get(release_version__dependency__name=k)
assert version.version == v["version"]
14 changes: 9 additions & 5 deletions api/outdated/outdated/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ class RepoError(ValueError):
class Tracker:
def __init__(self, project: Project) -> None:
self.project = project
self.local_path = Path(f"/projects/{self.project.clone_path}")
self.local_path = Path(f"{settings.REPOSITORY_ROOT}/{self.project.clone_path}")

def _run(
self,
command: list[str],
requires_local_copy: bool = False,
) -> CompletedProcess[str]:
if not self.local_path.exists() and requires_local_copy:
if not self.local_path.exists() and requires_local_copy: # pragma: no cover
msg = f"Can't run {command} without local copy of {self.project.repo}"
raise RepoError(msg)
return run(
Expand Down Expand Up @@ -68,7 +68,7 @@ def checkout(self): # pragma: no cover

@property
def lockfiles(self):
if not self.local_path.exists():
if not self.local_path.exists(): # pragma: no cover
msg = f"Unable to retrieve lockfiles for {self.project.repo} because it is not saved locally."
raise RepoError(msg)

Expand All @@ -83,10 +83,14 @@ def lockfiles(self):

@property
def repository_path(self):
return self.local_path.absolute() if self.local_path.exists() else "/projects/"
return (
self.local_path.absolute()
if self.local_path.exists()
else Path(settings.REPOSITORY_ROOT)
)

def sync(self):
if not self.local_path.exists():
if not self.local_path.exists(): # pragma: no cover
self.clone()
self.checkout()
dependencies = LockfileParser(self.lockfiles).parse()
Expand Down
2 changes: 2 additions & 0 deletions api/outdated/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ def default(default_dev=env.NOTSET, default_prod=env.NOTSET):
],
)

# Where to put the cloned projects
REPOSITORY_ROOT = "/home/outdated/projects"

NPM_FILES = ["yarn.lock", "pnpm-lock.yaml"]
PYPI_FILES = ["poetry.lock"]
Expand Down

0 comments on commit 866bd9a

Please sign in to comment.