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 1, 2023
1 parent db07750 commit 7f6fc97
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 6 deletions.
12 changes: 12 additions & 0 deletions api/outdated/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from functools import partial
from pathlib import Path
from shutil import rmtree
from typing import TYPE_CHECKING

import pytest
Expand Down Expand Up @@ -94,3 +96,13 @@ 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 repo_root(settings) -> Path: # noqa: ANN001
"""Change location of cloned projects to /tmp/outdated/projects/ ."""
settings.REPOSITORY_ROOT = _root = "/tmp/outdated/projects" # noqa: S108
root = Path(_root)
root.mkdir(parents=True, exist_ok=False)
yield root
rmtree(root)
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
regex = r"\/([^\s]+)\/([^_\s]+).*"
dependencies = [
findall(regex, dependency)[0]
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, 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(repo_root.iterdir()) == []

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

tracker = Tracker(project)
tracker.clone()
path = tracker.repository_path
assert path != 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 @@ -21,14 +21,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 @@ -67,7 +67,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 @@ -82,10 +82,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 = "/projects"

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

0 comments on commit 7f6fc97

Please sign in to comment.