Coverage for tests / unit / ai / test_sarif_artifact.py: 100%
86 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"""Tests for artifact side-channel output (#723 item 11)."""
3from __future__ import annotations
5import json
6from pathlib import Path
7from unittest.mock import MagicMock
9import pytest
10from assertpy import assert_that
12from lintro.enums.action import Action
13from lintro.models.core.tool_result import ToolResult
14from lintro.utils.tool_executor import _write_artifacts
17def _make_config(*, artifacts: list[str] | None = None) -> MagicMock:
18 """Build a minimal LintroConfig-like mock."""
19 cfg = MagicMock()
20 cfg.execution.artifacts = artifacts or []
21 return cfg
24def _make_logger() -> MagicMock:
25 return MagicMock()
28def _call_write(
29 results: list[ToolResult],
30 config: MagicMock,
31 logger: MagicMock,
32) -> None:
33 _write_artifacts(
34 results,
35 config,
36 logger,
37 action=Action.CHECK,
38 total_issues=0,
39 total_fixed=0,
40 )
43def test_no_artifacts_when_disabled(
44 tmp_path: Path,
45 monkeypatch: pytest.MonkeyPatch,
46) -> None:
47 """No files are produced when artifacts is empty and not in GHA."""
48 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
49 monkeypatch.chdir(tmp_path)
51 results = [ToolResult(name="ruff", success=True, issues_count=0)]
52 _call_write(results, _make_config(), _make_logger())
54 artifacts_dir = tmp_path / ".lintro" / "artifacts"
55 assert_that(artifacts_dir.exists()).is_false()
58def test_sarif_artifact_written_when_configured(
59 tmp_path: Path,
60 monkeypatch: pytest.MonkeyPatch,
61) -> None:
62 """SARIF file is produced when 'sarif' is in execution.artifacts."""
63 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
64 monkeypatch.chdir(tmp_path)
66 results = [ToolResult(name="ruff", success=True, issues_count=0)]
67 _call_write(results, _make_config(artifacts=["sarif"]), _make_logger())
69 sarif_path = tmp_path / ".lintro" / "artifacts" / "sarif" / "results.sarif.json"
70 assert_that(sarif_path.exists()).is_true()
72 data = json.loads(sarif_path.read_text())
73 assert_that(data["version"]).is_equal_to("2.1.0")
76def test_json_artifact_written_when_configured(
77 tmp_path: Path,
78 monkeypatch: pytest.MonkeyPatch,
79) -> None:
80 """JSON artifact file is produced when 'json' is in execution.artifacts."""
81 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
82 monkeypatch.chdir(tmp_path)
84 results = [ToolResult(name="ruff", success=True, issues_count=0)]
85 _call_write(results, _make_config(artifacts=["json"]), _make_logger())
87 json_path = tmp_path / ".lintro" / "artifacts" / "json" / "results.json"
88 assert_that(json_path.exists()).is_true()
90 data = json.loads(json_path.read_text())
91 assert_that(data).contains_key("summary")
94def test_csv_artifact_written_when_configured(
95 tmp_path: Path,
96 monkeypatch: pytest.MonkeyPatch,
97) -> None:
98 """CSV artifact file is produced when 'csv' is in execution.artifacts."""
99 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
100 monkeypatch.chdir(tmp_path)
102 results = [ToolResult(name="ruff", success=True, issues_count=0)]
103 _call_write(results, _make_config(artifacts=["csv"]), _make_logger())
105 csv_path = tmp_path / ".lintro" / "artifacts" / "csv" / "results.csv"
106 assert_that(csv_path.exists()).is_true()
107 assert_that(csv_path.read_text()).contains("tool")
110def test_multiple_artifacts_written(
111 tmp_path: Path,
112 monkeypatch: pytest.MonkeyPatch,
113) -> None:
114 """Multiple artifact formats can be emitted simultaneously."""
115 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
116 monkeypatch.chdir(tmp_path)
118 results = [ToolResult(name="ruff", success=True, issues_count=0)]
119 _call_write(
120 results,
121 _make_config(artifacts=["json", "csv", "markdown"]),
122 _make_logger(),
123 )
125 assert_that(
126 (tmp_path / ".lintro" / "artifacts" / "json" / "results.json").exists(),
127 ).is_true()
128 assert_that(
129 (tmp_path / ".lintro" / "artifacts" / "csv" / "results.csv").exists(),
130 ).is_true()
131 assert_that(
132 (tmp_path / ".lintro" / "artifacts" / "markdown" / "results.md").exists(),
133 ).is_true()
136def test_sarif_auto_emits_in_github_actions(
137 tmp_path: Path,
138 monkeypatch: pytest.MonkeyPatch,
139) -> None:
140 """SARIF file is auto-emitted when GITHUB_ACTIONS=true."""
141 monkeypatch.setenv("GITHUB_ACTIONS", "true")
142 monkeypatch.chdir(tmp_path)
144 results = [ToolResult(name="ruff", success=True, issues_count=0)]
145 _call_write(results, _make_config(), _make_logger())
147 sarif_path = tmp_path / ".lintro" / "artifacts" / "sarif" / "results.sarif.json"
148 assert_that(sarif_path.exists()).is_true()
151def test_unknown_artifact_format_warns(
152 tmp_path: Path,
153 monkeypatch: pytest.MonkeyPatch,
154) -> None:
155 """Unknown artifact format logs a warning and is skipped."""
156 monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
157 monkeypatch.chdir(tmp_path)
159 logger = _make_logger()
160 results = [ToolResult(name="ruff", success=True, issues_count=0)]
161 _call_write(results, _make_config(artifacts=["xlsx"]), logger)
163 logger.console_output.assert_called_once()
164 call_arg = logger.console_output.call_args[0][0]
165 assert_that(call_arg).contains("Unknown artifact format")
168def test_artifact_logs_warning_on_write_failure(
169 tmp_path: Path,
170 monkeypatch: pytest.MonkeyPatch,
171) -> None:
172 """A write failure logs a warning instead of crashing."""
173 monkeypatch.setenv("GITHUB_ACTIONS", "true")
174 # Block directory creation by placing a file where the dir should be
175 blocker = tmp_path / ".lintro" / "artifacts"
176 blocker.parent.mkdir(parents=True, exist_ok=True)
177 blocker.write_text("not a directory")
178 monkeypatch.chdir(tmp_path)
180 logger = _make_logger()
181 results = [ToolResult(name="ruff", success=True, issues_count=0)]
182 _call_write(results, _make_config(), logger)
184 logger.console_output.assert_called_once()
185 call_arg = logger.console_output.call_args[0][0]
186 assert_that(call_arg).contains("sarif artifact")