Coverage for tests / unit / exceptions / test_exceptions.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Unit tests for custom exception hierarchy and messages.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6from assertpy import assert_that 

7 

8from lintro.exceptions.errors import ( 

9 ConfigurationError, 

10 FileAccessError, 

11 InvalidToolConfigError, 

12 InvalidToolOptionError, 

13 LintroError, 

14 ParserError, 

15 ToolExecutionError, 

16 ToolTimeoutError, 

17) 

18 

19 

20@pytest.mark.parametrize( 

21 "exception_class,message", 

22 [ 

23 (LintroError, "base error"), 

24 (InvalidToolConfigError, "invalid config"), 

25 (InvalidToolOptionError, "invalid option"), 

26 (ToolExecutionError, "execution failed"), 

27 (ToolTimeoutError, "timeout occurred"), 

28 (ParserError, "parsing failed"), 

29 (ConfigurationError, "config error"), 

30 (FileAccessError, "file not found"), 

31 ], 

32 ids=[ 

33 "LintroError", 

34 "InvalidToolConfigError", 

35 "InvalidToolOptionError", 

36 "ToolExecutionError", 

37 "ToolTimeoutError", 

38 "ParserError", 

39 "ConfigurationError", 

40 "FileAccessError", 

41 ], 

42) 

43def test_exception_inheritance_and_message(exception_class: type, message: str) -> None: 

44 """Test all exceptions inherit from LintroError and preserve messages. 

45 

46 Args: 

47 exception_class: Exception class to test. 

48 message: Error message to use. 

49 """ 

50 exc = exception_class(message) 

51 assert_that(exc).is_instance_of(LintroError) 

52 assert_that(exc).is_instance_of(Exception) 

53 assert_that(str(exc)).is_equal_to(message) 

54 

55 

56@pytest.mark.parametrize( 

57 "exception_class", 

58 [ 

59 LintroError, 

60 InvalidToolConfigError, 

61 InvalidToolOptionError, 

62 ToolExecutionError, 

63 ToolTimeoutError, 

64 ParserError, 

65 ConfigurationError, 

66 FileAccessError, 

67 ], 

68) 

69def test_exception_can_be_raised_and_caught(exception_class: type) -> None: 

70 """Test exceptions can be raised and caught properly. 

71 

72 Args: 

73 exception_class: Exception class to test. 

74 

75 Raises: 

76 exception_class: The exception being tested. 

77 """ 

78 with pytest.raises(exception_class) as exc_info: 

79 raise exception_class("test message") 

80 assert_that(str(exc_info.value)).is_equal_to("test message") 

81 

82 

83@pytest.mark.parametrize( 

84 "exception_class", 

85 [ 

86 InvalidToolConfigError, 

87 InvalidToolOptionError, 

88 ToolExecutionError, 

89 ToolTimeoutError, 

90 ParserError, 

91 ConfigurationError, 

92 FileAccessError, 

93 ], 

94) 

95def test_subclass_caught_by_base_exception(exception_class: type) -> None: 

96 """Test subclass exceptions can be caught by LintroError. 

97 

98 Args: 

99 exception_class: Exception class to test. 

100 

101 Raises: 

102 exception_class: The exception being tested. 

103 """ 

104 with pytest.raises(LintroError): 

105 raise exception_class("caught by base") 

106 

107 

108def test_exception_args_preserved() -> None: 

109 """Test exception args are preserved for introspection.""" 

110 exc = ToolExecutionError("tool failed", "extra", "info") 

111 assert_that(exc.args).is_equal_to(("tool failed", "extra", "info")) 

112 

113 

114def test_exception_chaining() -> None: 

115 """Test exceptions can be chained with __cause__.""" 

116 original = ValueError("original error") 

117 try: 

118 try: 

119 raise original 

120 except ValueError as e: 

121 raise ParserError("parsing failed") from e 

122 except ParserError as pe: 

123 assert_that(pe.__cause__).is_equal_to(original) 

124 assert_that(str(pe.__cause__)).is_equal_to("original error")