Coverage for tests / unit / tools / tsc / test_execution.py: 100%

51 statements  

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

1"""Unit tests for tsc plugin execution.""" 

2 

3from __future__ import annotations 

4 

5import subprocess 

6from pathlib import Path 

7from unittest.mock import patch 

8 

9import pytest 

10from assertpy import assert_that 

11 

12from lintro.tools.definitions.tsc import TscPlugin 

13 

14# ============================================================================= 

15# Tests for TscPlugin.check method 

16# ============================================================================= 

17 

18 

19def test_check_with_mocked_subprocess_success( 

20 tsc_plugin: TscPlugin, 

21 tmp_path: Path, 

22) -> None: 

23 """Check returns success when no issues found. 

24 

25 Args: 

26 tsc_plugin: The TscPlugin instance to test. 

27 tmp_path: Temporary directory path for test files. 

28 """ 

29 test_file = tmp_path / "main.ts" 

30 test_file.write_text("const x: number = 42;\n") 

31 

32 with patch( 

33 "lintro.plugins.execution_preparation.verify_tool_version", 

34 return_value=None, 

35 ): 

36 with patch.object( 

37 tsc_plugin, 

38 "_run_subprocess", 

39 return_value=(True, ""), 

40 ): 

41 result = tsc_plugin.check([str(test_file)], {}) 

42 

43 assert_that(result.success).is_true() 

44 assert_that(result.issues_count).is_equal_to(0) 

45 

46 

47def test_check_with_mocked_subprocess_issues( 

48 tsc_plugin: TscPlugin, 

49 tmp_path: Path, 

50) -> None: 

51 """Check returns issues when tsc finds type errors. 

52 

53 Args: 

54 tsc_plugin: The TscPlugin instance to test. 

55 tmp_path: Temporary directory path for test files. 

56 """ 

57 test_file = tmp_path / "main.ts" 

58 test_file.write_text("const x: number = 'string';\n") 

59 

60 tsc_output = f"{test_file}(1,7): error TS2322: Type 'string' is not assignable to type 'number'." 

61 

62 with patch( 

63 "lintro.plugins.execution_preparation.verify_tool_version", 

64 return_value=None, 

65 ): 

66 with patch.object( 

67 tsc_plugin, 

68 "_run_subprocess", 

69 return_value=(False, tsc_output), 

70 ): 

71 result = tsc_plugin.check([str(test_file)], {}) 

72 

73 assert_that(result.success).is_false() 

74 assert_that(result.issues_count).is_greater_than(0) 

75 

76 

77def test_check_with_timeout( 

78 tsc_plugin: TscPlugin, 

79 tmp_path: Path, 

80) -> None: 

81 """Check handles timeout correctly. 

82 

83 Args: 

84 tsc_plugin: The TscPlugin instance to test. 

85 tmp_path: Temporary directory path for test files. 

86 """ 

87 test_file = tmp_path / "main.ts" 

88 test_file.write_text("const x: number = 42;\n") 

89 

90 with patch( 

91 "lintro.plugins.execution_preparation.verify_tool_version", 

92 return_value=None, 

93 ): 

94 with patch.object( 

95 tsc_plugin, 

96 "_run_subprocess", 

97 side_effect=subprocess.TimeoutExpired(cmd=["tsc"], timeout=60), 

98 ): 

99 result = tsc_plugin.check([str(test_file)], {}) 

100 

101 assert_that(result.success).is_false() 

102 assert_that(result.issues_count).is_greater_than(0) 

103 

104 

105def test_check_with_no_typescript_files( 

106 tsc_plugin: TscPlugin, 

107 tmp_path: Path, 

108) -> None: 

109 """Check returns success when no TypeScript files found. 

110 

111 Args: 

112 tsc_plugin: The TscPlugin instance to test. 

113 tmp_path: Temporary directory path for test files. 

114 """ 

115 non_ts_file = tmp_path / "test.txt" 

116 non_ts_file.write_text("Not a TypeScript file") 

117 

118 with patch( 

119 "lintro.plugins.execution_preparation.verify_tool_version", 

120 return_value=None, 

121 ): 

122 result = tsc_plugin.check([str(non_ts_file)], {}) 

123 

124 assert_that(result.success).is_true() 

125 assert_that(result.output).contains("No .ts/.tsx/.mts/.cts files") 

126 

127 

128def test_check_parses_multiple_issues( 

129 tsc_plugin: TscPlugin, 

130 tmp_path: Path, 

131) -> None: 

132 """Check correctly parses multiple issues from output. 

133 

134 Args: 

135 tsc_plugin: The TscPlugin instance to test. 

136 tmp_path: Temporary directory path for test files. 

137 """ 

138 test_file = tmp_path / "main.ts" 

139 test_file.write_text("const x: number = 'a';\nconst y: string = 42;\n") 

140 

141 tsc_output = f"""{test_file}(1,7): error TS2322: Type 'string' is not assignable to type 'number'. 

142{test_file}(2,7): error TS2322: Type 'number' is not assignable to type 'string'.""" 

143 

144 with patch( 

145 "lintro.plugins.execution_preparation.verify_tool_version", 

146 return_value=None, 

147 ): 

148 with patch.object( 

149 tsc_plugin, 

150 "_run_subprocess", 

151 return_value=(False, tsc_output), 

152 ): 

153 result = tsc_plugin.check([str(test_file)], {}) 

154 

155 assert_that(result.success).is_false() 

156 assert_that(result.issues_count).is_equal_to(2) 

157 

158 

159# ============================================================================= 

160# Tests for TscPlugin.fix method 

161# ============================================================================= 

162 

163 

164def test_fix_raises_not_implemented(tsc_plugin: TscPlugin) -> None: 

165 """Fix raises NotImplementedError. 

166 

167 Args: 

168 tsc_plugin: The TscPlugin instance to test. 

169 """ 

170 with pytest.raises(NotImplementedError, match="cannot automatically fix"): 

171 tsc_plugin.fix(paths=["src"], options={})