Coverage for tests / unit / tools / ruff / fix / test_timeout.py: 100%

47 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Tests for execute_ruff_fix - Timeout scenarios.""" 

2 

3from __future__ import annotations 

4 

5import subprocess 

6from unittest.mock import MagicMock, patch 

7 

8from assertpy import assert_that 

9 

10from lintro.tools.implementations.ruff.fix import execute_ruff_fix 

11 

12 

13def test_execute_ruff_fix_timeout_on_initial_check( 

14 mock_ruff_tool: MagicMock, 

15) -> None: 

16 """Return timeout result when initial check times out. 

17 

18 Args: 

19 mock_ruff_tool: Mock RuffTool instance for testing. 

20 """ 

21 with patch( 

22 "lintro.tools.implementations.ruff.fix.walk_files_with_excludes", 

23 ) as mock_walk: 

24 mock_walk.return_value = ["test.py"] 

25 

26 mock_ruff_tool._run_subprocess.side_effect = subprocess.TimeoutExpired( 

27 cmd=["ruff", "check"], 

28 timeout=30, 

29 ) 

30 

31 result = execute_ruff_fix(mock_ruff_tool, ["test.py"]) 

32 

33 assert_that(result.success).is_false() 

34 assert_that(result.output).contains("timed out") 

35 assert_that(result.issues_count).is_equal_to(1) 

36 

37 

38def test_execute_ruff_fix_timeout_on_fix_command( 

39 mock_ruff_tool: MagicMock, 

40) -> None: 

41 """Return timeout result when fix command times out. 

42 

43 Args: 

44 mock_ruff_tool: Mock RuffTool instance for testing. 

45 """ 

46 single_issue_output = """[ 

47 { 

48 "code": "F401", 

49 "message": "os imported but unused", 

50 "filename": "test.py", 

51 "location": {"row": 1, "column": 1}, 

52 "end_location": {"row": 1, "column": 10}, 

53 "fix": {"applicability": "safe"} 

54 } 

55 ]""" 

56 

57 with patch( 

58 "lintro.tools.implementations.ruff.fix.walk_files_with_excludes", 

59 ) as mock_walk: 

60 mock_walk.return_value = ["test.py"] 

61 

62 mock_ruff_tool._run_subprocess.side_effect = [ 

63 (False, single_issue_output), # Initial check: 1 issue 

64 subprocess.TimeoutExpired(cmd=["ruff", "check", "--fix"], timeout=30), 

65 ] 

66 

67 result = execute_ruff_fix(mock_ruff_tool, ["test.py"]) 

68 

69 assert_that(result.success).is_false() 

70 assert_that(result.output).contains("timed out") 

71 

72 

73def test_execute_ruff_fix_timeout_on_format_check( 

74 mock_ruff_tool: MagicMock, 

75) -> None: 

76 """Return timeout result when format check times out. 

77 

78 Args: 

79 mock_ruff_tool: Mock RuffTool instance for testing. 

80 """ 

81 mock_ruff_tool.options["format"] = True 

82 

83 single_issue_output = """[ 

84 { 

85 "code": "F401", 

86 "message": "os imported but unused", 

87 "filename": "test.py", 

88 "location": {"row": 1, "column": 1}, 

89 "end_location": {"row": 1, "column": 10}, 

90 "fix": {"applicability": "safe"} 

91 } 

92 ]""" 

93 

94 with patch( 

95 "lintro.tools.implementations.ruff.fix.walk_files_with_excludes", 

96 ) as mock_walk: 

97 mock_walk.return_value = ["test.py"] 

98 

99 mock_ruff_tool._run_subprocess.side_effect = [ 

100 (False, single_issue_output), # Initial lint check: 1 issue 

101 subprocess.TimeoutExpired(cmd=["ruff", "format", "--check"], timeout=30), 

102 ] 

103 

104 result = execute_ruff_fix(mock_ruff_tool, ["test.py"]) 

105 

106 assert_that(result.success).is_false() 

107 assert_that(result.output).contains("timed out") 

108 

109 

110def test_execute_ruff_fix_timeout_on_format_command( 

111 mock_ruff_tool: MagicMock, 

112 sample_ruff_json_empty_output: str, 

113) -> None: 

114 """Return timeout result when format command times out. 

115 

116 Args: 

117 mock_ruff_tool: Mock RuffTool instance for testing. 

118 sample_ruff_json_empty_output: Sample empty JSON output from ruff. 

119 """ 

120 mock_ruff_tool.options["format"] = True 

121 

122 # Only 1 format issue so validation passes (1 = 0 + 1) 

123 single_format_issue = "Would reformat: test.py\n" 

124 

125 with patch( 

126 "lintro.tools.implementations.ruff.fix.walk_files_with_excludes", 

127 ) as mock_walk: 

128 mock_walk.return_value = ["test.py"] 

129 

130 mock_ruff_tool._run_subprocess.side_effect = [ 

131 (True, sample_ruff_json_empty_output), # Initial lint check: 0 issues 

132 (False, single_format_issue), # Format check: 1 file needs formatting 

133 (True, sample_ruff_json_empty_output), # Lint fix 

134 subprocess.TimeoutExpired(cmd=["ruff", "format"], timeout=30), # Format fix 

135 ] 

136 

137 result = execute_ruff_fix(mock_ruff_tool, ["test.py"]) 

138 

139 assert_that(result.success).is_false() 

140 assert_that(result.output).contains("timed out") 

141 

142 

143def test_execute_ruff_fix_timeout_on_unsafe_check_continues( 

144 mock_ruff_tool: MagicMock, 

145) -> None: 

146 """Continue execution when unsafe fix check times out. 

147 

148 Args: 

149 mock_ruff_tool: Mock RuffTool instance for testing. 

150 """ 

151 remaining_output = """[ 

152 { 

153 "code": "F841", 

154 "message": "Local variable 'x' is assigned but never used", 

155 "filename": "test.py", 

156 "location": {"row": 1, "column": 1}, 

157 "end_location": {"row": 1, "column": 5}, 

158 "fix": {"applicability": "unsafe"} 

159 } 

160 ]""" 

161 

162 with patch( 

163 "lintro.tools.implementations.ruff.fix.walk_files_with_excludes", 

164 ) as mock_walk: 

165 mock_walk.return_value = ["test.py"] 

166 

167 mock_ruff_tool._run_subprocess.side_effect = [ 

168 (False, remaining_output), # Initial check 

169 (False, remaining_output), # Fix attempt 

170 subprocess.TimeoutExpired(cmd=["ruff"], timeout=30), # Unsafe check timeout 

171 ] 

172 

173 result = execute_ruff_fix(mock_ruff_tool, ["test.py"]) 

174 

175 # Should still complete with remaining issues 

176 assert_that(result.success).is_false() 

177 assert_that(result.remaining_issues_count).is_equal_to(1)