Coverage for tests / unit / tools / shfmt / test_fix_method.py: 100%

37 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-05 16:51 +0000

1"""Tests for ShfmtPlugin.fix method initial_issues population.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6from unittest.mock import patch 

7 

8from assertpy import assert_that 

9 

10from lintro.tools.definitions.shfmt import ShfmtPlugin 

11 

12 

13def test_fix_populates_initial_issues( 

14 shfmt_plugin: ShfmtPlugin, 

15 tmp_path: Path, 

16) -> None: 

17 """Fix populates initial_issues when issues are found and fixed. 

18 

19 Args: 

20 shfmt_plugin: The ShfmtPlugin instance to test. 

21 tmp_path: Temporary directory path for test files. 

22 """ 

23 test_file = tmp_path / "test_script.sh" 

24 test_file.write_text( 

25 '#!/bin/bash\nif [ "$foo" = "bar" ]; then\necho "match"\nfi\n', 

26 ) 

27 

28 shfmt_diff_output = f"""--- {test_file} 

29+++ {test_file} 

30@@ -1,4 +1,4 @@ 

31 #!/bin/bash 

32-if [ "$foo" = "bar" ]; then 

33+if [ "$foo" = "bar" ]; then 

34 echo "match" 

35 fi""" 

36 

37 def mock_run_subprocess( 

38 cmd: list[str], 

39 timeout: int, 

40 cwd: str | None = None, 

41 ) -> tuple[bool, str]: 

42 """Mock subprocess that returns diff on check, success on fix. 

43 

44 Args: 

45 cmd: Command list. 

46 timeout: Timeout in seconds. 

47 cwd: Working directory. 

48 

49 Returns: 

50 Tuple of (success, output). 

51 """ 

52 if "-d" in cmd: 

53 return (False, shfmt_diff_output) 

54 return (True, "") 

55 

56 with patch( 

57 "lintro.plugins.execution_preparation.verify_tool_version", 

58 return_value=None, 

59 ): 

60 with patch.object( 

61 shfmt_plugin, 

62 "_run_subprocess", 

63 side_effect=mock_run_subprocess, 

64 ): 

65 result = shfmt_plugin.fix([str(test_file)], {}) 

66 

67 assert_that(result.success).is_true() 

68 assert_that(result.initial_issues).is_not_none() 

69 assert_that(result.initial_issues).is_not_empty() 

70 assert_that(result.initial_issues_count).is_greater_than(0) 

71 assert_that(result.fixed_issues_count).is_greater_than(0) 

72 assert_that(result.remaining_issues_count).is_equal_to(0) 

73 

74 

75def test_fix_initial_issues_none_when_no_issues( 

76 shfmt_plugin: ShfmtPlugin, 

77 tmp_path: Path, 

78) -> None: 

79 """Fix sets initial_issues to None when no issues detected. 

80 

81 Args: 

82 shfmt_plugin: The ShfmtPlugin instance to test. 

83 tmp_path: Temporary directory path for test files. 

84 """ 

85 test_file = tmp_path / "test_script.sh" 

86 test_file.write_text('#!/bin/bash\necho "hello"\n') 

87 

88 with patch( 

89 "lintro.plugins.execution_preparation.verify_tool_version", 

90 return_value=None, 

91 ): 

92 with patch.object( 

93 shfmt_plugin, 

94 "_run_subprocess", 

95 return_value=(True, ""), 

96 ): 

97 result = shfmt_plugin.fix([str(test_file)], {}) 

98 

99 assert_that(result.success).is_true() 

100 assert_that(result.initial_issues).is_none() 

101 

102 

103def test_fix_surfaces_check_subprocess_failure( 

104 shfmt_plugin: ShfmtPlugin, 

105 tmp_path: Path, 

106) -> None: 

107 """When shfmt -d exits non-zero with no diff, report it as an error. 

108 

109 Previously the missing success boolean meant a failed check was 

110 silently treated as a clean file. 

111 

112 Args: 

113 shfmt_plugin: The ShfmtPlugin instance to test. 

114 tmp_path: Temporary directory path for test files. 

115 """ 

116 test_file = tmp_path / "broken.sh" 

117 test_file.write_text("#!/bin/bash\nif then fi\n") 

118 

119 with patch( 

120 "lintro.plugins.execution_preparation.verify_tool_version", 

121 return_value=None, 

122 ): 

123 with patch.object( 

124 shfmt_plugin, 

125 "_run_subprocess", 

126 return_value=(False, "syntax error: unexpected 'then'"), 

127 ): 

128 result = shfmt_plugin.fix([str(test_file)], {}) 

129 

130 # Non-zero exit with no diff should NOT be treated as success 

131 assert_that(result.success).is_false()