Repository: ivanklee86/gitsecret

Issue:
Implement git secret commands to show information about repo
Implement functionality & tests for:
- [x] git-secret-list
- [x] git-secret-changes

Discussion:
coveralls: ## Pull Request Test Coverage Report for [Build 35](https://coveralls.io/builds/19797952)

* **15** of **16**   **(93.75%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage decreased (**-1.0%**) to **99.0%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [gitsecret/__init__.py](https://coveralls.io/builds/19797952/source?filename=gitsecret%2F__init__.py#L171) | 15 | 16 | 93.75%
<!-- | **Total:** | **15** | **16** | **93.75%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/19797952/badge)](https://coveralls.io/builds/19797952) |
| :-- | --: |
| Change from base [Build 33](https://coveralls.io/builds/19794776): |  -1.0% |
| Covered Lines: | 99 |
| Relevant Lines: | 100 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/gitsecret/__init__.py b/gitsecret/__init__.py
index df097f2..12409d9 100644
--- a/gitsecret/__init__.py
+++ b/gitsecret/__init__.py
@@ -1,7 +1,7 @@
 import re
 import shlex
 import subprocess
-from typing import Optional, Tuple
+from typing import Optional, Tuple, List
 import git
 
 
@@ -18,7 +18,7 @@ class GitSecret():
         self.git_repo = git.Repo(self.repo_path)
 
     def _command_and_parse(self,
-                           command: list,
+                           command: list,  # pylint: disable=E0601
                            regex: Optional[str] = None) -> Tuple[subprocess.CompletedProcess, list]:
         """
         A (inflexible, implementation-focused) centralized method to run a method and optionally applies a regex to the
@@ -144,7 +144,31 @@ class GitSecret():
 
         self._command_and_parse(remove_command, remove_regex)
 
-    def clean(self) -> list:
+    def clean(self) -> Optional[List]:
         clean_command = shlex.split("git secret clean -v")
 
         return self._command_and_split(clean_command)[1:]
+
+    def list(self) -> Optional[List]:
+        list_command = shlex.split("git secret list")
+
+        return self._command_and_split(list_command)[:]
+
+    def changes(self, password: str, file_path: Optional[str] = None, gpg_path: Optional[str] = None) -> str:
+        changes_command = shlex.split("git secret changes")
+
+        if gpg_path:
+            changes_command.extend(["-d", gpg_path])
+
+        changes_command.extend(["-p", password])
+
+        if file_path:
+            changes_command.append(file_path)
+
+        output = subprocess.run(args=changes_command, capture_output=True, cwd=self.repo_path)
+
+        if output.returncode != 0:
+            raise GitSecretException("Error running git secret command.  stdout: %s; stderr: %s" %
+                                     (output.stdout.decode("utf-8"), output.stderr.decode("utf-8")))
+
+        return str(output.stdout.decode("utf-8"))


Test patch:
diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py
index f2f4681..67e5641 100644
--- a/tests/e2e/test_e2e.py
+++ b/tests/e2e/test_e2e.py
@@ -52,4 +52,7 @@ def test_gitsecret_files(gen_gitsecret):
     gitsecret.hide()
     gitsecret.reveal("test")
     # gitsecret.remove("hello.txt")
+    with open(os.path.join(PATH, "hello.txt"), "a") as test_file:
+        test_file.write("Hello again!")
+    assert "Hello again!" in gitsecret.changes("test")
     assert len(gitsecret.clean()) == 1
diff --git a/tests/unit/test_gitsecret_changes.py b/tests/unit/test_gitsecret_changes.py
new file mode 100644
index 0000000..1b8c02d
--- /dev/null
+++ b/tests/unit/test_gitsecret_changes.py
@@ -0,0 +1,78 @@
+import pytest
+import gitsecret
+from gitsecret import GitSecretException
+from tests.utilities.factories import FakeCompletedProcess
+from tests.utilities.fixtures import gen_gitsecret  # noqa: F401
+
+
+def test_gitsecret_changes(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes("test")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-p", "test"]
+
+
+def test_gitsecret_changes_path(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   file_path="/path/to/file")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-p", "test", "/path/to/file"]
+
+
+def test_gitsecret_changes_gpgpath(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   gpg_path="/path/to/key")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-d", "/path/to/key", "-p", "test"]
+
+
+def test_gitsecret_changes_all(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   gpg_path="/path/to/key",
+                                                   file_path="/path/to/file")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-d", "/path/to/key", "-p", "test", "/path/to/file"]
+
+
+def test_gitsecret_changes_exception(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "None",
+        'stderr': "git-secret: abort: /Users/ivanlee/repos/sandbox/box5/.gitsecret/paths/mapping.cfg is missing",
+        'returncode': 1
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    with pytest.raises(GitSecretException):
+        gen_gitsecret.changes("test")
diff --git a/tests/unit/test_gitsecret_list.py b/tests/unit/test_gitsecret_list.py
new file mode 100644
index 0000000..5d65e5d
--- /dev/null
+++ b/tests/unit/test_gitsecret_list.py
@@ -0,0 +1,32 @@
+import pytest
+import gitsecret
+from gitsecret import GitSecretException
+from tests.utilities.factories import FakeCompletedProcess
+from tests.utilities.fixtures import gen_gitsecret  # noqa: F401
+
+
+def test_gitsecret_list(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nhello.txt\nhello1.txt\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert gen_gitsecret.list() == ["hello.txt", "hello1.txt"]
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "list"]
+
+
+def test_gitsecret_list_exception(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "None",
+        'stderr': "git-secret: abort: /Users/ivanlee/repos/sandbox/box5/.gitsecret/paths/mapping.cfg is missing",
+        'returncode': 1
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    with pytest.raises(GitSecretException):
+        gen_gitsecret.list()