Coverage for tests / unit / parsers / test_markdownlint_parser.py: 100%
88 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 the Markdownlint output parser.
3These tests validate that the parser handles empty output and typical
4markdownlint-cli2 default formatter lines, producing structured issues.
5"""
7from assertpy import assert_that
9from lintro.parsers.markdownlint.markdownlint_parser import parse_markdownlint_output
12def test_parse_markdownlint_empty() -> None:
13 """Return an empty list for empty parser input."""
14 assert_that(parse_markdownlint_output("")).is_equal_to([])
15 assert_that(parse_markdownlint_output(" ")).is_equal_to([])
18def test_parse_markdownlint_lines() -> None:
19 """Parse typical markdownlint-cli2 lines and produce structured issues."""
20 out = (
21 "dir/about.md:1:1 MD021/no-multiple-space-closed-atx Multiple spaces "
22 'inside hashes on closed atx style heading [Context: "# About #"]\n'
23 "dir/about.md:4 MD032/blanks-around-lists Lists should be surrounded "
24 'by blank lines [Context: "1. List"]\n'
25 "viewme.md:3:10 MD009/no-trailing-spaces Trailing spaces "
26 "[Expected: 0 or 2; Actual: 1]"
27 )
28 issues = parse_markdownlint_output(out)
29 assert_that(len(issues)).is_equal_to(3)
31 i0 = issues[0]
32 assert_that(i0.file).is_equal_to("dir/about.md")
33 assert_that(i0.line).is_equal_to(1)
34 assert_that(i0.column).is_equal_to(1)
35 assert_that(i0.code).is_equal_to("MD021")
36 assert_that(i0.message).contains("Multiple spaces")
38 i1 = issues[1]
39 assert_that(i1.file).is_equal_to("dir/about.md")
40 assert_that(i1.line).is_equal_to(4)
41 assert_that(i1.column).is_equal_to(0) # 0 means unknown/not provided
42 assert_that(i1.code).is_equal_to("MD032")
43 assert_that(i1.message).contains("Lists should be surrounded")
45 i2 = issues[2]
46 assert_that(i2.file).is_equal_to("viewme.md")
47 assert_that(i2.line).is_equal_to(3)
48 assert_that(i2.column).is_equal_to(10)
49 assert_that(i2.code).is_equal_to("MD009")
50 assert_that(i2.message).contains("Trailing spaces")
53def test_parse_markdownlint_without_column() -> None:
54 """Parse markdownlint output without column information."""
55 out = "file.md:5 MD041/first-line-heading First line should be a heading"
56 issues = parse_markdownlint_output(out)
57 assert_that(len(issues)).is_equal_to(1)
58 i0 = issues[0]
59 assert_that(i0.file).is_equal_to("file.md")
60 assert_that(i0.line).is_equal_to(5)
61 assert_that(i0.column).is_equal_to(0) # 0 means unknown/not provided
62 assert_that(i0.code).is_equal_to("MD041")
63 assert_that(i0.message).contains("First line should be a heading")
66def test_parse_markdownlint_ignores_malformed_lines() -> None:
67 """Ignore lines that don't match the expected format."""
68 out = (
69 "file.md:1:1 MD013/line-length Line too long\n"
70 "This is not a valid markdownlint line\n"
71 "file.md:3 MD012/no-multiple-blanks Multiple blank lines"
72 )
73 issues = parse_markdownlint_output(out)
74 assert_that(len(issues)).is_equal_to(2)
75 assert_that(issues[0].code).is_equal_to("MD013")
76 assert_that(issues[1].code).is_equal_to("MD012")
79def test_parse_markdownlint_multiline_messages() -> None:
80 """Parse markdownlint output with multi-line messages (continuation lines)."""
81 out = (
82 "dir/about.md:1:1 MD021/no-multiple-space-closed-atx Multiple spaces\n"
83 ' inside hashes on closed atx style heading [Context: "# About #"]\n'
84 "dir/about.md:4 MD032/blanks-around-lists Lists should be surrounded\n"
85 ' by blank lines [Context: "1. List"]\n'
86 "viewme.md:3:10 MD009/no-trailing-spaces Trailing spaces\n"
87 " [Expected: 0 or 2; Actual: 1]"
88 )
89 issues = parse_markdownlint_output(out)
90 assert_that(len(issues)).is_equal_to(3)
92 i0 = issues[0]
93 assert_that(i0.file).is_equal_to("dir/about.md")
94 assert_that(i0.line).is_equal_to(1)
95 assert_that(i0.column).is_equal_to(1)
96 assert_that(i0.code).is_equal_to("MD021")
97 assert_that(i0.message).contains("Multiple spaces")
98 assert_that(i0.message).contains("inside hashes")
100 i1 = issues[1]
101 assert_that(i1.file).is_equal_to("dir/about.md")
102 assert_that(i1.line).is_equal_to(4)
103 assert_that(i1.column).is_equal_to(0) # 0 means unknown/not provided
104 assert_that(i1.code).is_equal_to("MD032")
105 assert_that(i1.message).contains("Lists should be surrounded")
106 assert_that(i1.message).contains("by blank lines")
108 i2 = issues[2]
109 assert_that(i2.file).is_equal_to("viewme.md")
110 assert_that(i2.line).is_equal_to(3)
111 assert_that(i2.column).is_equal_to(10)
112 assert_that(i2.code).is_equal_to("MD009")
113 assert_that(i2.message).contains("Trailing spaces")
116def test_parse_markdownlint_multiline_with_empty_lines() -> None:
117 """Parse markdownlint output with multi-line messages separated by empty lines."""
118 out = (
119 "file.md:1:1 MD013/line-length Line too long\n"
120 " continuation part one\n"
121 " continuation part two\n"
122 "\n"
123 "file.md:3 MD012/no-multiple-blanks Multiple blank lines"
124 )
125 issues = parse_markdownlint_output(out)
126 assert_that(len(issues)).is_equal_to(2)
128 i0 = issues[0]
129 assert_that(i0.file).is_equal_to("file.md")
130 assert_that(i0.line).is_equal_to(1)
131 assert_that(i0.code).is_equal_to("MD013")
132 assert_that(i0.message).contains("Line too long")
133 assert_that(i0.message).contains("continuation part one")
134 assert_that(i0.message).contains("continuation part two")
136 i1 = issues[1]
137 assert_that(i1.file).is_equal_to("file.md")
138 assert_that(i1.line).is_equal_to(3)
139 assert_that(i1.code).is_equal_to("MD012")
142def test_parse_markdownlint_ansi_codes_stripped() -> None:
143 """Strip ANSI escape codes from output for consistent CI/local parsing."""
144 # Output with ANSI color codes (common in CI environments)
145 output = "\x1b[31mdir/about.md:1:1 MD021/no-multiple-space-closed-atx Multiple spaces\x1b[0m"
146 issues = parse_markdownlint_output(output)
147 assert_that(len(issues)).is_equal_to(1)
148 assert_that(issues[0].file).is_equal_to("dir/about.md")
149 assert_that(issues[0].code).is_equal_to("MD021")