Coverage for tests / unit / ai / test_hook.py: 100%

58 statements  

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

1"""Tests for AI post-execution hook.""" 

2 

3from __future__ import annotations 

4 

5from unittest.mock import MagicMock, patch 

6 

7from assertpy import assert_that 

8 

9from lintro.ai.config import AIConfig 

10from lintro.ai.hook import AIPostExecutionHook 

11from lintro.config.lintro_config import LintroConfig 

12from lintro.enums.action import Action 

13from lintro.models.core.tool_result import ToolResult 

14from tests.unit.ai.conftest import MockIssue 

15 

16# --------------------------------------------------------------------------- 

17# TestShouldRun 

18# --------------------------------------------------------------------------- 

19 

20 

21def test_should_run_returns_true_for_check_when_enabled(): 

22 """Verify should_run returns True for CHECK action when AI is enabled.""" 

23 config = LintroConfig(ai=AIConfig(enabled=True)) 

24 hook = AIPostExecutionHook(config) 

25 

26 result = hook.should_run(Action.CHECK) 

27 

28 assert_that(result).is_true() 

29 

30 

31def test_should_run_returns_true_for_fix_when_enabled(): 

32 """Verify should_run returns True for FIX action when AI is enabled.""" 

33 config = LintroConfig(ai=AIConfig(enabled=True)) 

34 hook = AIPostExecutionHook(config) 

35 

36 result = hook.should_run(Action.FIX) 

37 

38 assert_that(result).is_true() 

39 

40 

41def test_should_run_returns_false_for_test_action(): 

42 """Verify should_run returns False for TEST action even when AI is enabled.""" 

43 config = LintroConfig(ai=AIConfig(enabled=True)) 

44 hook = AIPostExecutionHook(config) 

45 

46 result = hook.should_run(Action.TEST) 

47 

48 assert_that(result).is_false() 

49 

50 

51def test_should_run_returns_false_when_disabled(): 

52 """Verify should_run returns False when AI is disabled.""" 

53 config = LintroConfig(ai=AIConfig(enabled=False)) 

54 hook = AIPostExecutionHook(config) 

55 

56 result = hook.should_run(Action.CHECK) 

57 

58 assert_that(result).is_false() 

59 

60 

61# --------------------------------------------------------------------------- 

62# TestExecute 

63# --------------------------------------------------------------------------- 

64 

65 

66@patch("lintro.ai.orchestrator.run_ai_enhancement") 

67def test_execute_calls_run_ai_enhancement(mock_run_ai_enhancement): 

68 """Verify execute delegates to run_ai_enhancement with correct arguments.""" 

69 config = LintroConfig(ai=AIConfig(enabled=True)) 

70 hook = AIPostExecutionHook(config, ai_fix=True) 

71 console_logger = MagicMock() 

72 results = [ 

73 ToolResult( 

74 name="ruff", 

75 success=False, 

76 issues_count=1, 

77 issues=[ 

78 MockIssue( 

79 file="src/main.py", 

80 line=1, 

81 message="Use of assert", 

82 code="B101", 

83 ), 

84 ], 

85 ), 

86 ] 

87 

88 hook.execute( 

89 action=Action.CHECK, 

90 all_results=results, 

91 console_logger=console_logger, 

92 output_format="json", 

93 ) 

94 

95 mock_run_ai_enhancement.assert_called_once_with( 

96 action=Action.CHECK, 

97 all_results=results, 

98 lintro_config=config, 

99 logger=console_logger, 

100 output_format="json", 

101 ai_fix=True, 

102 ) 

103 

104 

105@patch("lintro.ai.orchestrator.run_ai_enhancement") 

106def test_execute_catches_exceptions_and_logs_warning(mock_run_ai_enhancement): 

107 """Exceptions don't propagate; warning is logged.""" 

108 mock_run_ai_enhancement.side_effect = RuntimeError("provider exploded") 

109 config = LintroConfig(ai=AIConfig(enabled=True)) 

110 hook = AIPostExecutionHook(config) 

111 console_logger = MagicMock() 

112 results = [ 

113 ToolResult( 

114 name="ruff", 

115 success=False, 

116 issues_count=1, 

117 issues=[ 

118 MockIssue( 

119 file="src/main.py", 

120 line=1, 

121 message="err", 

122 code="E501", 

123 ), 

124 ], 

125 ), 

126 ] 

127 

128 hook.execute( 

129 action=Action.CHECK, 

130 all_results=results, 

131 console_logger=console_logger, 

132 output_format="terminal", 

133 ) 

134 

135 console_logger.warning.assert_called_once() 

136 warning_msg = console_logger.warning.call_args[0][0] 

137 assert_that(warning_msg).contains("provider exploded") 

138 

139 

140def test_execute_handles_import_failure(): 

141 """Verify graceful handling when the lazy import of run_ai_enhancement fails.""" 

142 config = LintroConfig(ai=AIConfig(enabled=True)) 

143 hook = AIPostExecutionHook(config) 

144 console_logger = MagicMock() 

145 results = [ 

146 ToolResult( 

147 name="ruff", 

148 success=False, 

149 issues_count=1, 

150 issues=[ 

151 MockIssue( 

152 file="src/main.py", 

153 line=1, 

154 message="err", 

155 code="E501", 

156 ), 

157 ], 

158 ), 

159 ] 

160 

161 with patch.dict( 

162 "sys.modules", 

163 {"lintro.ai.orchestrator": None}, 

164 ): 

165 hook.execute( 

166 action=Action.CHECK, 

167 all_results=results, 

168 console_logger=console_logger, 

169 output_format="terminal", 

170 ) 

171 

172 console_logger.warning.assert_called_once() 

173 warning_msg = console_logger.warning.call_args[0][0] 

174 assert_that(warning_msg).contains("AI enhancement unavailable")