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
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Unit tests for hadolint plugin execution methods."""
3from __future__ import annotations
5from pathlib import Path
6from typing import cast
7from unittest.mock import MagicMock, patch
9from assertpy import assert_that
11from lintro.enums.tool_name import ToolName
12from lintro.parsers.hadolint.hadolint_issue import HadolintIssue
13from lintro.tools.definitions.hadolint import HadolintPlugin
15# =============================================================================
16# Tests for HadolintPlugin.check method with mocked subprocess
17# =============================================================================
20def test_check_with_issues(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None:
21 """Check returns issues when found.
23 Args:
24 hadolint_plugin: The HadolintPlugin instance to test.
25 tmp_path: Temporary directory path for test files.
26 """
27 import pathlib
29 dockerfile = pathlib.Path(tmp_path) / "Dockerfile"
30 dockerfile.write_text("FROM python\n")
32 mock_output = "Dockerfile:1 DL3006 warning: Always tag the version of an image"
33 mock_result = (False, mock_output)
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
47 result = hadolint_plugin.check([str(dockerfile)], {})
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")
57def test_check_multiple_files(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None:
58 """Check handles multiple Dockerfiles.
60 Args:
61 hadolint_plugin: The HadolintPlugin instance to test.
62 tmp_path: Temporary directory path for test files.
63 """
64 import pathlib
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")
71 mock_result = (True, "")
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
85 result = hadolint_plugin.check([str(dockerfile1), str(dockerfile2)], {})
87 assert_that(result.success).is_true()
90# =============================================================================
91# Tests for output parsing
92# =============================================================================
95def test_parse_single_issue(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None:
96 """Parse single issue from hadolint output.
98 Args:
99 hadolint_plugin: The HadolintPlugin instance to test.
100 tmp_path: Temporary directory path for test files.
101 """
102 import pathlib
104 dockerfile = pathlib.Path(tmp_path) / "Dockerfile"
105 dockerfile.write_text("FROM python\n")
107 mock_output = (
108 "Dockerfile:1 DL3006 error: Always tag the version of an image explicitly"
109 )
110 mock_result = (False, mock_output)
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
124 result = hadolint_plugin.check([str(dockerfile)], {})
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")
136def test_parse_multiple_issues(hadolint_plugin: HadolintPlugin, tmp_path: Path) -> None:
137 """Parse multiple issues from hadolint output.
139 Args:
140 hadolint_plugin: The HadolintPlugin instance to test.
141 tmp_path: Temporary directory path for test files.
142 """
143 import pathlib
145 dockerfile = pathlib.Path(tmp_path) / "Dockerfile"
146 dockerfile.write_text("FROM python\nRUN apt-get update\n")
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)
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
166 result = hadolint_plugin.check([str(dockerfile)], {})
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")