@@ -408,3 +408,124 @@ def test_setenv_without_value() -> None:
408408 matches += 1
409409 assert matches == 1
410410
411+
412+ # Parse Error Context Tests
413+
414+
415+ def test_parse_error_includes_context_field () -> None :
416+ """
417+ Test that parse errors include the 'context' field
418+ This verifies that error responses contain context information
419+ """
420+ rule_text = """
421+ SecRule ARGS @rx "missing quotes
422+ """
423+ result = parser .process_from_str (rule_text )
424+
425+ # Should return error dict, not a model
426+ assert isinstance (result , dict ), "Expected parse error to return dict"
427+
428+ # Verify all error fields are present
429+ assert 'line' in result , "Error should include line number"
430+ assert 'col' in result , "Error should include column number"
431+ assert 'message' in result , "Error should include error message"
432+ assert 'context' in result , "Error should include context field"
433+
434+ # Verify context is not None or empty
435+ assert result ['context' ] is not None , "Context should not be None"
436+ assert len (str (result ['context' ])) > 0 , "Context should not be empty"
437+
438+
439+ def test_parse_error_context_with_invalid_directive () -> None :
440+ """
441+ Test that invalid directive syntax provides context
442+ """
443+ rule_text = """
444+ InvalidDirective ARGS "@rx test"
445+ """
446+ result = parser .process_from_str (rule_text )
447+
448+ assert isinstance (result , dict ), "Expected parse error"
449+ assert 'context' in result , "Error should include context"
450+ assert result ['line' ] > 0 , "Should have line number"
451+ assert result ['col' ] > 0 , "Should have column number"
452+
453+
454+ def test_parse_error_context_with_missing_operator () -> None :
455+ """
456+ Test that incomplete operator syntax provides context
457+ """
458+ rule_text = """
459+ SecRule ARGS @ "id:1,deny"
460+ """
461+ result = parser .process_from_str (rule_text )
462+
463+ assert isinstance (result , dict ), "Expected parse error"
464+ assert 'context' in result , "Error should include context"
465+ assert 'message' in result , "Error should include message"
466+
467+
468+ def test_parse_error_context_with_malformed_actions () -> None :
469+ """
470+ Test that malformed actions provide context
471+ """
472+ rule_text = """
473+ SecRule ARGS "@rx test" "id:,phase:2"
474+ """
475+ result = parser .process_from_str (rule_text )
476+
477+ assert isinstance (result , dict ), "Expected parse error"
478+ assert 'context' in result , "Error should include context"
479+ assert result ['line' ] > 0 , "Should have line number"
480+
481+
482+ def test_parse_error_context_with_unclosed_quotes () -> None :
483+ """
484+ Test that unclosed quotes provide context
485+ """
486+ rule_text = """
487+ SecRule ARGS "@rx test" "id:1,msg:'unclosed message
488+ """
489+ result = parser .process_from_str (rule_text )
490+
491+ assert isinstance (result , dict ), "Expected parse error"
492+ assert 'context' in result , "Error should include context"
493+ assert 'line' in result , "Error should include line"
494+ assert 'col' in result , "Error should include col"
495+
496+
497+ def test_successful_parse_does_not_return_dict () -> None :
498+ """
499+ Test that successful parses return model, not dict
500+ This ensures we can distinguish between success and error
501+ """
502+ rule_text = """
503+ SecRule ARGS "@rx test" "id:1,phase:2,deny"
504+ """
505+ result = parser .process_from_str (rule_text )
506+
507+ # Successful parse should NOT return a dict
508+ assert not isinstance (result , dict ), "Successful parse should return model, not dict"
509+
510+ # Should have rules attribute
511+ assert hasattr (result , 'rules' ), "Model should have rules attribute"
512+ assert len (result .rules ) > 0 , "Should have at least one rule"
513+
514+
515+ def test_parse_error_context_multiline () -> None :
516+ """
517+ Test that parse errors in multiline rules include context
518+ """
519+ rule_text = """
520+ SecRule ARGS "@rx test" \\
521+ "id:1,\\
522+ phase:2,\\
523+ invalid_action_here,\\
524+ deny"
525+ """
526+ result = parser .process_from_str (rule_text )
527+
528+ assert isinstance (result , dict ), "Expected parse error"
529+ assert 'context' in result , "Error should include context"
530+ assert result ['line' ] > 0 , "Should have line number"
531+
0 commit comments