Coverage for tests / unit / tools / shellcheck / test_execution.py: 100%
54 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 shellcheck plugin check execution and output parsing."""
3from __future__ import annotations
5from pathlib import Path
6from unittest.mock import patch
8import pytest
9from assertpy import assert_that
11from lintro.parsers.shellcheck.shellcheck_parser import parse_shellcheck_output
12from lintro.tools.definitions.shellcheck import ShellcheckPlugin
14# Tests for ShellcheckPlugin.check method
17def test_check_with_mocked_subprocess_success(
18 shellcheck_plugin: ShellcheckPlugin,
19 tmp_path: Path,
20) -> None:
21 """Check returns success when no issues found.
23 Args:
24 shellcheck_plugin: The ShellcheckPlugin instance to test.
25 tmp_path: Temporary directory path for test files.
26 """
27 # Create a test shell file
28 test_file = tmp_path / "test_script.sh"
29 test_file.write_text('#!/bin/bash\necho "Hello World"\n')
31 with patch.object(
32 shellcheck_plugin,
33 "_run_subprocess",
34 return_value=(True, "[]"),
35 ):
36 result = shellcheck_plugin.check([str(test_file)], {})
38 assert_that(result.success).is_true()
39 assert_that(result.issues_count).is_equal_to(0)
42def test_check_with_mocked_subprocess_issues(
43 shellcheck_plugin: ShellcheckPlugin,
44 tmp_path: Path,
45) -> None:
46 """Check returns issues when shellcheck finds problems.
48 Args:
49 shellcheck_plugin: The ShellcheckPlugin instance to test.
50 tmp_path: Temporary directory path for test files.
51 """
52 test_file = tmp_path / "test_script.sh"
53 test_file.write_text("#!/bin/bash\necho $var\n")
55 shellcheck_output = """[
56 {
57 "file": "test_script.sh",
58 "line": 2,
59 "endLine": 2,
60 "column": 6,
61 "endColumn": 10,
62 "level": "warning",
63 "code": 2086,
64 "message": "Double quote to prevent globbing and word splitting."
65 }
66 ]"""
68 with patch.object(
69 shellcheck_plugin,
70 "_run_subprocess",
71 return_value=(False, shellcheck_output),
72 ):
73 result = shellcheck_plugin.check([str(test_file)], {})
75 assert_that(result.success).is_false()
76 assert_that(result.issues_count).is_greater_than(0)
79def test_check_with_no_shell_files(
80 shellcheck_plugin: ShellcheckPlugin,
81 tmp_path: Path,
82) -> None:
83 """Check returns success when no shell files found.
85 Args:
86 shellcheck_plugin: The ShellcheckPlugin instance to test.
87 tmp_path: Temporary directory path for test files.
88 """
89 non_shell_file = tmp_path / "test.txt"
90 non_shell_file.write_text("Not a shell file")
92 result = shellcheck_plugin.check([str(non_shell_file)], {})
94 assert_that(result.success).is_true()
95 assert_that(result.output).contains("No")
98# Tests for ShellcheckPlugin.fix method
101def test_fix_raises_not_implemented(shellcheck_plugin: ShellcheckPlugin) -> None:
102 """Fix raises NotImplementedError.
104 Args:
105 shellcheck_plugin: The ShellcheckPlugin instance to test.
106 """
107 with pytest.raises(NotImplementedError, match="Shellcheck cannot automatically"):
108 shellcheck_plugin.fix([], {})
111# Tests for output parsing
114def test_parse_shellcheck_output_single_issue() -> None:
115 """Parse single issue from shellcheck output."""
116 output = """[
117 {
118 "file": "test.sh",
119 "line": 10,
120 "endLine": 10,
121 "column": 5,
122 "endColumn": 10,
123 "level": "warning",
124 "code": 2086,
125 "message": "Double quote to prevent globbing and word splitting."
126 }
127 ]"""
128 issues = parse_shellcheck_output(output)
130 assert_that(issues).is_length(1)
131 assert_that(issues[0].file).is_equal_to("test.sh")
132 assert_that(issues[0].line).is_equal_to(10)
133 assert_that(issues[0].code).is_equal_to("SC2086")
134 assert_that(issues[0].message).contains("Double quote")
137def test_parse_shellcheck_output_multiple_issues() -> None:
138 """Parse multiple issues from shellcheck output."""
139 output = """[
140 {
141 "file": "test.sh",
142 "line": 10,
143 "column": 5,
144 "level": "warning",
145 "code": 2086,
146 "message": "Double quote to prevent globbing."
147 },
148 {
149 "file": "test.sh",
150 "line": 20,
151 "column": 1,
152 "level": "error",
153 "code": 1091,
154 "message": "Not following sourced file."
155 }
156 ]"""
157 issues = parse_shellcheck_output(output)
159 assert_that(issues).is_length(2)
160 assert_that(issues[0].code).is_equal_to("SC2086")
161 assert_that(issues[1].code).is_equal_to("SC1091")
164def test_parse_shellcheck_output_empty() -> None:
165 """Parse empty output returns empty list."""
166 issues = parse_shellcheck_output("[]")
168 assert_that(issues).is_empty()
171def test_parse_shellcheck_output_none() -> None:
172 """Parse None output returns empty list."""
173 issues = parse_shellcheck_output(None)
175 assert_that(issues).is_empty()
178def test_parse_shellcheck_output_invalid_json() -> None:
179 """Parse invalid JSON returns empty list."""
180 issues = parse_shellcheck_output("not valid json")
182 assert_that(issues).is_empty()