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

1"""Unit tests for the Markdownlint output parser. 

2 

3These tests validate that the parser handles empty output and typical 

4markdownlint-cli2 default formatter lines, producing structured issues. 

5""" 

6 

7from assertpy import assert_that 

8 

9from lintro.parsers.markdownlint.markdownlint_parser import parse_markdownlint_output 

10 

11 

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([]) 

16 

17 

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) 

30 

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") 

37 

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") 

44 

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") 

51 

52 

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") 

64 

65 

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") 

77 

78 

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) 

91 

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") 

99 

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") 

107 

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") 

114 

115 

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) 

127 

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") 

135 

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") 

140 

141 

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")