diff --git a/.gitignore b/.gitignore index 674cab40..3ce84936 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,4 @@ dmypy.json cython_debug/ .DS_Store +.vscode/ diff --git a/pyproject.toml b/pyproject.toml index bb3fb3db..6a123080 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,8 @@ tests = [ "pytest-mock", "pytest-sugar", "pytest-test-utils>=0.1.0,<0.2", + "proxy.py", + "pytest-socket", ] dev = [ "mypy==1.11.1", @@ -66,13 +68,14 @@ where = ["src"] namespaces = false [tool.pytest.ini_options] -addopts = "-ra" +addopts = "-ra --allow-unix-socket" markers = [ "skip_git_backend: skip tests for given backend", "slow: mark test as slow to run", ] asyncio_mode = "auto" + [tool.coverage.run] branch = true source = ["scmrepo", "tests"] diff --git a/src/scmrepo/git/backend/dulwich/__init__.py b/src/scmrepo/git/backend/dulwich/__init__.py index a4280168..c54b0678 100644 --- a/src/scmrepo/git/backend/dulwich/__init__.py +++ b/src/scmrepo/git/backend/dulwich/__init__.py @@ -618,7 +618,8 @@ def push_refspecs( # noqa: C901 try: _remote, location = get_remote_repo(self.repo, url) - client, path = get_transport_and_path(location, **kwargs) + _config = kwargs.pop("config", StackedConfig.default()) + client, path = get_transport_and_path(location, config=_config, **kwargs) except Exception as exc: raise SCMError(f"'{url}' is not a valid Git remote or URL") from exc @@ -725,7 +726,8 @@ def determine_wants( with reraise(Exception, SCMError(f"'{url}' is not a valid Git remote or URL")): _remote, location = get_remote_repo(self.repo, url) - client, path = get_transport_and_path(location, **kwargs) + _config = kwargs.pop("config", StackedConfig.default()) + client, path = get_transport_and_path(location, config=_config, **kwargs) with reraise( (NotGitRepository, KeyError), @@ -911,6 +913,7 @@ def validate_git_remote(self, url: str, **kwargs): try: _, location = get_remote_repo(self.repo, url) + _config = kwargs.pop("config", StackedConfig.default()) client, path = get_transport_and_path(location, **kwargs) except Exception as exc: raise InvalidRemote(url) from exc diff --git a/tests/test_git.py b/tests/test_git.py index 6e394c76..069d6204 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -1,5 +1,6 @@ import os import shutil +from pathlib import Path from typing import Any, Optional import pytest @@ -7,14 +8,20 @@ from asyncssh.connection import SSHClientConnection from dulwich.client import LocalGitClient from git import Repo as GitPythonRepo +from proxy import TestCase as ProxyTestCase from pygit2 import GitError from pygit2.remotes import Remote from pytest_mock import MockerFixture from pytest_test_utils import TempDirFactory, TmpDir from pytest_test_utils.matchers import Matcher -from scmrepo.exceptions import InvalidRemote, MergeConflictError, RevError, SCMError -from scmrepo.git import Git +from scmrepo.exceptions import ( + InvalidRemote, + MergeConflictError, + RevError, + SCMError, +) +from scmrepo.git import Git, GitBackends from scmrepo.git.objects import GitTag from .conftest import backends @@ -22,6 +29,13 @@ # pylint: disable=redefined-outer-name,unused-argument,protected-access +BAD_PROXY_CONFIG = """[http] +proxy = "http://bad-proxy.dvc.org" +[https] +proxy = "http://bad-proxy.dvc.org" +""" + + @pytest.fixture def submodule_dir(tmp_dir: TmpDir, scm: Git): scm.commit("init") @@ -941,6 +955,78 @@ def test_clone( assert fobj.read().strip() == "foo" +@pytest.fixture +def proxy_server(): + class _ProxyServer(ProxyTestCase): + pass + + _ProxyServer.setUpClass() + yield f"http://{_ProxyServer.PROXY.flags.hostname}:{_ProxyServer.PROXY.flags.port}" + _ProxyServer.tearDownClass() + + +@pytest.mark.allow_hosts(["127.0.0.1", "::1"]) +def test_clone_proxy_server(proxy_server: str, scm: Git, git: Git, tmp_dir: TmpDir): + url = "https://github.com/iterative/dvcyaml-schema" + + p = Path(os.environ["HOME"]) / ".gitconfig" + p.write_text(BAD_PROXY_CONFIG) + with pytest.raises(Exception): # noqa: PT011, B017 + git.clone(url, "dir") + + mock_config_content = f"""[http]\n +proxy = {proxy_server} +[https] +proxy = {proxy_server} +""" + + p.write_text(mock_config_content) + git.clone(url, "dir") + + +@pytest.mark.allow_hosts(["127.0.0.1", "::1"]) +def test_iter_remote_refs_proxy_server(proxy_server: str, scm: Git, tmp_dir: TmpDir): + url = "https://github.com/iterative/dvcyaml-schema" + git = GitBackends.DEFAULT.get("dulwich")(".") + + p = Path(os.environ["HOME"]) / ".gitconfig" + p.write_text(BAD_PROXY_CONFIG) + with pytest.raises(Exception): # noqa: PT011, B017 + list(git.iter_remote_refs(url)) + + mock_config_content = f"""[http] +proxy = {proxy_server} +[https] +proxy = {proxy_server} +""" + + p.write_text(mock_config_content) + res = list(git.iter_remote_refs(url)) + assert res + + +@pytest.mark.skip_git_backend("gitpython") +@pytest.mark.allow_hosts(["127.0.0.1", "::1"]) +def test_fetch_refspecs_proxy_server( + proxy_server: str, scm: Git, git: Git, tmp_dir: TmpDir +): + url = "https://github.com/iterative/dvcyaml-schema" + + p = Path(os.environ["HOME"]) / ".gitconfig" + p.write_text(BAD_PROXY_CONFIG) + with pytest.raises(Exception): # noqa: PT011, B017 + git.fetch_refspecs(url, ["refs/heads/master:refs/heads/master"]) + + mock_config_content = f"""[http] +proxy = {proxy_server} +[https] +proxy = {proxy_server} +""" + + p.write_text(mock_config_content) + git.fetch_refspecs(url, "refs/heads/master:refs/heads/master") + + @pytest.mark.skip_git_backend("pygit2") def test_fetch(tmp_dir: TmpDir, scm: Git, git: Git, tmp_dir_factory: TempDirFactory): tmp_dir.gen("foo", "foo")