Skip to content

Commit 52c3e20

Browse files
yjawLee-W
authored andcommitted
refactor(commit): refactor test case and logic for tag --body-length-limit in commit command
1 parent eff56e5 commit 52c3e20

6 files changed

Lines changed: 77 additions & 221 deletions

‎commitizen/commands/commit.py‎

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tempfile
88
from pathlib import Path
99
import textwrap
10+
from itertools import chain
1011
from typing import TYPE_CHECKING, TypedDict
1112

1213
import questionary
@@ -84,7 +85,7 @@ def _get_message_by_prompt_commit_questions(self) -> str:
8485

8586
message = self.cz.message(answers)
8687
self._validate_subject_length(message)
87-
message = self._rewrap_body(message)
88+
message = self._wrap_body(message)
8889
return message
8990

9091
def _validate_subject_length(self, message: str) -> None:
@@ -103,30 +104,23 @@ def _validate_subject_length(self, message: str) -> None:
103104
f"Length of commit message exceeds limit ({len(subject)}/{message_length_limit}), subject: '{subject}'"
104105
)
105106

106-
def _rewrap_body(self, message: str) -> str:
107+
def _wrap_body(self, message: str) -> str:
107108
body_length_limit = self.arguments.get(
108109
"body_length_limit", self.config.settings.get("body_length_limit", 0)
109110
)
110111
# By the contract, body_length_limit is set to 0 for no limit
111-
if (
112-
body_length_limit is None or body_length_limit <= 0
113-
): # do nothing for no limit
112+
if body_length_limit <= 0:
114113
return message
115114

116-
message_parts = message.split("\n", 2)
117-
if len(message_parts) < 3:
115+
lines = message.split("\n")
116+
if len(lines) < 3:
118117
return message
119118

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

131125
def manual_edit(self, message: str) -> str:
132126
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
@@ -377,227 +377,76 @@ def test_commit_command_with_config_message_length_limit(
377377

378378

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

510-
commit_mock = mocker.patch(
511-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
512-
)
513-
514-
# Set body_length_limit in config
515-
config.settings["body_length_limit"] = 50
516-
432+
config.settings["body_length_limit"] = body_length_limit
517433
commands.Commit(config, {})()
518434

519435
success_mock.assert_called_once()
520-
521-
# Get the actual commit message
522436
committed_message = commit_mock.call_args[0][0]
523437

524-
# Verify all body lines are within the limit
525-
lines = committed_message.split("\n")
526-
body_lines = lines[2:]
527-
for line in body_lines:
528-
if line.strip():
529-
assert len(line) <= 50, (
530-
f"Line exceeds 50 chars: '{line}' ({len(line)} chars)"
531-
)
532-
533-
534-
@pytest.mark.usefixtures("staging_is_clean")
535-
def test_commit_command_body_length_limit_cli_overrides_config(
536-
config, success_mock: MockType, mocker: MockFixture
537-
):
538-
"""Test that CLI argument overrides config setting."""
539-
mocker.patch(
540-
"questionary.prompt",
541-
return_value={
542-
"prefix": "feat",
543-
"subject": "add feature",
544-
"scope": "",
545-
"is_breaking_change": False,
546-
"body": "This is a line that is longer than 40 characters but shorter than 80 characters",
547-
"footer": "",
548-
},
438+
# File regression check - uses test_id to create separate files
439+
file_regression.check(
440+
committed_message,
441+
extension=".txt",
442+
basename=f"test_commit_command_body_length_limit_{test_id}",
549443
)
550444

551-
commit_mock = mocker.patch(
552-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
553-
)
554-
555-
# Set config to 40 (would wrap)
556-
config.settings["body_length_limit"] = 40
557-
558-
# Override with CLI argument to 0 (should NOT wrap)
559-
commands.Commit(config, {"body_length_limit": 0})()
560-
561-
success_mock.assert_called_once()
562-
563-
# Get the actual commit message
564-
committed_message = commit_mock.call_args[0][0]
565-
566-
# The line should NOT be wrapped (CLI override to 0 disables wrapping)
567-
assert (
568-
"This is a line that is longer than 40 characters but shorter than 80 characters"
569-
in committed_message
570-
)
571-
572-
573-
@pytest.mark.usefixtures("staging_is_clean")
574-
def test_commit_command_with_body_length_limit_no_body(
575-
config, success_mock: MockType, mocker: MockFixture
576-
):
577-
"""Test that commits without body work correctly with body_length_limit set."""
578-
mocker.patch(
579-
"questionary.prompt",
580-
return_value={
581-
"prefix": "feat",
582-
"subject": "add feature",
583-
"scope": "",
584-
"is_breaking_change": False,
585-
"body": "", # No body
586-
"footer": "",
587-
},
588-
)
589-
590-
commit_mock = mocker.patch(
591-
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
592-
)
593-
594-
# Execute commit with body_length_limit (should not crash)
595-
commands.Commit(config, {"body_length_limit": 72})()
596-
597-
success_mock.assert_called_once()
598-
599-
# Get the actual commit message
600-
committed_message = commit_mock.call_args[0][0]
601-
602-
# Should just be the subject line
603-
assert committed_message.strip() == "feat: add feature"
445+
# Validate line lengths if limit is not 0
446+
if body_length_limit > 0:
447+
lines = committed_message.split("\n")
448+
body_lines = lines[2:] # Skip subject and blank line
449+
for line in body_lines:
450+
assert len(line) <= body_length_limit, (
451+
f"Line exceeds {body_length_limit} chars: '{line}' ({len(line)} chars)"
452+
)
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)