Skip to content

Commit 53d0601

Browse files
authored
Merge pull request #7 from aj3sh/pytest-setup
test: added pytest tests for commitlint and cli
2 parents 5dc0e83 + 401a172 commit 53d0601

10 files changed

+293
-5
lines changed

.pre-commit-config.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
repos:
22
- repo: local
33
hooks:
4+
- id: run-tests
5+
name: Run tests
6+
entry: pipenv run test
7+
language: system
8+
pass_filenames: false
9+
410
- id: commitlint
511
name: Commitlint
612
entry: python -m src.commitlint.cli --file

CONTRIBUTING.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Install Development Dependencies (Using Pipenv)
44

5-
All the dependencies are managed by Pipenv. Please install Pipenv on your system first by following the instructions at [https://pipenv.pypa.io/en/latest/installation/](https://pipenv.pypa.io/en/latest/installation/).
5+
All the dependencies are managed by Pipenv. Please install Pipenv on your system first by following the instructions at [https://pipenv.pypa.io/en/latest/installation.html](https://pipenv.pypa.io/en/latest/installation.html).
66

77
Once Pipenv is installed, you can install the development dependencies by running the following command:
88

@@ -17,3 +17,11 @@ To install pre-commit and commit-msg hook for this project, run the following co
1717
```bash
1818
pipenv run install-hooks
1919
```
20+
21+
## Run tests
22+
23+
Run the tests using the below command:
24+
25+
```bash
26+
pipenv run test
27+
```

Pipfile

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ pytest-cov = "*"
1313
[scripts]
1414
test = "pytest"
1515
coverage = "pytest --cov=src/ --no-cov-on-fail"
16+
coverage-html = "pytest --cov=src/ --cov-report=html --no-cov-on-fail"
1617
install-hooks = "pre-commit install --hook-type pre-commit --hook-type commit-msg"

src/commitlint/cli.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ def main() -> None:
8282
if success:
8383
sys.stdout.write(f"{COMMIT_SUCCESSFUL}\n")
8484
sys.exit(0)
85-
86-
_show_errors(errors)
87-
sys.exit(1)
85+
else:
86+
_show_errors(errors)
87+
sys.exit(1)
8888

8989

9090
if __name__ == "__main__":
91-
main()
91+
main() # pragma: no cover

tests/__init__.py

Whitespace-only changes.

tests/test_cli.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# type: ignore
2+
# pylint: disable=all
3+
4+
from unittest.mock import MagicMock, call, mock_open, patch
5+
6+
from src.commitlint.cli import get_args, main
7+
from src.commitlint.messages import COMMIT_SUCCESSFUL, INCORRECT_FORMAT_ERROR
8+
9+
10+
class TestCLI:
11+
@patch(
12+
"argparse.ArgumentParser.parse_args",
13+
return_value=MagicMock(commit_message="commit message", file=None),
14+
)
15+
def test__get_args__with_commit_message(self, *_):
16+
args = get_args()
17+
assert args.commit_message == "commit message"
18+
assert args.file is None
19+
20+
@patch(
21+
"argparse.ArgumentParser.parse_args",
22+
return_value=MagicMock(commit_message=None, file="path/to/file.txt"),
23+
)
24+
def test__get_args__with_file(self, *_):
25+
args = get_args()
26+
assert args.file == "path/to/file.txt"
27+
assert args.commit_message is None
28+
29+
@patch("argparse.ArgumentParser.error")
30+
def test__get_args__without_commit_message_and_file(self, mock_error):
31+
get_args()
32+
mock_error.assert_called_with(
33+
"Please provide either a commit message or a file."
34+
)
35+
36+
@patch(
37+
"src.commitlint.cli.get_args",
38+
return_value=MagicMock(commit_message="feat: valid commit message", file=None),
39+
)
40+
@patch("sys.stdout.write")
41+
@patch("sys.exit")
42+
def test__main__valid_commit_message(
43+
self,
44+
mock_sys_exit,
45+
mock_stdout_write,
46+
*_,
47+
):
48+
main()
49+
mock_sys_exit.assert_called_with(0)
50+
mock_stdout_write.assert_called_with(f"{COMMIT_SUCCESSFUL}\n")
51+
52+
@patch(
53+
"src.commitlint.cli.get_args",
54+
return_value=MagicMock(commit_message="Invalid commit message", file=None),
55+
)
56+
@patch("sys.stderr.write")
57+
@patch("sys.exit")
58+
def test__main__invalid_commit_message(
59+
self,
60+
mock_sys_exit,
61+
mock_stderr_write,
62+
*_,
63+
):
64+
main()
65+
mock_sys_exit.assert_called_with(1)
66+
mock_stderr_write.assert_has_calls(
67+
[call("✖ Found 1 errors.\n\n"), call(f"- {INCORRECT_FORMAT_ERROR}\n\n")]
68+
)
69+
70+
@patch(
71+
"src.commitlint.cli.get_args",
72+
return_value=MagicMock(file="path/to/file.txt", commit_message=None),
73+
)
74+
@patch("sys.stdout.write")
75+
@patch("sys.exit")
76+
@patch("builtins.open", mock_open(read_data="feat: valid commit message"))
77+
def test__main__valid_commit_message_from_file(
78+
self, mock_sys_exit, mock_stdout_write, *_
79+
):
80+
main()
81+
mock_sys_exit.assert_called_with(0)
82+
mock_stdout_write.assert_called_with(f"{COMMIT_SUCCESSFUL}\n")
83+
84+
@patch(
85+
"src.commitlint.cli.get_args",
86+
return_value=MagicMock(file="path/to/file.txt", commit_message=None),
87+
)
88+
@patch("sys.stderr.write")
89+
@patch("sys.exit")
90+
@patch("builtins.open", mock_open(read_data="Invalid commit message"))
91+
def test__main__invalid_commit_message_from_file(
92+
self, mock_sys_exit, mock_stderr_write, *_
93+
):
94+
main()
95+
mock_sys_exit.assert_called_with(1)
96+
mock_stderr_write.assert_has_calls(
97+
[call("✖ Found 1 errors.\n\n"), call(f"- {INCORRECT_FORMAT_ERROR}\n\n")]
98+
)

tests/test_commitlint/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# type: ignore
2+
# pylint: disable=all
3+
4+
from src.commitlint import check_commit_message
5+
from src.commitlint.constants import COMMIT_MAX_LENGTH
6+
from src.commitlint.messages import HEADER_LENGTH_ERROR, INCORRECT_FORMAT_ERROR
7+
8+
9+
def test__check_commit_message__header_length_error():
10+
commit_message = "feat: " + "a" * (COMMIT_MAX_LENGTH + 1)
11+
success, errors = check_commit_message(commit_message)
12+
assert success is False
13+
assert HEADER_LENGTH_ERROR in errors
14+
15+
16+
def test__check_commit_message__header_length_valid():
17+
commit_message = "feat: " + "a" * (COMMIT_MAX_LENGTH - 1)
18+
success, errors = check_commit_message(commit_message)
19+
assert success is False
20+
assert HEADER_LENGTH_ERROR in errors
21+
22+
23+
def test__check_commit_message__incorrect_format_error():
24+
commit_message = "This is an invalid commit message"
25+
success, errors = check_commit_message(commit_message)
26+
assert success is False
27+
assert INCORRECT_FORMAT_ERROR in errors
28+
29+
30+
def test__check_commit_message__incorrect_format_error_and_health_length_invalid():
31+
commit_message = "Test " + "a" * (COMMIT_MAX_LENGTH + 1)
32+
success, errors = check_commit_message(commit_message)
33+
assert success is False
34+
assert HEADER_LENGTH_ERROR in errors
35+
assert INCORRECT_FORMAT_ERROR in errors
36+
37+
38+
def test__check_commit_message__valid():
39+
commit_message = "feat: add new feature"
40+
success, errors = check_commit_message(commit_message)
41+
assert success is True
42+
assert errors == []
43+
44+
45+
def test__check_commit_message__valid_with_scope():
46+
commit_message = "feat(scope): add new feature"
47+
success, errors = check_commit_message(commit_message)
48+
assert success is True
49+
assert errors == []
50+
51+
52+
def test__check_commit_message__empty_scope_error():
53+
commit_message = "feat(): add new feature"
54+
success, errors = check_commit_message(commit_message)
55+
assert success is False
56+
assert INCORRECT_FORMAT_ERROR in errors
57+
58+
59+
def test__check_commit_message__valid_with_body():
60+
commit_message = "fix(scope): fix a bug\n\nThis is the body of the commit message."
61+
success, errors = check_commit_message(commit_message)
62+
assert success is True
63+
assert errors == []
64+
65+
66+
def test__check_commit_message__header_line_error():
67+
commit_message = "feat(): add new feature\ntest"
68+
success, errors = check_commit_message(commit_message)
69+
assert success is False
70+
assert INCORRECT_FORMAT_ERROR in errors
71+
72+
73+
def test__check_commit_message__with_comments():
74+
commit_message = "feat(scope): add new feature\n#this is a comment"
75+
success, errors = check_commit_message(commit_message)
76+
assert success is True
77+
assert errors == []
78+
79+
80+
def test__check_commit_message__with_diff():
81+
commit_message = (
82+
"fix: fixed a bug\n\nthis is body\n"
83+
"# ------------------------ >8 ------------------------\nDiff message"
84+
)
85+
success, errors = check_commit_message(commit_message)
86+
assert success is True
87+
assert errors == []
88+
89+
90+
def test__check_commit_message__ignored():
91+
commit_message = "Merge pull request #123"
92+
success, errors = check_commit_message(commit_message)
93+
assert success is True
94+
assert errors == []
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# type: ignore
2+
# pylint: disable=all
3+
4+
import pytest
5+
6+
from src.commitlint.commitlint import is_ignored
7+
8+
9+
@pytest.mark.parametrize(
10+
"commit_message, expected_result",
11+
[
12+
("Merge pull request #123", True),
13+
("Merge feature-branch into production", True),
14+
("Merge branch hotfix-123", True),
15+
("Merge tag release-v2.0.1", True),
16+
('Revert "Undo last commit"', True),
17+
("revert Fix-Typo", True),
18+
("Merged bugfix-789 in master", True),
19+
("Merged PR #987: Update documentation", True),
20+
("Merge remote-tracking branch upstream/develop", True),
21+
("Automatic merge from CI/CD", True),
22+
("Auto-merged feature-branch into staging", True),
23+
("Merge tag v3.5.0", True),
24+
("Merge pull request #456: Feature XYZ", True),
25+
('Revert "Apply security patch"', True),
26+
("Merged PR #321: Bugfix - Resolve issue with login", True),
27+
("Merge my feature", False),
28+
("Add new feature", False),
29+
("feat: this is conventional commit format", False),
30+
],
31+
)
32+
def test__is_ignored(commit_message, expected_result):
33+
result = is_ignored(commit_message)
34+
assert result == expected_result
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# type: ignore
2+
# pylint: disable=all
3+
4+
from src.commitlint.commitlint import remove_comments
5+
6+
7+
def test__remove_comments__no_comments():
8+
input_msg = "Commit message without comments"
9+
expected_output = "Commit message without comments"
10+
result = remove_comments(input_msg)
11+
assert result == expected_output
12+
13+
14+
def test__remove_comments__with_comments():
15+
input_msg = "# Comment\nRegular text"
16+
expected_output = "Regular text"
17+
result = remove_comments(input_msg)
18+
assert result == expected_output
19+
20+
21+
def test__remove_comments__with_diff_message():
22+
input_msg = (
23+
"Fix a bug\n"
24+
"# ------------------------ >8 ------------------------\n"
25+
"Diff message"
26+
)
27+
expected_output = "Fix a bug"
28+
result = remove_comments(input_msg)
29+
assert result == expected_output
30+
31+
32+
def test__remove_comments__multiple_comments():
33+
input_msg = "New feature\n# Comment\n# Another comment"
34+
expected_output = "New feature"
35+
result = remove_comments(input_msg)
36+
assert result == expected_output
37+
38+
39+
def test__remove_comments__comments_before_diff():
40+
input_msg = (
41+
"#Comments\n"
42+
"# ------------------------ >8 ------------------------\n"
43+
"Diff message"
44+
)
45+
expected_output = ""
46+
result = remove_comments(input_msg)
47+
assert result == expected_output

0 commit comments

Comments
 (0)