@@ -273,3 +273,77 @@ fn stdin_inline_config_warning_non_fatal_by_default() {
273273 "stdin inline config warning must not affect exit code by default"
274274 ) ;
275275}
276+
277+ /// --silent suppresses the printed notice but the flag must still exit 2.
278+ #[ test]
279+ fn deny_config_warnings_silent_still_exits_two ( ) {
280+ let dir = tempdir ( ) . unwrap ( ) ;
281+ std:: fs:: write ( dir. path ( ) . join ( "clean.md" ) , "# Title\n \n Text.\n " ) . unwrap ( ) ;
282+ std:: fs:: write ( dir. path ( ) . join ( ".rumdl.toml" ) , "[global]\n enable = [\" MD999\" ]\n " ) . unwrap ( ) ;
283+
284+ let output = rumdl ( )
285+ . args ( [ "check" , "--no-cache" , "--deny-config-warnings" , "--silent" , "clean.md" ] )
286+ . current_dir ( dir. path ( ) )
287+ . output ( )
288+ . expect ( "run rumdl check" ) ;
289+ assert_eq ! ( output. status. code( ) , Some ( TOOL_ERROR ) ) ;
290+ assert ! (
291+ String :: from_utf8_lossy( & output. stderr) . is_empty( ) ,
292+ "--silent must suppress the printed warning even while the flag makes it fatal. stderr: {}" ,
293+ String :: from_utf8_lossy( & output. stderr)
294+ ) ;
295+ }
296+
297+ /// A config warning outranks a real Markdown violation: exit 2, not 1.
298+ #[ test]
299+ fn deny_config_warnings_take_precedence_over_violations ( ) {
300+ let dir = tempdir ( ) . unwrap ( ) ;
301+ // MD041: first line is not a top-level heading -> a real violation.
302+ std:: fs:: write ( dir. path ( ) . join ( "dirty.md" ) , "no heading here\n " ) . unwrap ( ) ;
303+ std:: fs:: write ( dir. path ( ) . join ( ".rumdl.toml" ) , "[global]\n enable = [\" MD999\" ]\n " ) . unwrap ( ) ;
304+
305+ let status = rumdl ( )
306+ . args ( [ "check" , "--no-cache" , "--deny-config-warnings" , "dirty.md" ] )
307+ . current_dir ( dir. path ( ) )
308+ . status ( )
309+ . expect ( "run rumdl check" ) ;
310+ assert_eq ! (
311+ status. code( ) ,
312+ Some ( TOOL_ERROR ) ,
313+ "a config problem must exit 2 even when Markdown violations (exit 1) are also present"
314+ ) ;
315+ }
316+
317+ /// Sanity check the precedence test's premise: the same violation WITHOUT a
318+ /// config problem exits 1, not 2.
319+ #[ test]
320+ fn violations_without_config_problem_exit_one ( ) {
321+ let dir = tempdir ( ) . unwrap ( ) ;
322+ std:: fs:: write ( dir. path ( ) . join ( "dirty.md" ) , "no heading here\n " ) . unwrap ( ) ;
323+
324+ let status = rumdl ( )
325+ . args ( [ "check" , "--no-cache" , "--deny-config-warnings" , "dirty.md" ] )
326+ . current_dir ( dir. path ( ) )
327+ . status ( )
328+ . expect ( "run rumdl check" ) ;
329+ assert_eq ! (
330+ status. code( ) ,
331+ Some ( 1 ) ,
332+ "a plain Markdown violation exits 1; the flag only changes config-problem exits"
333+ ) ;
334+ }
335+
336+ /// `fmt` shares config loading and the flag; it also exits 2 on a config problem.
337+ #[ test]
338+ fn deny_config_warnings_applies_to_fmt ( ) {
339+ let dir = tempdir ( ) . unwrap ( ) ;
340+ std:: fs:: write ( dir. path ( ) . join ( "clean.md" ) , "# Title\n \n Text.\n " ) . unwrap ( ) ;
341+ std:: fs:: write ( dir. path ( ) . join ( ".rumdl.toml" ) , "[global]\n enable = [\" MD999\" ]\n " ) . unwrap ( ) ;
342+
343+ let status = rumdl ( )
344+ . args ( [ "fmt" , "--no-cache" , "--deny-config-warnings" , "clean.md" ] )
345+ . current_dir ( dir. path ( ) )
346+ . status ( )
347+ . expect ( "run rumdl fmt" ) ;
348+ assert_eq ! ( status. code( ) , Some ( TOOL_ERROR ) ) ;
349+ }
0 commit comments