Skip to content

Commit b97d042

Browse files
Dave Berenbaumpmrowla
andauthored
disallow cred helper to return empty output (#224)
* disallow cred helper to return empty output * check for empty credentials instead of empty output * add tests for credentialhelper --------- Co-authored-by: Peter Rowlands <[email protected]>
1 parent 23b23dc commit b97d042

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

src/scmrepo/git/credentials.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ def get(self, **kwargs) -> "Credential":
164164
credentials[key] = value
165165
except ValueError:
166166
continue
167+
if not credentials:
168+
raise CredentialNotFoundError("No credentials found")
167169
return Credential(**credentials)
168170

169171
def store(self, **kwargs):
@@ -367,6 +369,11 @@ def __init__(
367369
self.username = self.username or parsed.username
368370
self.password = self.password or parsed.password
369371

372+
def __eq__(self, other: object) -> bool:
373+
if isinstance(other, Credential):
374+
return self._helper_kwargs == other._helper_kwargs
375+
return False
376+
370377
@property
371378
def url(self) -> str:
372379
if self.username or self.password:

tests/test_credentials.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import os
2+
3+
import pytest
4+
5+
from scmrepo.git.credentials import (
6+
Credential,
7+
CredentialNotFoundError,
8+
GitCredentialHelper,
9+
)
10+
11+
12+
@pytest.fixture(name="helper")
13+
def helper_fixture(mocker) -> "GitCredentialHelper":
14+
mocker.patch("shutil.which", return_value="/usr/bin/git-credential-foo")
15+
return GitCredentialHelper("foo")
16+
17+
18+
def test_subprocess_get(helper, mocker):
19+
run = mocker.patch(
20+
"subprocess.run",
21+
return_value=mocker.Mock(
22+
stdout=os.linesep.join(
23+
["protocol=https", "host=foo.com", "username=foo", "password=bar", ""]
24+
)
25+
),
26+
)
27+
creds = helper.get(protocol="https", host="foo.com")
28+
assert run.call_args.args[0] == ["git-credential-foo", "get"]
29+
assert run.call_args.kwargs.get("input") == os.linesep.join(
30+
["protocol=https", "host=foo.com", ""]
31+
)
32+
assert creds == Credential(url="https://foo:[email protected]")
33+
34+
35+
def test_subprocess_get_failed(helper, mocker):
36+
from subprocess import CalledProcessError
37+
38+
mocker.patch("subprocess.run", side_effect=CalledProcessError(1, "/usr/bin/foo"))
39+
with pytest.raises(CredentialNotFoundError):
40+
helper.get(protocol="https", host="foo.com")
41+
42+
43+
def test_subprocess_get_no_output(helper, mocker):
44+
mocker.patch("subprocess.run", return_value=mocker.Mock(stdout=os.linesep))
45+
with pytest.raises(CredentialNotFoundError):
46+
helper.get(protocol="https", host="foo.com")
47+
48+
49+
def test_subprocess_store(helper, mocker):
50+
run = mocker.patch("subprocess.run")
51+
helper.store(protocol="https", host="foo.com", username="foo", password="bar")
52+
assert run.call_args.args[0] == ["git-credential-foo", "store"]
53+
assert run.call_args.kwargs.get("input") == os.linesep.join(
54+
["protocol=https", "host=foo.com", "username=foo", "password=bar", ""]
55+
)
56+
57+
58+
def test_subprocess_erase(helper, mocker):
59+
run = mocker.patch("subprocess.run")
60+
helper.erase(protocol="https", host="foo.com")
61+
assert run.call_args.args[0] == ["git-credential-foo", "erase"]
62+
assert run.call_args.kwargs.get("input") == os.linesep.join(
63+
["protocol=https", "host=foo.com", ""]
64+
)

0 commit comments

Comments
 (0)