fishertools 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. fishertools/__init__.py +82 -0
  2. fishertools/config/__init__.py +24 -0
  3. fishertools/config/manager.py +247 -0
  4. fishertools/config/models.py +96 -0
  5. fishertools/config/parser.py +265 -0
  6. fishertools/decorators.py +93 -0
  7. fishertools/documentation/__init__.py +38 -0
  8. fishertools/documentation/api.py +242 -0
  9. fishertools/documentation/generator.py +502 -0
  10. fishertools/documentation/models.py +126 -0
  11. fishertools/documentation/visual.py +583 -0
  12. fishertools/errors/__init__.py +29 -0
  13. fishertools/errors/exceptions.py +191 -0
  14. fishertools/errors/explainer.py +303 -0
  15. fishertools/errors/formatters.py +386 -0
  16. fishertools/errors/models.py +228 -0
  17. fishertools/errors/patterns.py +119 -0
  18. fishertools/errors/recovery.py +467 -0
  19. fishertools/examples/__init__.py +22 -0
  20. fishertools/examples/models.py +118 -0
  21. fishertools/examples/repository.py +770 -0
  22. fishertools/helpers.py +116 -0
  23. fishertools/integration.py +451 -0
  24. fishertools/learn/__init__.py +18 -0
  25. fishertools/learn/examples.py +550 -0
  26. fishertools/learn/tips.py +281 -0
  27. fishertools/learning/__init__.py +32 -0
  28. fishertools/learning/core.py +349 -0
  29. fishertools/learning/models.py +112 -0
  30. fishertools/learning/progress.py +314 -0
  31. fishertools/learning/session.py +500 -0
  32. fishertools/learning/tutorial.py +626 -0
  33. fishertools/legacy/__init__.py +76 -0
  34. fishertools/legacy/deprecated.py +261 -0
  35. fishertools/legacy/deprecation.py +149 -0
  36. fishertools/safe/__init__.py +16 -0
  37. fishertools/safe/collections.py +242 -0
  38. fishertools/safe/files.py +240 -0
  39. fishertools/safe/strings.py +15 -0
  40. fishertools/utils.py +57 -0
  41. fishertools-0.2.1.dist-info/METADATA +256 -0
  42. fishertools-0.2.1.dist-info/RECORD +81 -0
  43. fishertools-0.2.1.dist-info/WHEEL +5 -0
  44. fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
  45. fishertools-0.2.1.dist-info/top_level.txt +2 -0
  46. tests/__init__.py +6 -0
  47. tests/conftest.py +25 -0
  48. tests/test_config/__init__.py +3 -0
  49. tests/test_config/test_basic_config.py +57 -0
  50. tests/test_config/test_config_error_handling.py +287 -0
  51. tests/test_config/test_config_properties.py +435 -0
  52. tests/test_documentation/__init__.py +3 -0
  53. tests/test_documentation/test_documentation_properties.py +253 -0
  54. tests/test_documentation/test_visual_documentation_properties.py +444 -0
  55. tests/test_errors/__init__.py +3 -0
  56. tests/test_errors/test_api.py +301 -0
  57. tests/test_errors/test_error_handling.py +354 -0
  58. tests/test_errors/test_explainer.py +173 -0
  59. tests/test_errors/test_formatters.py +338 -0
  60. tests/test_errors/test_models.py +248 -0
  61. tests/test_errors/test_patterns.py +270 -0
  62. tests/test_examples/__init__.py +3 -0
  63. tests/test_examples/test_example_repository_properties.py +204 -0
  64. tests/test_examples/test_specific_examples.py +303 -0
  65. tests/test_integration.py +298 -0
  66. tests/test_integration_enhancements.py +462 -0
  67. tests/test_learn/__init__.py +3 -0
  68. tests/test_learn/test_examples.py +221 -0
  69. tests/test_learn/test_tips.py +285 -0
  70. tests/test_learning/__init__.py +3 -0
  71. tests/test_learning/test_interactive_learning_properties.py +337 -0
  72. tests/test_learning/test_learning_system_properties.py +194 -0
  73. tests/test_learning/test_progress_tracking_properties.py +279 -0
  74. tests/test_legacy/__init__.py +3 -0
  75. tests/test_legacy/test_backward_compatibility.py +236 -0
  76. tests/test_legacy/test_deprecation_warnings.py +208 -0
  77. tests/test_safe/__init__.py +3 -0
  78. tests/test_safe/test_collections_properties.py +189 -0
  79. tests/test_safe/test_files.py +104 -0
  80. tests/test_structure.py +58 -0
  81. tests/test_structure_enhancements.py +115 -0
@@ -0,0 +1,248 @@
1
+ """
2
+ Tests for error explanation data models.
3
+ """
4
+
5
+ import pytest
6
+ from hypothesis import given, strategies as st
7
+
8
+ from fishertools.errors.models import ErrorPattern, ErrorExplanation, ExplainerConfig
9
+
10
+
11
+ # Hypothesis strategies for generating test data
12
+ exception_types = st.sampled_from([
13
+ TypeError, ValueError, AttributeError, IndexError, KeyError, ImportError, SyntaxError
14
+ ])
15
+
16
+ russian_text = st.text(
17
+ alphabet="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ ",
18
+ min_size=10, max_size=200
19
+ )
20
+
21
+ code_example = st.text(
22
+ alphabet="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789()[]{}=+-*/.,:;#' \n",
23
+ min_size=5, max_size=100
24
+ )
25
+
26
+
27
+ class TestErrorPattern:
28
+ """Tests for ErrorPattern data model."""
29
+
30
+ def test_error_pattern_creation(self):
31
+ """Test basic ErrorPattern creation."""
32
+ pattern = ErrorPattern(
33
+ error_type=TypeError,
34
+ error_keywords=["cannot", "interpreted"],
35
+ explanation="Объяснение типа ошибки",
36
+ tip="Совет по исправлению",
37
+ example="# Пример кода",
38
+ common_causes=["Неправильный тип данных"]
39
+ )
40
+
41
+ assert pattern.error_type == TypeError
42
+ assert "cannot" in pattern.error_keywords
43
+ assert pattern.explanation == "Объяснение типа ошибки"
44
+
45
+ def test_pattern_matches_correct_exception(self):
46
+ """Test that pattern correctly matches exceptions."""
47
+ pattern = ErrorPattern(
48
+ error_type=TypeError,
49
+ error_keywords=["cannot", "interpreted"],
50
+ explanation="Test explanation",
51
+ tip="Test tip",
52
+ example="# Test example",
53
+ common_causes=["Test cause"]
54
+ )
55
+
56
+ # Should match TypeError with matching keywords
57
+ exception = TypeError("'str' object cannot be interpreted as an integer")
58
+ assert pattern.matches(exception) is True
59
+
60
+ # Should not match different exception type
61
+ value_error = ValueError("some error")
62
+ assert pattern.matches(value_error) is False
63
+
64
+ # Should not match TypeError without matching keywords
65
+ type_error_no_match = TypeError("different error message")
66
+ assert pattern.matches(type_error_no_match) is False
67
+
68
+
69
+ class TestErrorExplanation:
70
+ """Tests for ErrorExplanation data model."""
71
+
72
+ def test_error_explanation_creation(self):
73
+ """Test basic ErrorExplanation creation."""
74
+ explanation = ErrorExplanation(
75
+ original_error="TypeError: test error",
76
+ error_type="TypeError",
77
+ simple_explanation="Простое объяснение",
78
+ fix_tip="Совет по исправлению",
79
+ code_example="# Пример кода"
80
+ )
81
+
82
+ assert explanation.original_error == "TypeError: test error"
83
+ assert explanation.error_type == "TypeError"
84
+ assert explanation.simple_explanation == "Простое объяснение"
85
+
86
+ def test_error_explanation_to_dict(self):
87
+ """Test ErrorExplanation serialization to dictionary."""
88
+ explanation = ErrorExplanation(
89
+ original_error="Test error",
90
+ error_type="TestError",
91
+ simple_explanation="Test explanation",
92
+ fix_tip="Test tip",
93
+ code_example="# Test code"
94
+ )
95
+
96
+ result_dict = explanation.to_dict()
97
+
98
+ assert isinstance(result_dict, dict)
99
+ assert result_dict["original_error"] == "Test error"
100
+ assert result_dict["error_type"] == "TestError"
101
+ assert result_dict["additional_info"] is None
102
+
103
+
104
+ class TestExplainerConfig:
105
+ """Tests for ExplainerConfig data model."""
106
+
107
+ def test_explainer_config_defaults(self):
108
+ """Test ExplainerConfig default values."""
109
+ config = ExplainerConfig()
110
+
111
+ assert config.language == 'ru'
112
+ assert config.format_type == 'console'
113
+ assert config.show_original_error is True
114
+ assert config.show_traceback is False
115
+ assert config.use_colors is True
116
+ assert config.max_explanation_length == 200
117
+
118
+ def test_explainer_config_custom_values(self):
119
+ """Test ExplainerConfig with custom values."""
120
+ config = ExplainerConfig(
121
+ language='en',
122
+ format_type='json',
123
+ show_original_error=False,
124
+ use_colors=False,
125
+ max_explanation_length=150
126
+ )
127
+
128
+ assert config.language == 'en'
129
+ assert config.format_type == 'json'
130
+ assert config.show_original_error is False
131
+ assert config.use_colors is False
132
+ assert config.max_explanation_length == 150
133
+
134
+ def test_explainer_config_to_dict(self):
135
+ """Test ExplainerConfig serialization to dictionary."""
136
+ config = ExplainerConfig()
137
+ result_dict = config.to_dict()
138
+
139
+ assert isinstance(result_dict, dict)
140
+ assert result_dict["language"] == 'ru'
141
+ assert result_dict["format_type"] == 'console'
142
+
143
+ @given(
144
+ language=st.sampled_from(['ru', 'en']),
145
+ format_type=st.sampled_from(['console', 'json', 'plain']),
146
+ show_original_error=st.booleans(),
147
+ show_traceback=st.booleans(),
148
+ use_colors=st.booleans(),
149
+ max_explanation_length=st.integers(min_value=1, max_value=1000)
150
+ )
151
+ @pytest.mark.property
152
+ def test_explainer_config_property_serialization(self, language, format_type,
153
+ show_original_error, show_traceback,
154
+ use_colors, max_explanation_length):
155
+ """
156
+ Property test: ExplainerConfig should always serialize to dict correctly.
157
+ Feature: fishertools-refactor, Property: Config serialization consistency
158
+ """
159
+ config = ExplainerConfig(
160
+ language=language,
161
+ format_type=format_type,
162
+ show_original_error=show_original_error,
163
+ show_traceback=show_traceback,
164
+ use_colors=use_colors,
165
+ max_explanation_length=max_explanation_length
166
+ )
167
+
168
+ result_dict = config.to_dict()
169
+
170
+ # Property: serialization should always produce a dict with correct keys
171
+ assert isinstance(result_dict, dict)
172
+ assert result_dict["language"] == language
173
+ assert result_dict["format_type"] == format_type
174
+ assert result_dict["show_original_error"] == show_original_error
175
+ assert result_dict["show_traceback"] == show_traceback
176
+ assert result_dict["use_colors"] == use_colors
177
+ assert result_dict["max_explanation_length"] == max_explanation_length
178
+
179
+
180
+ @pytest.mark.property
181
+ class TestCompleteErrorExplanationStructure:
182
+ """Property tests for complete error explanation structure."""
183
+
184
+ @given(
185
+ original_error=st.text(min_size=0, max_size=200), # Allow empty strings
186
+ error_type=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()), # Ensure non-empty after strip
187
+ simple_explanation=russian_text,
188
+ fix_tip=russian_text,
189
+ code_example=code_example,
190
+ additional_info=st.one_of(st.none(), russian_text)
191
+ )
192
+ def test_complete_error_explanation_structure(self, original_error, error_type,
193
+ simple_explanation, fix_tip,
194
+ code_example, additional_info):
195
+ """
196
+ Property 3: Complete Error Explanation Structure
197
+ For any supported Python exception, the explain_error() function should produce
198
+ output containing a Russian explanation, a practical fix tip, and a relevant code example.
199
+
200
+ Feature: fishertools-refactor, Property 3: Complete Error Explanation Structure
201
+ Validates: Requirements 2.1, 2.2, 2.3
202
+ """
203
+ # Create ErrorExplanation with all required components
204
+ explanation = ErrorExplanation(
205
+ original_error=original_error,
206
+ error_type=error_type,
207
+ simple_explanation=simple_explanation,
208
+ fix_tip=fix_tip,
209
+ code_example=code_example,
210
+ additional_info=additional_info
211
+ )
212
+
213
+ # Property: ErrorExplanation must contain all required components
214
+ assert explanation.original_error is not None
215
+ assert explanation.error_type is not None
216
+ assert explanation.simple_explanation is not None
217
+ assert explanation.fix_tip is not None
218
+ assert explanation.code_example is not None
219
+
220
+ # Property: All text fields must be non-empty strings (except original_error which can be empty)
221
+ assert isinstance(explanation.original_error, str)
222
+ assert isinstance(explanation.error_type, str)
223
+ assert isinstance(explanation.simple_explanation, str)
224
+ assert isinstance(explanation.fix_tip, str)
225
+ assert isinstance(explanation.code_example, str)
226
+
227
+ # original_error can be empty, but others must have content
228
+ assert len(explanation.error_type.strip()) > 0
229
+ assert len(explanation.simple_explanation.strip()) > 0
230
+ assert len(explanation.fix_tip.strip()) > 0
231
+ assert len(explanation.code_example.strip()) > 0
232
+
233
+ # Property: Serialization must preserve all components
234
+ serialized = explanation.to_dict()
235
+ assert "original_error" in serialized
236
+ assert "error_type" in serialized
237
+ assert "simple_explanation" in serialized
238
+ assert "fix_tip" in serialized
239
+ assert "code_example" in serialized
240
+ assert "additional_info" in serialized
241
+
242
+ # Property: Serialized data must match original data
243
+ assert serialized["original_error"] == explanation.original_error
244
+ assert serialized["error_type"] == explanation.error_type
245
+ assert serialized["simple_explanation"] == explanation.simple_explanation
246
+ assert serialized["fix_tip"] == explanation.fix_tip
247
+ assert serialized["code_example"] == explanation.code_example
248
+ assert serialized["additional_info"] == explanation.additional_info
@@ -0,0 +1,270 @@
1
+ """
2
+ Unit tests for error pattern matching functionality.
3
+
4
+ Tests specific error patterns and their matching behavior for common Python exceptions.
5
+ """
6
+
7
+ import pytest
8
+ from fishertools.errors.patterns import load_default_patterns, DEFAULT_PATTERNS
9
+ from fishertools.errors.explainer import ErrorExplainer
10
+ from fishertools.errors.models import ErrorPattern
11
+
12
+
13
+ class TestErrorPatternMatching:
14
+ """Unit tests for specific error pattern matching."""
15
+
16
+ def test_load_default_patterns(self):
17
+ """Test that default patterns are loaded correctly."""
18
+ patterns = load_default_patterns()
19
+
20
+ # Should have patterns for all required exception types
21
+ assert len(patterns) > 0
22
+
23
+ # Check that we have patterns for all required exception types
24
+ exception_types = {pattern.error_type for pattern in patterns}
25
+ required_types = {TypeError, ValueError, AttributeError, IndexError, KeyError, ImportError, SyntaxError}
26
+
27
+ assert required_types.issubset(exception_types), f"Missing patterns for: {required_types - exception_types}"
28
+
29
+ def test_type_error_operand_pattern_matching(self):
30
+ """Test TypeError pattern matching for operand type errors."""
31
+ explainer = ErrorExplainer()
32
+
33
+ # Test unsupported operand type error
34
+ exception = TypeError("unsupported operand type(s) for +: 'int' and 'str'")
35
+ explanation = explainer.explain(exception)
36
+
37
+ assert explanation.error_type == "TypeError"
38
+ assert "несовместимыми типами" in explanation.simple_explanation
39
+ assert "преобразования типов" in explanation.fix_tip
40
+ assert "int(" in explanation.code_example or "str(" in explanation.code_example
41
+
42
+ def test_type_error_function_arguments_pattern_matching(self):
43
+ """Test TypeError pattern matching for function argument errors."""
44
+ explainer = ErrorExplainer()
45
+
46
+ # Test missing positional argument error
47
+ exception = TypeError("greet() missing 1 required positional argument: 'age'")
48
+ explanation = explainer.explain(exception)
49
+
50
+ assert explanation.error_type == "TypeError"
51
+ assert "неправильным количеством аргументов" in explanation.simple_explanation
52
+ assert "правильное количество аргументов" in explanation.fix_tip
53
+ assert "def " in explanation.code_example
54
+
55
+ def test_type_error_not_callable_pattern_matching(self):
56
+ """Test TypeError pattern matching for 'not callable' errors."""
57
+ explainer = ErrorExplainer()
58
+
59
+ # Test object not callable error
60
+ exception = TypeError("'list' object is not callable")
61
+ explanation = explainer.explain(exception)
62
+
63
+ assert explanation.error_type == "TypeError"
64
+ assert "не является" in explanation.simple_explanation and "функцией" in explanation.simple_explanation
65
+ assert "функцию" in explanation.fix_tip
66
+ assert "len(" in explanation.code_example
67
+
68
+ def test_value_error_conversion_pattern_matching(self):
69
+ """Test ValueError pattern matching for conversion errors."""
70
+ explainer = ErrorExplainer()
71
+
72
+ # Test invalid literal for int conversion
73
+ exception = ValueError("invalid literal for int() with base 10: 'abc'")
74
+ explanation = explainer.explain(exception)
75
+
76
+ assert explanation.error_type == "ValueError"
77
+ assert "преобразовать строку в число" in explanation.simple_explanation
78
+ assert "цифры" in explanation.fix_tip
79
+ assert "isdigit()" in explanation.code_example
80
+
81
+ def test_value_error_unpacking_pattern_matching(self):
82
+ """Test ValueError pattern matching for unpacking errors."""
83
+ explainer = ErrorExplainer()
84
+
85
+ # Test too many values to unpack
86
+ exception = ValueError("too many values to unpack (expected 2)")
87
+ explanation = explainer.explain(exception)
88
+
89
+ assert explanation.error_type == "ValueError"
90
+ assert "Количество переменных" in explanation.simple_explanation
91
+ assert "количество переменных" in explanation.fix_tip
92
+ assert "=" in explanation.code_example
93
+
94
+ def test_attribute_error_pattern_matching(self):
95
+ """Test AttributeError pattern matching."""
96
+ explainer = ErrorExplainer()
97
+
98
+ # Test attribute error
99
+ exception = AttributeError("'str' object has no attribute 'append'")
100
+ explanation = explainer.explain(exception)
101
+
102
+ assert explanation.error_type == "AttributeError"
103
+ assert "атрибуту или методу" in explanation.simple_explanation
104
+ assert "правильность написания" in explanation.fix_tip
105
+ assert "dir()" in explanation.fix_tip
106
+
107
+ def test_index_error_pattern_matching(self):
108
+ """Test IndexError pattern matching."""
109
+ explainer = ErrorExplainer()
110
+
111
+ # Test list index out of range
112
+ exception = IndexError("list index out of range")
113
+ explanation = explainer.explain(exception)
114
+
115
+ assert explanation.error_type == "IndexError"
116
+ assert "индексу, который не существует" in explanation.simple_explanation
117
+ assert "len()" in explanation.fix_tip
118
+ assert "len(" in explanation.code_example
119
+
120
+ def test_key_error_pattern_matching(self):
121
+ """Test KeyError pattern matching."""
122
+ explainer = ErrorExplainer()
123
+
124
+ # Test key error
125
+ exception = KeyError("'missing_key'")
126
+ explanation = explainer.explain(exception)
127
+
128
+ assert explanation.error_type == "KeyError"
129
+ assert "ключу, которого в словаре не существует" in explanation.simple_explanation
130
+ assert "get()" in explanation.fix_tip or "'in'" in explanation.fix_tip
131
+ assert "get(" in explanation.code_example or " in " in explanation.code_example
132
+
133
+ def test_import_error_pattern_matching(self):
134
+ """Test ImportError pattern matching."""
135
+ explainer = ErrorExplainer()
136
+
137
+ # Test module not found error
138
+ exception = ImportError("No module named 'nonexistent_module'")
139
+ explanation = explainer.explain(exception)
140
+
141
+ assert explanation.error_type == "ImportError"
142
+ assert "не может найти модуль" in explanation.simple_explanation
143
+ assert "установлен" in explanation.fix_tip
144
+ assert "pip install" in explanation.code_example or "import" in explanation.code_example
145
+
146
+ def test_syntax_error_pattern_matching(self):
147
+ """Test SyntaxError pattern matching."""
148
+ explainer = ErrorExplainer()
149
+
150
+ # Test syntax error
151
+ exception = SyntaxError("invalid syntax")
152
+ explanation = explainer.explain(exception)
153
+
154
+ assert explanation.error_type == "SyntaxError"
155
+ assert "синтаксическая ошибка" in explanation.simple_explanation
156
+ assert "скобки" in explanation.fix_tip or "отступы" in explanation.fix_tip
157
+ assert ":" in explanation.code_example
158
+
159
+
160
+ class TestErrorPatternValidation:
161
+ """Tests for error pattern validation and structure."""
162
+
163
+ def test_all_patterns_have_required_fields(self):
164
+ """Test that all patterns have required fields populated."""
165
+ patterns = load_default_patterns()
166
+
167
+ for pattern in patterns:
168
+ assert isinstance(pattern, ErrorPattern)
169
+ assert pattern.error_type is not None
170
+ # Allow empty keywords for certain patterns (like KeyError)
171
+ assert pattern.error_keywords is not None
172
+ assert pattern.explanation.strip() != ""
173
+ assert pattern.tip.strip() != ""
174
+ assert pattern.example.strip() != ""
175
+ assert len(pattern.common_causes) > 0
176
+
177
+ def test_patterns_have_russian_content(self):
178
+ """Test that all patterns contain Russian text."""
179
+ patterns = load_default_patterns()
180
+ cyrillic_chars = set('абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ')
181
+
182
+ for pattern in patterns:
183
+ # Check explanation contains Russian text
184
+ assert any(char in cyrillic_chars for char in pattern.explanation)
185
+ # Check tip contains Russian text
186
+ assert any(char in cyrillic_chars for char in pattern.tip)
187
+
188
+ def test_pattern_matching_logic(self):
189
+ """Test the pattern matching logic works correctly."""
190
+ patterns = load_default_patterns()
191
+
192
+ # Find a TypeError pattern for operand types
193
+ type_error_pattern = None
194
+ for pattern in patterns:
195
+ if pattern.error_type == TypeError and "operand" in pattern.error_keywords[0]:
196
+ type_error_pattern = pattern
197
+ break
198
+
199
+ assert type_error_pattern is not None
200
+
201
+ # Test that it matches appropriate exceptions
202
+ matching_exception = TypeError("unsupported operand type(s) for +: 'int' and 'str'")
203
+ assert type_error_pattern.matches(matching_exception)
204
+
205
+ # Test that it doesn't match inappropriate exceptions
206
+ non_matching_exception = ValueError("invalid literal")
207
+ assert not type_error_pattern.matches(non_matching_exception)
208
+
209
+ # Test that it doesn't match TypeError with different message
210
+ different_type_error = TypeError("takes 1 positional argument but 2 were given")
211
+ # This should not match the operand pattern (should match a different TypeError pattern)
212
+ assert not type_error_pattern.matches(different_type_error)
213
+
214
+ def test_pattern_coverage_for_requirements(self):
215
+ """Test that we have patterns covering all required exception types from requirements."""
216
+ patterns = load_default_patterns()
217
+ covered_types = {pattern.error_type for pattern in patterns}
218
+
219
+ # Requirements 6.1-6.7 specify these exception types
220
+ required_types = {
221
+ TypeError, # 6.1
222
+ ValueError, # 6.2
223
+ AttributeError, # 6.3
224
+ IndexError, # 6.4
225
+ KeyError, # 6.5
226
+ ImportError, # 6.6
227
+ SyntaxError # 6.7
228
+ }
229
+
230
+ assert required_types.issubset(covered_types), f"Missing coverage for: {required_types - covered_types}"
231
+
232
+ # Ensure we have multiple patterns for TypeError (most common)
233
+ type_error_patterns = [p for p in patterns if p.error_type == TypeError]
234
+ assert len(type_error_patterns) >= 2, "Should have multiple TypeError patterns for different scenarios"
235
+
236
+
237
+ class TestPatternIntegration:
238
+ """Integration tests for patterns with the explainer system."""
239
+
240
+ def test_explainer_uses_patterns_correctly(self):
241
+ """Test that explainer correctly uses loaded patterns."""
242
+ explainer = ErrorExplainer()
243
+
244
+ # Verify patterns are loaded
245
+ assert len(explainer.patterns) > 0
246
+
247
+ # Test with a specific exception that should match a pattern
248
+ exception = TypeError("unsupported operand type(s) for +: 'int' and 'str'")
249
+ explanation = explainer.explain(exception)
250
+
251
+ # Should get a pattern-based explanation, not fallback
252
+ assert "несовместимыми типами" in explanation.simple_explanation
253
+ assert explanation.additional_info is not None
254
+ assert "Частые причины:" in explanation.additional_info
255
+
256
+ def test_explainer_fallback_when_no_pattern_matches(self):
257
+ """Test that explainer falls back correctly when no pattern matches."""
258
+ explainer = ErrorExplainer()
259
+
260
+ # Create an exception that won't match any pattern
261
+ class UnknownError(Exception):
262
+ pass
263
+
264
+ exception = UnknownError("some unknown error")
265
+ explanation = explainer.explain(exception)
266
+
267
+ # Should get fallback explanation
268
+ assert "UnknownError" in explanation.simple_explanation
269
+ assert "что-то пошло не так" in explanation.simple_explanation
270
+ assert explanation.additional_info is not None
@@ -0,0 +1,3 @@
1
+ """
2
+ Tests for the examples repository module.
3
+ """