Coverage for tests / unit / parsers / test_base_issue.py: 100%
121 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 lintro.parsers.base_issue module."""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import ClassVar
8import pytest
9from assertpy import assert_that
11from lintro.enums.severity_level import SeverityLevel
12from lintro.parsers.base_issue import BaseIssue
15def test_base_issue_default_values() -> None:
16 """BaseIssue has empty string and zero defaults."""
17 issue = BaseIssue()
18 assert_that(issue.file).is_equal_to("")
19 assert_that(issue.line).is_equal_to(0)
20 assert_that(issue.column).is_equal_to(0)
21 assert_that(issue.message).is_equal_to("")
24def test_base_issue_accepts_values() -> None:
25 """BaseIssue accepts custom values."""
26 issue = BaseIssue(file="test.py", line=10, column=5, message="Error found")
27 assert_that(issue.file).is_equal_to("test.py")
28 assert_that(issue.line).is_equal_to(10)
29 assert_that(issue.column).is_equal_to(5)
30 assert_that(issue.message).is_equal_to("Error found")
33def test_to_display_row_basic_fields() -> None:
34 """to_display_row includes basic fields."""
35 issue = BaseIssue(file="test.py", line=10, column=5, message="Test message")
36 result = issue.to_display_row()
37 assert_that(result["file"]).is_equal_to("test.py")
38 assert_that(result["line"]).is_equal_to("10")
39 assert_that(result["column"]).is_equal_to("5")
40 assert_that(result["message"]).is_equal_to("Test message")
43def test_to_display_row_zero_line_shows_dash() -> None:
44 """to_display_row shows dash for zero line."""
45 issue = BaseIssue(file="test.py", line=0, column=0)
46 result = issue.to_display_row()
47 assert_that(result["line"]).is_equal_to("-")
48 assert_that(result["column"]).is_equal_to("-")
51def test_to_display_row_missing_optional_fields() -> None:
52 """to_display_row handles missing optional fields."""
53 issue = BaseIssue()
54 result = issue.to_display_row()
55 assert_that(result["code"]).is_equal_to("")
56 assert_that(result["severity"]).is_equal_to("WARNING")
57 assert_that(result["fixable"]).is_equal_to("")
60def test_display_field_map_class_variable() -> None:
61 """BaseIssue has DISPLAY_FIELD_MAP class variable."""
62 assert_that(BaseIssue.DISPLAY_FIELD_MAP).contains_key("code")
63 assert_that(BaseIssue.DISPLAY_FIELD_MAP).contains_key("severity")
64 assert_that(BaseIssue.DISPLAY_FIELD_MAP).contains_key("fixable")
65 assert_that(BaseIssue.DISPLAY_FIELD_MAP).contains_key("message")
68def test_subclass_with_custom_fields() -> None:
69 """Subclass can add custom fields."""
71 @dataclass
72 class CustomIssue(BaseIssue):
73 code: str = ""
74 severity: str = ""
76 issue = CustomIssue(
77 file="test.py",
78 line=1,
79 column=1,
80 message="Test",
81 code="E001",
82 severity="error",
83 )
84 result = issue.to_display_row()
85 assert_that(result["code"]).is_equal_to("E001")
86 assert_that(result["severity"]).is_equal_to("ERROR")
89def test_subclass_with_custom_field_map() -> None:
90 """Subclass can customize field mapping."""
92 @dataclass
93 class MappedIssue(BaseIssue):
94 DISPLAY_FIELD_MAP: ClassVar[dict[str, str]] = {
95 "code": "rule_id",
96 "severity": "level",
97 "fixable": "fixable",
98 "message": "message",
99 }
100 rule_id: str = ""
101 level: str = ""
103 issue = MappedIssue(
104 file="test.py",
105 line=1,
106 column=1,
107 message="Test",
108 rule_id="RULE001",
109 level="warning",
110 )
111 result = issue.to_display_row()
112 assert_that(result["code"]).is_equal_to("RULE001")
113 assert_that(result["severity"]).is_equal_to("WARNING")
116def test_to_display_row_fixable_true() -> None:
117 """to_display_row shows Yes for fixable=True."""
119 @dataclass
120 class FixableIssue(BaseIssue):
121 fixable: bool = False
123 issue = FixableIssue(file="test.py", line=1, column=1, fixable=True)
124 result = issue.to_display_row()
125 assert_that(result["fixable"]).is_equal_to("Yes")
128def test_to_display_row_fixable_false() -> None:
129 """to_display_row shows empty string for fixable=False."""
131 @dataclass
132 class FixableIssue(BaseIssue):
133 fixable: bool = False
135 issue = FixableIssue(file="test.py", line=1, column=1, fixable=False)
136 result = issue.to_display_row()
137 assert_that(result["fixable"]).is_equal_to("")
140@pytest.mark.parametrize(
141 ("line", "column", "expected_line", "expected_column"),
142 [
143 (1, 1, "1", "1"),
144 (100, 50, "100", "50"),
145 (0, 5, "-", "5"),
146 (10, 0, "10", "-"),
147 ],
148)
149def test_to_display_row_line_column_formatting(
150 line: int,
151 column: int,
152 expected_line: str,
153 expected_column: str,
154) -> None:
155 """to_display_row formats line and column correctly.
157 Args:
158 line: The line number to test.
159 column: The column number to test.
160 expected_line: The expected line string in display row.
161 expected_column: The expected column string in display row.
162 """
163 issue = BaseIssue(file="test.py", line=line, column=column)
164 result = issue.to_display_row()
165 assert_that(result["line"]).is_equal_to(expected_line)
166 assert_that(result["column"]).is_equal_to(expected_column)
169# =============================================================================
170# Tests for get_severity()
171# =============================================================================
174def test_get_severity_returns_default_when_no_severity_field() -> None:
175 """get_severity returns DEFAULT_SEVERITY when issue has no severity attr."""
176 issue = BaseIssue(file="test.py", line=1)
177 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.WARNING)
180def test_get_severity_normalizes_string_field() -> None:
181 """get_severity normalizes a native severity string."""
183 @dataclass
184 class SeverityIssue(BaseIssue):
185 severity: str = ""
187 issue = SeverityIssue(file="test.py", line=1, severity="error")
188 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.ERROR)
191def test_get_severity_uses_display_field_map() -> None:
192 """get_severity resolves the attribute via DISPLAY_FIELD_MAP."""
194 @dataclass
195 class MappedIssue(BaseIssue):
196 DISPLAY_FIELD_MAP: ClassVar[dict[str, str]] = {
197 **BaseIssue.DISPLAY_FIELD_MAP,
198 "severity": "level",
199 }
200 level: str = ""
202 issue = MappedIssue(file="test.py", line=1, level="warning")
203 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.WARNING)
206def test_get_severity_falls_back_on_empty_string() -> None:
207 """get_severity falls back to DEFAULT_SEVERITY for empty string."""
209 @dataclass
210 class SeverityIssue(BaseIssue):
211 severity: str = ""
213 issue = SeverityIssue(file="test.py", line=1, severity="")
214 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.WARNING)
217def test_get_severity_falls_back_on_unknown_value() -> None:
218 """get_severity falls back to DEFAULT_SEVERITY for unrecognized strings."""
220 @dataclass
221 class SeverityIssue(BaseIssue):
222 severity: str = ""
224 issue = SeverityIssue(file="test.py", line=1, severity="banana")
225 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.WARNING)
228def test_get_severity_respects_custom_default() -> None:
229 """get_severity uses subclass DEFAULT_SEVERITY."""
231 @dataclass
232 class InfoIssue(BaseIssue):
233 DEFAULT_SEVERITY: ClassVar[SeverityLevel] = SeverityLevel.INFO
235 issue = InfoIssue(file="test.py", line=1)
236 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.INFO)
239def test_get_severity_passes_through_enum_instance() -> None:
240 """get_severity returns SeverityLevel instances unchanged."""
242 @dataclass
243 class EnumIssue(BaseIssue):
244 severity: SeverityLevel = SeverityLevel.ERROR
246 issue = EnumIssue(file="test.py", line=1)
247 assert_that(issue.get_severity()).is_equal_to(SeverityLevel.ERROR)