@@ -17,6 +17,43 @@ func TestCheck_FirstLineH1_NoViolation(t *testing.T) {
1717 require .Len (t , diags , 0 , "expected 0 diagnostics, got %d: %+v" , len (diags ), diags )
1818}
1919
20+ func TestCheck_SetextHeading_NoViolation (t * testing.T ) {
21+ src := []byte ("Title\n =====\n \n Some text\n " )
22+ f , err := lint .NewFile ("test.md" , src )
23+ require .NoError (t , err )
24+ r := & Rule {Level : 1 }
25+ diags := r .Check (f )
26+ require .Len (t , diags , 0 , "setext heading on line 1 should pass, got %d: %+v" , len (diags ), diags )
27+ }
28+
29+ func TestCheck_EmphasisHeading_NoViolation (t * testing.T ) {
30+ src := []byte ("# *Title*\n \n Some text\n " )
31+ f , err := lint .NewFile ("test.md" , src )
32+ require .NoError (t , err )
33+ r := & Rule {Level : 1 }
34+ diags := r .Check (f )
35+ require .Len (t , diags , 0 , "heading with emphasis on line 1 should pass, got %d: %+v" , len (diags ), diags )
36+ }
37+
38+ func TestCheck_LinkHeading_NoViolation (t * testing.T ) {
39+ src := []byte ("# [link](url)\n \n Some text\n " )
40+ f , err := lint .NewFile ("test.md" , src )
41+ require .NoError (t , err )
42+ r := & Rule {Level : 1 }
43+ diags := r .Check (f )
44+ require .Len (t , diags , 0 , "heading with link on line 1 should pass, got %d: %+v" , len (diags ), diags )
45+ }
46+
47+ func TestCheck_BlankLineSetextHeading (t * testing.T ) {
48+ src := []byte ("\n Title\n =====\n " )
49+ f , err := lint .NewFile ("test.md" , src )
50+ require .NoError (t , err )
51+ r := & Rule {Level : 1 }
52+ diags := r .Check (f )
53+ require .Len (t , diags , 1 , "setext heading after blank line should fail, got %d: %+v" , len (diags ), diags )
54+ require .Equal (t , "first line should be a level 1 heading, found blank line" , diags [0 ].Message )
55+ }
56+
2057func TestCheck_EmptyFile (t * testing.T ) {
2158 src := []byte ("" )
2259 f , err := lint .NewFile ("test.md" , src )
@@ -36,6 +73,7 @@ func TestCheck_StartsWithParagraph(t *testing.T) {
3673 r := & Rule {Level : 1 }
3774 diags := r .Check (f )
3875 require .Len (t , diags , 1 , "expected 1 diagnostic, got %d: %+v" , len (diags ), diags )
76+ require .Equal (t , "first line should be a level 1 heading" , diags [0 ].Message )
3977}
4078
4179func TestCheck_BlankLineThenHeading (t * testing.T ) {
@@ -45,6 +83,57 @@ func TestCheck_BlankLineThenHeading(t *testing.T) {
4583 r := & Rule {Level : 1 }
4684 diags := r .Check (f )
4785 require .Len (t , diags , 1 , "expected 1 diagnostic for heading not on line 1, got %d: %+v" , len (diags ), diags )
86+ require .Equal (t , "first line should be a level 1 heading, found blank line" , diags [0 ].Message )
87+ }
88+
89+ func TestCheck_MultipleBlankLinesThenHeading (t * testing.T ) {
90+ src := []byte ("\n \n \n # Title\n " )
91+ f , err := lint .NewFile ("test.md" , src )
92+ require .NoError (t , err )
93+ r := & Rule {Level : 1 }
94+ diags := r .Check (f )
95+ require .Len (t , diags , 1 , "expected 1 diagnostic, got %d: %+v" , len (diags ), diags )
96+ require .Equal (t , "first line should be a level 1 heading, found blank line" , diags [0 ].Message )
97+ }
98+
99+ func TestCheck_WhitespaceBlankLineThenEmptyHeading (t * testing.T ) {
100+ src := []byte (" \n # \n " )
101+ f , err := lint .NewFile ("test.md" , src )
102+ require .NoError (t , err )
103+ r := & Rule {Level : 1 }
104+ diags := r .Check (f )
105+ require .Len (t , diags , 1 ,
106+ "whitespace-only blank line before empty heading should trigger, got %d: %+v" ,
107+ len (diags ), diags )
108+ require .Equal (t , "first line should be a level 1 heading, found blank line" , diags [0 ].Message )
109+ }
110+
111+ func TestCheck_BlankLineThenEmptyHeading (t * testing.T ) {
112+ src := []byte ("\n # \n " )
113+ f , err := lint .NewFile ("test.md" , src )
114+ require .NoError (t , err )
115+ r := & Rule {Level : 1 }
116+ diags := r .Check (f )
117+ require .Len (t , diags , 1 , "expected 1 diagnostic for empty heading after blank line, got %d: %+v" , len (diags ), diags )
118+ require .Equal (t , "first line should be a level 1 heading, found blank line" , diags [0 ].Message )
119+ }
120+
121+ func TestCheck_EmptyHeadingOnLine1 (t * testing.T ) {
122+ src := []byte ("# \n " )
123+ f , err := lint .NewFile ("test.md" , src )
124+ require .NoError (t , err )
125+ r := & Rule {Level : 1 }
126+ diags := r .Check (f )
127+ require .Len (t , diags , 0 , "empty heading on line 1 should not trigger diagnostic, got %d: %+v" , len (diags ), diags )
128+ }
129+
130+ func TestCheck_LevelZeroDefault (t * testing.T ) {
131+ src := []byte ("# Title\n " )
132+ f , err := lint .NewFile ("test.md" , src )
133+ require .NoError (t , err )
134+ r := & Rule {Level : 0 }
135+ diags := r .Check (f )
136+ require .Len (t , diags , 0 , "Level 0 should default to 1; expected 0 diagnostics, got %d: %+v" , len (diags ), diags )
48137}
49138
50139func TestCheck_WrongLevel (t * testing.T ) {
@@ -54,6 +143,7 @@ func TestCheck_WrongLevel(t *testing.T) {
54143 r := & Rule {Level : 1 }
55144 diags := r .Check (f )
56145 require .Len (t , diags , 1 , "expected 1 diagnostic, got %d: %+v" , len (diags ), diags )
146+ require .Equal (t , "first heading should be level 1, got 2" , diags [0 ].Message )
57147}
58148
59149func TestCheck_Level2Config (t * testing.T ) {
@@ -103,6 +193,12 @@ func TestApplySettings_LevelOutOfRange(t *testing.T) {
103193 require .Error (t , err , "expected error for level > 6" )
104194}
105195
196+ func TestApplySettings_LevelZero (t * testing.T ) {
197+ r := & Rule {Level : 1 }
198+ err := r .ApplySettings (map [string ]any {"level" : 0 })
199+ require .Error (t , err , "expected error for level 0" )
200+ }
201+
106202func TestApplySettings_UnknownKey (t * testing.T ) {
107203 r := & Rule {Level : 1 }
108204 err := r .ApplySettings (map [string ]any {"unknown" : true })
0 commit comments