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

90 statements  

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

1"""Unit tests for hadolint plugin execution methods.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6from typing import cast 

7from unittest.mock import MagicMock, patch 

8 

9from assertpy import assert_that 

10 

11from lintro.enums.tool_name import ToolName 

12from lintro.parsers.hadolint.hadolint_issue import HadolintIssue 

13from lintro.tools.definitions.hadolint import HadolintPlugin 

14 

15# ============================================================================= 

16# Tests for HadolintPlugin.check method with mocked subprocess 

17# ============================================================================= 

18 

19 

20def test_check_with_issues(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None: 

21 """Check returns issues when found. 

22 

23 Args: 

24 hadolint_plugin: The HadolintPlugin instance to test. 

25 tmp_path: Temporary directory path for test files. 

26 """ 

27 import pathlib 

28 

29 dockerfile = pathlib.Path(tmp_path) / "Dockerfile" 

30 dockerfile.write_text("FROM python\n") 

31 

32 mock_output = "Dockerfile:1 DL3006 warning: Always tag the version of an image" 

33 mock_result = (False, mock_output) 

34 

35 with ( 

36 patch.object(hadolint_plugin, "_prepare_execution") as mock_prepare, 

37 patch.object(hadolint_plugin, "_run_subprocess", return_value=mock_result), 

38 ): 

39 mock_ctx = MagicMock() 

40 mock_ctx.should_skip = False 

41 mock_ctx.early_result = None 

42 mock_ctx.timeout = 30 

43 mock_ctx.cwd = str(tmp_path) 

44 mock_ctx.files = [str(dockerfile)] 

45 mock_prepare.return_value = mock_ctx 

46 

47 result = hadolint_plugin.check([str(dockerfile)], {}) 

48 

49 assert_that(result.name).is_equal_to(ToolName.HADOLINT) 

50 assert_that(result.success).is_false() 

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

52 assert_that(result.issues).is_not_none() 

53 issue = cast(HadolintIssue, result.issues[0]) # type: ignore[index] # validated via is_not_none 

54 assert_that(issue.code).is_equal_to("DL3006") 

55 

56 

57def test_check_multiple_files(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None: 

58 """Check handles multiple Dockerfiles. 

59 

60 Args: 

61 hadolint_plugin: The HadolintPlugin instance to test. 

62 tmp_path: Temporary directory path for test files. 

63 """ 

64 import pathlib 

65 

66 dockerfile1 = pathlib.Path(tmp_path) / "Dockerfile" 

67 dockerfile1.write_text("FROM python:3.11\n") 

68 dockerfile2 = pathlib.Path(tmp_path) / "Dockerfile.dev" 

69 dockerfile2.write_text("FROM python:3.11-slim\n") 

70 

71 mock_result = (True, "") 

72 

73 with ( 

74 patch.object(hadolint_plugin, "_prepare_execution") as mock_prepare, 

75 patch.object(hadolint_plugin, "_run_subprocess", return_value=mock_result), 

76 ): 

77 mock_ctx = MagicMock() 

78 mock_ctx.should_skip = False 

79 mock_ctx.early_result = None 

80 mock_ctx.timeout = 30 

81 mock_ctx.cwd = str(tmp_path) 

82 mock_ctx.files = [str(dockerfile1), str(dockerfile2)] 

83 mock_prepare.return_value = mock_ctx 

84 

85 result = hadolint_plugin.check([str(dockerfile1), str(dockerfile2)], {}) 

86 

87 assert_that(result.success).is_true() 

88 

89 

90# ============================================================================= 

91# Tests for output parsing 

92# ============================================================================= 

93 

94 

95def test_parse_single_issue(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None: 

96 """Parse single issue from hadolint output. 

97 

98 Args: 

99 hadolint_plugin: The HadolintPlugin instance to test. 

100 tmp_path: Temporary directory path for test files. 

101 """ 

102 import pathlib 

103 

104 dockerfile = pathlib.Path(tmp_path) / "Dockerfile" 

105 dockerfile.write_text("FROM python\n") 

106 

107 mock_output = ( 

108 "Dockerfile:1 DL3006 error: Always tag the version of an image explicitly" 

109 ) 

110 mock_result = (False, mock_output) 

111 

112 with ( 

113 patch.object(hadolint_plugin, "_prepare_execution") as mock_prepare, 

114 patch.object(hadolint_plugin, "_run_subprocess", return_value=mock_result), 

115 ): 

116 mock_ctx = MagicMock() 

117 mock_ctx.should_skip = False 

118 mock_ctx.early_result = None 

119 mock_ctx.timeout = 30 

120 mock_ctx.cwd = str(tmp_path) 

121 mock_ctx.files = [str(dockerfile)] 

122 mock_prepare.return_value = mock_ctx 

123 

124 result = hadolint_plugin.check([str(dockerfile)], {}) 

125 

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

127 assert_that(result.issues).is_not_none() 

128 issue = cast(HadolintIssue, result.issues[0]) # type: ignore[index] # validated via is_not_none 

129 assert_that(issue.file).is_equal_to("Dockerfile") 

130 assert_that(issue.line).is_equal_to(1) 

131 assert_that(issue.code).is_equal_to("DL3006") 

132 assert_that(issue.level).is_equal_to("error") 

133 assert_that(issue.message).contains("tag the version") 

134 

135 

136def test_parse_multiple_issues(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None: 

137 """Parse multiple issues from hadolint output. 

138 

139 Args: 

140 hadolint_plugin: The HadolintPlugin instance to test. 

141 tmp_path: Temporary directory path for test files. 

142 """ 

143 import pathlib 

144 

145 dockerfile = pathlib.Path(tmp_path) / "Dockerfile" 

146 dockerfile.write_text("FROM python\nRUN apt-get update\n") 

147 

148 mock_output = ( 

149 "Dockerfile:1 DL3006 error: Always tag the version of an image explicitly\n" 

150 "Dockerfile:2 DL3009 info: Delete the apt-get lists after installing something" 

151 ) 

152 mock_result = (False, mock_output) 

153 

154 with ( 

155 patch.object(hadolint_plugin, "_prepare_execution") as mock_prepare, 

156 patch.object(hadolint_plugin, "_run_subprocess", return_value=mock_result), 

157 ): 

158 mock_ctx = MagicMock() 

159 mock_ctx.should_skip = False 

160 mock_ctx.early_result = None 

161 mock_ctx.timeout = 30 

162 mock_ctx.cwd = str(tmp_path) 

163 mock_ctx.files = [str(dockerfile)] 

164 mock_prepare.return_value = mock_ctx 

165 

166 result = hadolint_plugin.check([str(dockerfile)], {}) 

167 

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

169 assert_that(result.issues).is_not_none() 

170 first_issue = cast(HadolintIssue, result.issues[0]) # type: ignore[index] # validated via is_not_none 

171 second_issue = cast(HadolintIssue, result.issues[1]) # type: ignore[index] # validated via is_not_none 

172 assert_that(first_issue.code).is_equal_to("DL3006") 

173 assert_that(second_issue.code).is_equal_to("DL3009")