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,462 @@
1
+ """
2
+ Integration tests for fishertools enhancements.
3
+
4
+ Tests the interaction between all enhancement components:
5
+ - Learning System integration with Tutorial Engine and Example Repository
6
+ - Documentation Generator integration with Visual Documentation
7
+ - Error recovery and graceful degradation
8
+ - End-to-end learning scenarios
9
+ """
10
+
11
+ import pytest
12
+ import tempfile
13
+ import os
14
+ from unittest.mock import Mock, patch
15
+ from fishertools.integration import FishertoolsIntegration, get_integration, reset_integration
16
+ from fishertools.learning.models import DifficultyLevel
17
+ from fishertools.errors.recovery import ErrorSeverity, RecoveryStrategy
18
+
19
+
20
+ class TestComponentIntegration:
21
+ """Test integration between different components."""
22
+
23
+ def setup_method(self):
24
+ """Set up test environment."""
25
+ reset_integration()
26
+ self.integration = FishertoolsIntegration(project_name="test_project")
27
+
28
+ def teardown_method(self):
29
+ """Clean up after tests."""
30
+ reset_integration()
31
+
32
+ def test_learning_system_tutorial_engine_integration(self):
33
+ """Test that Learning System properly integrates with Tutorial Engine."""
34
+ # Start a learning session
35
+ session = self.integration.start_learning_session("variables", "beginner", "test_user")
36
+
37
+ assert session is not None
38
+ assert session.topic == "variables"
39
+ assert session.level == DifficultyLevel.BEGINNER
40
+ assert len(session.exercises) > 0
41
+
42
+ # Verify tutorial engine is connected
43
+ assert self.integration.learning_system._tutorial_engine is not None
44
+ assert self.integration.learning_system._tutorial_engine == self.integration.tutorial_engine
45
+
46
+ def test_learning_system_example_repository_integration(self):
47
+ """Test that Learning System integrates with Example Repository."""
48
+ # Get examples for a topic
49
+ examples = self.integration.example_repository.get_examples_by_topic("lists")
50
+ assert len(examples) > 0
51
+
52
+ # Start session and verify examples are used
53
+ session = self.integration.start_learning_session("lists", "beginner", "test_user")
54
+
55
+ # Should have interactive session if examples are available
56
+ if hasattr(session, 'interactive_session') and session.interactive_session:
57
+ assert session.interactive_session is not None
58
+
59
+ def test_tutorial_engine_example_repository_integration(self):
60
+ """Test that Tutorial Engine can access Example Repository."""
61
+ # Set up the integration
62
+ self.integration.tutorial_engine._example_repository = self.integration.example_repository
63
+
64
+ # Test getting related examples
65
+ related_examples = self.integration.tutorial_engine.get_related_examples("variables")
66
+
67
+ # Should return examples if available
68
+ assert isinstance(related_examples, list)
69
+
70
+ def test_documentation_generator_visual_integration(self):
71
+ """Test that Documentation Generator integrates with Visual Documentation."""
72
+ if not self.integration.doc_generator or not self.integration.visual_docs:
73
+ pytest.skip("Documentation components not available")
74
+
75
+ # Set up integration
76
+ self.integration.doc_generator._visual_docs = self.integration.visual_docs
77
+
78
+ # Test enhanced documentation generation
79
+ with tempfile.TemporaryDirectory() as temp_dir:
80
+ test_module = os.path.join(temp_dir, "test_module.py")
81
+ with open(test_module, 'w') as f:
82
+ f.write('''
83
+ """Test module for documentation."""
84
+
85
+ def test_function(param1: str, param2: int = 0) -> str:
86
+ """Test function with parameters.
87
+
88
+ Args:
89
+ param1: First parameter
90
+ param2: Second parameter
91
+
92
+ Returns:
93
+ str: Result string
94
+ """
95
+ return f"{param1}_{param2}"
96
+ ''')
97
+
98
+ try:
99
+ result = self.integration.doc_generator.generate_enhanced_documentation([test_module])
100
+
101
+ assert 'sphinx_docs' in result
102
+ assert 'visual_artifacts' in result
103
+ assert 'enhanced_files' in result
104
+
105
+ except Exception as e:
106
+ # Documentation generation might fail in test environment
107
+ pytest.skip(f"Documentation generation failed: {e}")
108
+
109
+ def test_session_manager_integration(self):
110
+ """Test that Session Manager integrates with other components."""
111
+ if not self.integration.session_manager:
112
+ pytest.skip("Session manager not available")
113
+
114
+ # Create session from example
115
+ examples = self.integration.example_repository.get_examples_by_topic("variables")
116
+ if examples:
117
+ session = self.integration.session_manager.create_session_from_example(
118
+ "test_user", examples[0]
119
+ )
120
+
121
+ assert session is not None
122
+ assert len(session.exercises) > 0
123
+ assert session.topic in examples[0].topics
124
+
125
+
126
+ class TestEndToEndScenarios:
127
+ """Test complete end-to-end learning scenarios."""
128
+
129
+ def setup_method(self):
130
+ """Set up test environment."""
131
+ reset_integration()
132
+ self.integration = FishertoolsIntegration(project_name="test_project")
133
+
134
+ def teardown_method(self):
135
+ """Clean up after tests."""
136
+ reset_integration()
137
+
138
+ def test_complete_learning_flow(self):
139
+ """Test a complete learning flow from start to finish."""
140
+ user_id = "test_learner"
141
+ topic = "variables"
142
+
143
+ # 1. Start learning session
144
+ session = self.integration.start_learning_session(topic, "beginner", user_id)
145
+ assert session is not None
146
+
147
+ # 2. Get step-by-step explanation for code
148
+ code = "name = 'Alice'"
149
+ explanation_result = self.integration.explain_code_with_examples(code, include_visuals=False)
150
+
151
+ assert 'step_explanations' in explanation_result
152
+ assert 'related_examples' in explanation_result
153
+ assert 'concepts_covered' in explanation_result
154
+ assert len(explanation_result['step_explanations']) > 0
155
+
156
+ # 3. Get learning recommendations
157
+ recommendations = self.integration.get_learning_recommendations(user_id, topic)
158
+
159
+ assert 'next_topics' in recommendations
160
+ assert 'recommended_examples' in recommendations
161
+ assert 'progress_summary' in recommendations
162
+
163
+ def test_learning_with_progress_tracking(self):
164
+ """Test learning scenario with progress tracking."""
165
+ user_id = "progress_user"
166
+
167
+ # Start multiple learning sessions
168
+ topics = ["variables", "lists", "functions"]
169
+
170
+ for topic in topics:
171
+ session = self.integration.start_learning_session(topic, "beginner", user_id)
172
+ assert session is not None
173
+
174
+ # Simulate completing the topic
175
+ if self.integration.learning_system:
176
+ self.integration.learning_system.track_progress(user_id, topic, True)
177
+
178
+ # Get final recommendations
179
+ recommendations = self.integration.get_learning_recommendations(user_id)
180
+
181
+ if recommendations.get('progress_summary'):
182
+ progress = recommendations['progress_summary']
183
+ assert 'completed_topics' in progress
184
+ assert len(progress['completed_topics']) <= len(topics)
185
+
186
+ def test_code_explanation_with_examples(self):
187
+ """Test comprehensive code explanation with examples."""
188
+ # Test with different types of code
189
+ test_codes = [
190
+ "x = 5",
191
+ "numbers = [1, 2, 3]",
192
+ "def greet(name): return f'Hello, {name}!'",
193
+ "for item in [1, 2, 3]: print(item)"
194
+ ]
195
+
196
+ for code in test_codes:
197
+ result = self.integration.explain_code_with_examples(code, include_visuals=False)
198
+
199
+ assert 'step_explanations' in result
200
+ assert 'related_examples' in result
201
+ assert 'concepts_covered' in result
202
+
203
+ # Should have at least one explanation
204
+ assert len(result['step_explanations']) > 0
205
+
206
+ # Should identify some concepts
207
+ assert len(result['concepts_covered']) > 0
208
+
209
+ def test_documentation_generation_flow(self):
210
+ """Test complete documentation generation flow."""
211
+ if not self.integration.doc_generator:
212
+ pytest.skip("Documentation generator not available")
213
+
214
+ # Create a temporary module to document
215
+ with tempfile.TemporaryDirectory() as temp_dir:
216
+ test_module = os.path.join(temp_dir, "example_module.py")
217
+ with open(test_module, 'w') as f:
218
+ f.write('''
219
+ """Example module for testing documentation generation."""
220
+
221
+ class ExampleClass:
222
+ """An example class for demonstration."""
223
+
224
+ def __init__(self, name: str):
225
+ """Initialize with a name."""
226
+ self.name = name
227
+
228
+ def greet(self) -> str:
229
+ """Return a greeting message."""
230
+ return f"Hello, {self.name}!"
231
+
232
+ def example_function(x: int, y: int = 0) -> int:
233
+ """Add two numbers together.
234
+
235
+ Args:
236
+ x: First number
237
+ y: Second number (default 0)
238
+
239
+ Returns:
240
+ int: Sum of x and y
241
+ """
242
+ return x + y
243
+ ''')
244
+
245
+ try:
246
+ # Generate comprehensive documentation
247
+ result = self.integration.generate_comprehensive_documentation([test_module])
248
+
249
+ assert 'sphinx_docs' in result
250
+ assert 'visual_artifacts' in result
251
+ assert 'publish_result' in result
252
+
253
+ # Check that documentation was generated
254
+ sphinx_docs = result['sphinx_docs']
255
+ assert sphinx_docs.source_files
256
+ assert 'index.rst' in sphinx_docs.source_files
257
+
258
+ except Exception as e:
259
+ # Documentation generation might fail in test environment
260
+ pytest.skip(f"Documentation generation failed: {e}")
261
+
262
+
263
+ class TestErrorRecoveryIntegration:
264
+ """Test error recovery and graceful degradation."""
265
+
266
+ def setup_method(self):
267
+ """Set up test environment."""
268
+ reset_integration()
269
+
270
+ def teardown_method(self):
271
+ """Clean up after tests."""
272
+ reset_integration()
273
+
274
+ def test_graceful_degradation_on_component_failure(self):
275
+ """Test that system degrades gracefully when components fail."""
276
+ # Mock a component initialization failure
277
+ with patch('fishertools.learning.TutorialEngine') as mock_tutorial:
278
+ mock_tutorial.side_effect = Exception("Tutorial engine failed")
279
+
280
+ # Should still initialize with minimal components
281
+ integration = FishertoolsIntegration(project_name="test_project")
282
+
283
+ # Should have basic learning system
284
+ assert integration.learning_system is not None
285
+ assert integration.example_repository is not None
286
+
287
+ def test_error_recovery_in_learning_session(self):
288
+ """Test error recovery during learning session creation."""
289
+ integration = FishertoolsIntegration(project_name="test_project")
290
+
291
+ # Mock session manager to fail
292
+ if integration.session_manager:
293
+ with patch.object(integration.session_manager, 'create_session') as mock_create:
294
+ mock_create.side_effect = Exception("Session creation failed")
295
+
296
+ # Should still create basic tutorial session
297
+ session = integration.start_learning_session("variables", "beginner", "test_user")
298
+ assert session is not None
299
+
300
+ def test_error_recovery_in_documentation_generation(self):
301
+ """Test error recovery during documentation generation."""
302
+ integration = FishertoolsIntegration(project_name="test_project")
303
+
304
+ if not integration.doc_generator:
305
+ pytest.skip("Documentation generator not available")
306
+
307
+ # Test with invalid module path
308
+ try:
309
+ result = integration.generate_comprehensive_documentation(["/nonexistent/module.py"])
310
+ # Should either succeed with error handling or raise appropriate exception
311
+ assert result is not None or True # Either works or fails gracefully
312
+ except Exception as e:
313
+ # Should be a fishertools error, not a raw exception
314
+ assert "fishertools" in str(type(e)).lower() or "documentation" in str(e).lower()
315
+
316
+ def test_configuration_error_recovery(self):
317
+ """Test recovery from configuration errors."""
318
+ # Test with invalid configuration path
319
+ integration = FishertoolsIntegration(
320
+ config_path="/nonexistent/config.json",
321
+ project_name="test_project"
322
+ )
323
+
324
+ # Should initialize with default configuration
325
+ assert integration.config is not None
326
+ assert integration.config_manager is not None
327
+
328
+ def test_recovery_manager_integration(self):
329
+ """Test that recovery manager is properly integrated."""
330
+ integration = FishertoolsIntegration(project_name="test_project")
331
+
332
+ assert integration.recovery_manager is not None
333
+
334
+ # Test error statistics
335
+ stats = integration.recovery_manager.get_error_statistics()
336
+ assert 'total_errors' in stats
337
+ assert 'error_counts_by_type' in stats
338
+
339
+
340
+ class TestSystemStatus:
341
+ """Test system status and health checks."""
342
+
343
+ def setup_method(self):
344
+ """Set up test environment."""
345
+ reset_integration()
346
+ self.integration = FishertoolsIntegration(project_name="test_project")
347
+
348
+ def teardown_method(self):
349
+ """Clean up after tests."""
350
+ reset_integration()
351
+
352
+ def test_system_status_reporting(self):
353
+ """Test that system status is properly reported."""
354
+ status = self.integration.get_system_status()
355
+
356
+ # Should have status for all components
357
+ expected_components = [
358
+ 'learning_system',
359
+ 'documentation_generator',
360
+ 'example_repository',
361
+ 'visual_documentation',
362
+ 'configuration_manager'
363
+ ]
364
+
365
+ for component in expected_components:
366
+ assert component in status
367
+ assert status[component] in ['initialized', 'failed']
368
+
369
+ # Should have configuration info
370
+ assert 'current_config' in status
371
+ assert 'total_examples' in status
372
+
373
+ def test_global_integration_instance(self):
374
+ """Test global integration instance management."""
375
+ # Get global instance
376
+ global_integration = get_integration(project_name="global_test")
377
+ assert global_integration is not None
378
+
379
+ # Should return same instance on subsequent calls
380
+ same_integration = get_integration()
381
+ assert same_integration is global_integration
382
+
383
+ # Reset and get new instance
384
+ reset_integration()
385
+ new_integration = get_integration(project_name="new_test")
386
+ assert new_integration is not global_integration
387
+
388
+ def test_convenience_functions(self):
389
+ """Test convenience functions work properly."""
390
+ from fishertools.integration import start_learning, explain_code, get_recommendations
391
+
392
+ # Test start_learning convenience function
393
+ session = start_learning("variables", "beginner", "convenience_user")
394
+ assert session is not None
395
+
396
+ # Test explain_code convenience function
397
+ result = explain_code("x = 42", include_visuals=False)
398
+ assert 'step_explanations' in result
399
+
400
+ # Test get_recommendations convenience function
401
+ recommendations = get_recommendations("convenience_user", "variables")
402
+ assert 'next_topics' in recommendations
403
+
404
+
405
+ class TestConfigurationIntegration:
406
+ """Test configuration system integration."""
407
+
408
+ def setup_method(self):
409
+ """Set up test environment."""
410
+ reset_integration()
411
+
412
+ def teardown_method(self):
413
+ """Clean up after tests."""
414
+ reset_integration()
415
+
416
+ def test_configuration_update_integration(self):
417
+ """Test that configuration updates are applied to all components."""
418
+ integration = FishertoolsIntegration(project_name="config_test")
419
+
420
+ # Update configuration
421
+ new_config = {
422
+ 'default_level': 'intermediate',
423
+ 'docs_output_dir': 'custom_docs'
424
+ }
425
+
426
+ try:
427
+ integration.update_configuration(new_config)
428
+
429
+ # Verify configuration was updated
430
+ assert integration.config.default_level == 'intermediate'
431
+
432
+ # Verify it was applied to components
433
+ if integration.doc_generator:
434
+ assert integration.doc_generator.output_dir == 'custom_docs'
435
+
436
+ except Exception as e:
437
+ # Configuration update might fail in test environment
438
+ pytest.skip(f"Configuration update failed: {e}")
439
+
440
+ def test_configuration_validation_integration(self):
441
+ """Test configuration validation during integration."""
442
+ # Test with invalid configuration
443
+ invalid_config = {
444
+ 'default_level': 'invalid_level',
445
+ 'explanation_verbosity': 'invalid_verbosity'
446
+ }
447
+
448
+ integration = FishertoolsIntegration(project_name="validation_test")
449
+
450
+ try:
451
+ integration.update_configuration(invalid_config)
452
+ # Should either succeed with validation or raise appropriate error
453
+ except ValueError as e:
454
+ # Expected for invalid configuration
455
+ assert "configuration" in str(e).lower() or "invalid" in str(e).lower()
456
+ except Exception as e:
457
+ # Other exceptions should be handled gracefully
458
+ assert integration.config is not None # Should still have valid config
459
+
460
+
461
+ if __name__ == "__main__":
462
+ pytest.main([__file__])
@@ -0,0 +1,3 @@
1
+ """
2
+ Tests for learning tools.
3
+ """
@@ -0,0 +1,221 @@
1
+ """
2
+ Unit tests for the examples module in fishertools.learn.
3
+
4
+ Tests the generate_example function and related utilities for
5
+ generating educational code examples.
6
+ """
7
+
8
+ import pytest
9
+ from fishertools.learn.examples import (
10
+ generate_example,
11
+ list_available_concepts,
12
+ get_concept_info,
13
+ CODE_EXAMPLES
14
+ )
15
+
16
+
17
+ class TestGenerateExample:
18
+ """Test the generate_example function."""
19
+
20
+ def test_generate_example_valid_concept(self):
21
+ """Test generating example for a valid concept."""
22
+ result = generate_example("variables")
23
+
24
+ # Should return a formatted string with content
25
+ assert isinstance(result, str)
26
+ assert len(result) > 0
27
+
28
+ # Should contain expected sections
29
+ assert "Переменные и типы данных" in result
30
+ assert "Описание:" in result
31
+ assert "Пример кода:" in result
32
+ assert "Совет:" in result
33
+
34
+ # Should contain actual code content
35
+ assert "name = " in result
36
+ assert "print(" in result
37
+
38
+ def test_generate_example_invalid_concept(self):
39
+ """Test generating example for an invalid concept."""
40
+ result = generate_example("nonexistent_concept")
41
+
42
+ # Should return error message
43
+ assert "❌ Концепция 'nonexistent_concept' не найдена" in result
44
+ assert "📚 Доступные концепции:" in result
45
+
46
+ def test_generate_example_case_insensitive(self):
47
+ """Test that concept matching is case insensitive."""
48
+ result1 = generate_example("VARIABLES")
49
+ result2 = generate_example("variables")
50
+ result3 = generate_example("Variables")
51
+
52
+ # All should produce the same result
53
+ assert "Переменные и типы данных" in result1
54
+ assert "Переменные и типы данных" in result2
55
+ assert "Переменные и типы данных" in result3
56
+
57
+ def test_generate_example_whitespace_handling(self):
58
+ """Test that whitespace is handled correctly."""
59
+ result = generate_example(" variables ")
60
+
61
+ # Should work despite extra whitespace
62
+ assert "Переменные и типы данных" in result
63
+
64
+ def test_all_concepts_generate_valid_examples(self):
65
+ """Test that all available concepts generate valid examples."""
66
+ concepts = list_available_concepts()
67
+
68
+ for concept in concepts:
69
+ result = generate_example(concept)
70
+
71
+ # Each should return a valid formatted example
72
+ assert isinstance(result, str)
73
+ assert len(result) > 100 # Should be substantial content
74
+ assert "Описание:" in result
75
+ assert "Пример кода:" in result
76
+ assert "Совет:" in result
77
+
78
+ # Should not contain error messages (but may contain ❌ in educational content)
79
+ assert "не найдена" not in result
80
+ assert "Доступные концепции:" not in result
81
+
82
+
83
+ class TestListAvailableConcepts:
84
+ """Test the list_available_concepts function."""
85
+
86
+ def test_returns_list(self):
87
+ """Test that function returns a list."""
88
+ result = list_available_concepts()
89
+ assert isinstance(result, list)
90
+
91
+ def test_contains_expected_concepts(self):
92
+ """Test that list contains expected concepts."""
93
+ concepts = list_available_concepts()
94
+
95
+ # Should contain core Python concepts
96
+ expected_concepts = [
97
+ "variables", "lists", "dictionaries",
98
+ "functions", "loops", "conditionals", "file_operations"
99
+ ]
100
+
101
+ for concept in expected_concepts:
102
+ assert concept in concepts
103
+
104
+ def test_all_concepts_have_data(self):
105
+ """Test that all listed concepts have corresponding data."""
106
+ concepts = list_available_concepts()
107
+
108
+ for concept in concepts:
109
+ assert concept in CODE_EXAMPLES
110
+ assert "title" in CODE_EXAMPLES[concept]
111
+ assert "description" in CODE_EXAMPLES[concept]
112
+ assert "code" in CODE_EXAMPLES[concept]
113
+
114
+
115
+ class TestGetConceptInfo:
116
+ """Test the get_concept_info function."""
117
+
118
+ def test_valid_concept_returns_info(self):
119
+ """Test getting info for a valid concept."""
120
+ info = get_concept_info("variables")
121
+
122
+ assert info is not None
123
+ assert isinstance(info, dict)
124
+ assert "title" in info
125
+ assert "description" in info
126
+
127
+ # Should contain expected content
128
+ assert "Переменные и типы данных" in info["title"]
129
+ assert "Основы работы с переменными" in info["description"]
130
+
131
+ def test_invalid_concept_returns_none(self):
132
+ """Test getting info for an invalid concept."""
133
+ info = get_concept_info("nonexistent")
134
+ assert info is None
135
+
136
+ def test_case_insensitive_lookup(self):
137
+ """Test that concept lookup is case insensitive."""
138
+ info1 = get_concept_info("VARIABLES")
139
+ info2 = get_concept_info("variables")
140
+
141
+ assert info1 is not None
142
+ assert info2 is not None
143
+ assert info1["title"] == info2["title"]
144
+
145
+ def test_whitespace_handling(self):
146
+ """Test that whitespace is handled in concept lookup."""
147
+ info = get_concept_info(" variables ")
148
+
149
+ assert info is not None
150
+ assert "Переменные и типы данных" in info["title"]
151
+
152
+
153
+ class TestCodeExamplesData:
154
+ """Test the CODE_EXAMPLES data structure."""
155
+
156
+ def test_all_examples_have_required_fields(self):
157
+ """Test that all code examples have required fields."""
158
+ for concept, data in CODE_EXAMPLES.items():
159
+ assert "title" in data
160
+ assert "description" in data
161
+ assert "code" in data
162
+
163
+ # Fields should not be empty
164
+ assert len(data["title"]) > 0
165
+ assert len(data["description"]) > 0
166
+ assert len(data["code"]) > 0
167
+
168
+ def test_code_examples_contain_actual_code(self):
169
+ """Test that code examples contain actual Python code."""
170
+ for concept, data in CODE_EXAMPLES.items():
171
+ code = data["code"]
172
+
173
+ # Should contain Python-like syntax
174
+ assert any(keyword in code for keyword in ["print(", "def ", "=", "if ", "for "])
175
+
176
+ def test_examples_are_educational(self):
177
+ """Test that examples contain educational content."""
178
+ for concept, data in CODE_EXAMPLES.items():
179
+ code = data["code"]
180
+
181
+ # Should contain comments (educational explanations)
182
+ assert "#" in code
183
+
184
+ # Should contain Russian explanations
185
+ assert any(char in code for char in "абвгдеёжзийклмнопрстуфхцчшщъыьэюя")
186
+
187
+
188
+ class TestIntegration:
189
+ """Integration tests for the examples module."""
190
+
191
+ def test_complete_workflow(self):
192
+ """Test complete workflow of discovering and generating examples."""
193
+ # Get available concepts
194
+ concepts = list_available_concepts()
195
+ assert len(concepts) > 0
196
+
197
+ # Get info for first concept
198
+ first_concept = concepts[0]
199
+ info = get_concept_info(first_concept)
200
+ assert info is not None
201
+
202
+ # Generate example for the concept
203
+ example = generate_example(first_concept)
204
+ assert "❌" not in example # Should not be an error
205
+ assert info["title"] in example # Should contain the title
206
+
207
+ def test_error_handling_consistency(self):
208
+ """Test that error handling is consistent across functions."""
209
+ invalid_concept = "definitely_not_a_concept"
210
+
211
+ # generate_example should return error message
212
+ example_result = generate_example(invalid_concept)
213
+ assert "❌" in example_result
214
+
215
+ # get_concept_info should return None
216
+ info_result = get_concept_info(invalid_concept)
217
+ assert info_result is None
218
+
219
+ # list_available_concepts should not include invalid concept
220
+ concepts = list_available_concepts()
221
+ assert invalid_concept not in concepts