Skip to content

Commit faacc87

Browse files
committed
refactor(commit): refactor test case and logic for tag --body-length-limit in commit command
1 parent 1f74bb2 commit faacc87

File tree

6 files changed

+77
-221
lines changed

6 files changed

+77
-221
lines changed

commitizen/commands/commit.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import subprocess
77
import tempfile
88
import textwrap
9+
from itertools import chain
910
from typing import TYPE_CHECKING, TypedDict
1011

1112
import questionary
@@ -86,7 +87,7 @@ def _get_message_by_prompt_commit_questions(self) -> str:
8687

8788
message = self.cz.message(answers)
8889
self._validate_subject_length(message)
89-
message = self._rewrap_body(message)
90+
message = self._wrap_body(message)
9091
return message
9192

9293
def _validate_subject_length(self, message: str) -> None:
@@ -105,30 +106,23 @@ def _validate_subject_length(self, message: str) -> None:
105106
f"Length of commit message exceeds limit ({len(subject)}/{message_length_limit}), subject: '{subject}'"
106107
)
107108

108-
def _rewrap_body(self, message: str) -> str:
109+
def _wrap_body(self, message: str) -> str:
109110
body_length_limit = self.arguments.get(
110111
"body_length_limit", self.config.settings.get("body_length_limit", 0)
111112
)
112113
# By the contract, body_length_limit is set to 0 for no limit
113-
if (
114-
body_length_limit is None or body_length_limit <= 0
115-
): # do nothing for no limit
114+
if body_length_limit <= 0:
116115
return message
117116

118-
message_parts = message.split("\n", 2)
119-
if len(message_parts) < 3:
117+
lines = message.split("\n")
118+
if len(lines) < 3:
120119
return message
121120

122121
# First line is subject, second is blank line, rest is body
123-
subject = message_parts[0]
124-
blank_line = message_parts[1]
125-
body = message_parts[2].strip()
126-
body_lines = body.split("\n")
127-
wrapped_body_lines = []
128-
for line in body_lines:
129-
wrapped_body_lines.append(textwrap.fill(line, width=body_length_limit))
130-
wrapped_body = "\n".join(wrapped_body_lines)
131-
return f"{subject}\n{blank_line}\n{wrapped_body}"
122+
wrapped_body_lines = [
123+
textwrap.wrap(line, width=body_length_limit) for line in lines[2:]
124+
]
125+
return "\n".join(chain(lines[:2], chain.from_iterable(wrapped_body_lines)))
132126

133127
def manual_edit(self, message: str) -> str:
134128
editor = git.get_core_editor()

tests/commands/test_commit_command.py

Lines changed: 54 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -368,227 +368,76 @@ def test_commit_command_with_config_message_length_limit(
368368

369369

370370
@pytest.mark.usefixtures("staging_is_clean")
371-
def test_commit_command_with_body_length_limit_wrapping(
372-
config, success_mock: MockType, mocker: MockFixture
373-
):
374-
"""Test that long body lines are automatically wrapped to the specified limit."""
375-
mocker.patch(
376-
"questionary.prompt",
377-
return_value={
378-
"prefix": "feat",
379-
"subject": "add feature",
380-
"scope": "",
381-
"is_breaking_change": False,
382-
"body": "This is a very long line that exceeds 72 characters and should be automatically wrapped by the system to fit within the limit",
383-
"footer": "",
384-
},
385-
)
386-
387-
commit_mock = mocker.patch(
388-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
389-
)
390-
391-
# Execute with body_length_limit
392-
commands.Commit(config, {"body_length_limit": 72})()
393-
success_mock.assert_called_once()
394-
395-
# Verify wrapping occurred
396-
committed_message = commit_mock.call_args[0][0]
397-
lines = committed_message.split("\n")
398-
assert lines[0] == "feat: add feature"
399-
assert lines[1] == ""
400-
body_lines = lines[2:]
401-
for line in body_lines:
402-
if line.strip():
403-
assert len(line) <= 72, (
404-
f"Line exceeds 72 chars: '{line}' ({len(line)} chars)"
405-
)
406-
407-
408-
@pytest.mark.usefixtures("staging_is_clean")
409-
def test_commit_command_with_body_length_limit_preserves_line_breaks(
410-
config, success_mock: MockType, mocker: MockFixture
411-
):
412-
"""Test that intentional line breaks (from | character) are preserved."""
413-
# Simulate what happens after multiple_line_breaker processes "line1 | line2 | line3"
414-
mocker.patch(
415-
"questionary.prompt",
416-
return_value={
417-
"prefix": "feat",
418-
"subject": "add feature",
419-
"scope": "",
420-
"is_breaking_change": False,
421-
"body": "Line1 that is very long and exceeds the limit\nLine2 that is very long and exceeds the limit\nLine3 that is very long and exceeds the limit",
422-
"footer": "",
423-
},
424-
)
425-
426-
commit_mock = mocker.patch(
427-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
428-
)
429-
430-
commands.Commit(config, {"body_length_limit": 45})()
431-
success_mock.assert_called_once()
432-
433-
committed_message = commit_mock.call_args[0][0]
434-
lines = committed_message.split("\n")
435-
436-
# Should have a subject, a blank line
437-
assert lines[0] == "feat: add feature"
438-
assert lines[1] == ""
439-
# Each original line should be wrapped separately, preserving the line breaks
440-
body_lines = lines[2:]
441-
# All lines should be <= 45 chars
442-
for line in body_lines:
443-
if line.strip():
444-
assert len(line) == 45, (
445-
f"Line's length is not 45 chars: '{line}' ({len(line)} chars)"
446-
)
447-
448-
449-
@pytest.mark.usefixtures("staging_is_clean")
450-
def test_commit_command_with_body_length_limit_disabled(
451-
config, success_mock: MockType, mocker: MockFixture
452-
):
453-
"""Test that body_length_limit = 0 disables wrapping."""
454-
long_body = "This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0"
455-
456-
mocker.patch(
457-
"questionary.prompt",
458-
return_value={
459-
"prefix": "feat",
460-
"subject": "add feature",
461-
"scope": "",
462-
"is_breaking_change": False,
463-
"body": long_body,
464-
"footer": "",
465-
},
466-
)
467-
468-
commit_mock = mocker.patch(
469-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
470-
)
471-
472-
# Execute with body_length_limit = 0 (disabled)
473-
commands.Commit(config, {"body_length_limit": 0})()
474-
475-
success_mock.assert_called_once()
476-
477-
# Get the actual commit message
478-
committed_message = commit_mock.call_args[0][0]
479-
480-
# Verify the body was NOT wrapped (should contain the original long line)
481-
assert long_body in committed_message, "Body should not be wrapped when limit is 0"
482-
483-
484-
@pytest.mark.usefixtures("staging_is_clean")
485-
def test_commit_command_with_body_length_limit_from_config(
486-
config, success_mock: MockType, mocker: MockFixture
371+
@pytest.mark.parametrize(
372+
"test_id,body,body_length_limit",
373+
[
374+
# Basic wrapping - long line gets wrapped
375+
(
376+
"wrapping",
377+
"This is a very long line that exceeds 72 characters and should be automatically wrapped by the system to fit within the limit",
378+
72,
379+
),
380+
# Line break preservation - multiple lines with \n
381+
(
382+
"preserves_line_breaks",
383+
"Line1 that is very long and exceeds the limit\nLine2 that is very long and exceeds the limit\nLine3 that is very long and exceeds the limit",
384+
72,
385+
),
386+
# Disabled wrapping - limit = 0
387+
(
388+
"disabled",
389+
"This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0",
390+
0,
391+
),
392+
# No body - empty string
393+
(
394+
"no_body",
395+
"",
396+
72,
397+
),
398+
],
399+
)
400+
def test_commit_command_body_length_limit(
401+
test_id,
402+
body,
403+
body_length_limit,
404+
config,
405+
success_mock: MockType,
406+
commit_mock,
407+
mocker: MockFixture,
408+
file_regression,
487409
):
488-
"""Test that body_length_limit can be set via config."""
410+
"""Parameterized test for body_length_limit feature with file regression."""
489411
mocker.patch(
490412
"questionary.prompt",
491413
return_value={
492414
"prefix": "feat",
493415
"subject": "add feature",
494416
"scope": "",
495417
"is_breaking_change": False,
496-
"body": "This is a very long line that exceeds 50 characters and should be wrapped",
418+
"body": body,
497419
"footer": "",
498420
},
499421
)
500422

501-
commit_mock = mocker.patch(
502-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
503-
)
504-
505-
# Set body_length_limit in config
506-
config.settings["body_length_limit"] = 50
507-
423+
config.settings["body_length_limit"] = body_length_limit
508424
commands.Commit(config, {})()
509425

510426
success_mock.assert_called_once()
511-
512-
# Get the actual commit message
513427
committed_message = commit_mock.call_args[0][0]
514428

515-
# Verify all body lines are within the limit
516-
lines = committed_message.split("\n")
517-
body_lines = lines[2:]
518-
for line in body_lines:
519-
if line.strip():
520-
assert len(line) <= 50, (
521-
f"Line exceeds 50 chars: '{line}' ({len(line)} chars)"
522-
)
523-
524-
525-
@pytest.mark.usefixtures("staging_is_clean")
526-
def test_commit_command_body_length_limit_cli_overrides_config(
527-
config, success_mock: MockType, mocker: MockFixture
528-
):
529-
"""Test that CLI argument overrides config setting."""
530-
mocker.patch(
531-
"questionary.prompt",
532-
return_value={
533-
"prefix": "feat",
534-
"subject": "add feature",
535-
"scope": "",
536-
"is_breaking_change": False,
537-
"body": "This is a line that is longer than 40 characters but shorter than 80 characters",
538-
"footer": "",
539-
},
429+
# File regression check - uses test_id to create separate files
430+
file_regression.check(
431+
committed_message,
432+
extension=".txt",
433+
basename=f"test_commit_command_body_length_limit_{test_id}",
540434
)
541435

542-
commit_mock = mocker.patch(
543-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
544-
)
545-
546-
# Set config to 40 (would wrap)
547-
config.settings["body_length_limit"] = 40
548-
549-
# Override with CLI argument to 0 (should NOT wrap)
550-
commands.Commit(config, {"body_length_limit": 0})()
551-
552-
success_mock.assert_called_once()
553-
554-
# Get the actual commit message
555-
committed_message = commit_mock.call_args[0][0]
556-
557-
# The line should NOT be wrapped (CLI override to 0 disables wrapping)
558-
assert (
559-
"This is a line that is longer than 40 characters but shorter than 80 characters"
560-
in committed_message
561-
)
562-
563-
564-
@pytest.mark.usefixtures("staging_is_clean")
565-
def test_commit_command_with_body_length_limit_no_body(
566-
config, success_mock: MockType, mocker: MockFixture
567-
):
568-
"""Test that commits without body work correctly with body_length_limit set."""
569-
mocker.patch(
570-
"questionary.prompt",
571-
return_value={
572-
"prefix": "feat",
573-
"subject": "add feature",
574-
"scope": "",
575-
"is_breaking_change": False,
576-
"body": "", # No body
577-
"footer": "",
578-
},
579-
)
580-
581-
commit_mock = mocker.patch(
582-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
583-
)
584-
585-
# Execute commit with body_length_limit (should not crash)
586-
commands.Commit(config, {"body_length_limit": 72})()
587-
588-
success_mock.assert_called_once()
589-
590-
# Get the actual commit message
591-
committed_message = commit_mock.call_args[0][0]
592-
593-
# Should just be the subject line
594-
assert committed_message.strip() == "feat: add feature"
436+
# Validate line lengths if limit is not 0
437+
if body_length_limit > 0:
438+
lines = committed_message.split("\n")
439+
body_lines = lines[2:] # Skip subject and blank line
440+
for line in body_lines:
441+
assert len(line) <= body_length_limit, (
442+
f"Line exceeds {body_length_limit} chars: '{line}' ({len(line)} chars)"
443+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
feat: add feature
2+
3+
This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
feat: add feature
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
feat: add feature
2+
3+
Line1 that is very long and exceeds the limit
4+
Line2 that is very long and exceeds the limit
5+
Line3 that is very long and exceeds the limit
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
feat: add feature
2+
3+
This is a very long line that exceeds 72 characters and should be
4+
automatically wrapped by the system to fit within the limit

0 commit comments

Comments
 (0)