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,194 @@
1
+ """
2
+ Property-based tests for Learning System completeness.
3
+
4
+ Feature: fishertools-enhancements
5
+ Property 1: Learning System Completeness
6
+ Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5
7
+ """
8
+
9
+ import pytest
10
+ from hypothesis import given, strategies as st, assume
11
+ from fishertools.learning import LearningSystem
12
+ from fishertools.learning.models import DifficultyLevel, CodeContext
13
+
14
+
15
+ class TestLearningSystemCompleteness:
16
+ """Property tests for Learning System completeness."""
17
+
18
+ def setup_method(self):
19
+ """Set up test fixtures."""
20
+ self.learning_system = LearningSystem()
21
+
22
+ @given(
23
+ topic=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
24
+ level=st.sampled_from(["beginner", "intermediate", "advanced"])
25
+ )
26
+ def test_step_by_step_explanation_completeness(self, topic, level):
27
+ """
28
+ Property 1: For any fishertools function used by a beginner,
29
+ the Learning_System should provide step-by-step explanations
30
+ that include input/output examples, related topics, and
31
+ level-appropriate content adaptation.
32
+
33
+ **Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5**
34
+ """
35
+ # Generate simple Python code for testing
36
+ test_codes = [
37
+ "x = 5",
38
+ "my_list = [1, 2, 3]",
39
+ "def greet(name): return f'Hello {name}'",
40
+ "if x > 0: print('positive')",
41
+ "for i in range(3): print(i)"
42
+ ]
43
+
44
+ for code in test_codes:
45
+ # Test that explanations are provided
46
+ explanations = self.learning_system.get_step_by_step_explanation(code)
47
+
48
+ # Property: Should always provide explanations for valid code
49
+ assert len(explanations) > 0, f"No explanations provided for code: {code}"
50
+
51
+ # Property: Each explanation should have required components
52
+ for explanation in explanations:
53
+ assert explanation.step_number > 0, "Step number should be positive"
54
+ assert explanation.description, "Description should not be empty"
55
+ assert explanation.code_snippet, "Code snippet should not be empty"
56
+ assert isinstance(explanation.related_concepts, list), "Related concepts should be a list"
57
+
58
+ @given(
59
+ topic=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
60
+ level=st.sampled_from(["beginner", "intermediate", "advanced"])
61
+ )
62
+ def test_tutorial_session_creation_completeness(self, topic, level):
63
+ """
64
+ Property 1: For any topic request, the Learning_System should
65
+ create a tutorial session with appropriate exercises and content.
66
+
67
+ **Validates: Requirements 1.1, 1.4, 1.5**
68
+ """
69
+ assume(len(topic.strip()) > 0)
70
+
71
+ try:
72
+ # Test tutorial session creation
73
+ session = self.learning_system.start_tutorial(topic, level)
74
+
75
+ # Property: Session should be created successfully
76
+ assert session is not None, "Tutorial session should be created"
77
+ assert session.topic == topic, "Session topic should match requested topic"
78
+ assert session.level.value == level, "Session level should match requested level"
79
+ assert session.session_id, "Session should have a unique ID"
80
+ assert isinstance(session.exercises, list), "Session should have exercises list"
81
+
82
+ except ValueError as e:
83
+ # Invalid inputs should raise ValueError, which is acceptable
84
+ assert "Invalid" in str(e) or "must be" in str(e)
85
+
86
+ @given(
87
+ current_topic=st.text(min_size=1, max_size=50).filter(lambda x: x.strip())
88
+ )
89
+ def test_related_topics_suggestion_completeness(self, current_topic):
90
+ """
91
+ Property 1: For any current topic, the Learning_System should
92
+ suggest related topics for further learning.
93
+
94
+ **Validates: Requirements 1.4, 1.5**
95
+ """
96
+ assume(len(current_topic.strip()) > 0)
97
+
98
+ # Test related topics suggestion
99
+ related_topics = self.learning_system.suggest_related_topics(current_topic)
100
+
101
+ # Property: Should return a list (may be empty for unknown topics)
102
+ assert isinstance(related_topics, list), "Related topics should be a list"
103
+
104
+ # Property: All suggested topics should be strings
105
+ for topic in related_topics:
106
+ assert isinstance(topic, str), "Each related topic should be a string"
107
+ assert len(topic.strip()) > 0, "Related topics should not be empty strings"
108
+
109
+ # Property: Should not suggest more than reasonable number of topics
110
+ assert len(related_topics) <= 10, "Should not suggest too many topics"
111
+
112
+ @given(
113
+ content=st.text(min_size=1, max_size=200),
114
+ level=st.sampled_from(["beginner", "intermediate", "advanced"])
115
+ )
116
+ def test_content_adaptation_completeness(self, content, level):
117
+ """
118
+ Property 1: For any content and level, the Learning_System should
119
+ adapt content appropriately for the specified difficulty level.
120
+
121
+ **Validates: Requirements 1.5**
122
+ """
123
+ assume(len(content.strip()) > 0)
124
+
125
+ # Test content adaptation
126
+ adapted_content = self.learning_system.adapt_content_for_level(content, level)
127
+
128
+ # Property: Should return adapted content
129
+ assert isinstance(adapted_content, str), "Adapted content should be a string"
130
+ assert len(adapted_content) > 0, "Adapted content should not be empty"
131
+
132
+ # Property: Beginner content should be more detailed
133
+ if level == "beginner":
134
+ # Beginner content often has additional explanatory text
135
+ assert len(adapted_content) >= len(content) * 0.8, "Beginner content should maintain or increase length"
136
+
137
+ @given(
138
+ code=st.text(min_size=1, max_size=100).filter(lambda x: x.strip())
139
+ )
140
+ def test_explanation_with_context_completeness(self, code):
141
+ """
142
+ Property 1: For any code with context, the Learning_System should
143
+ provide enhanced explanations using the context information.
144
+
145
+ **Validates: Requirements 1.2, 1.3**
146
+ """
147
+ assume(len(code.strip()) > 0)
148
+
149
+ # Create context
150
+ context = CodeContext(
151
+ function_name="test_function",
152
+ variables={"x": 5, "name": "test"}
153
+ )
154
+
155
+ try:
156
+ # Test explanation with context
157
+ explanations = self.learning_system.get_step_by_step_explanation(code, context)
158
+
159
+ # Property: Should provide explanations even with context
160
+ assert isinstance(explanations, list), "Explanations should be a list"
161
+
162
+ # Property: Each explanation should be well-formed
163
+ for explanation in explanations:
164
+ assert hasattr(explanation, 'step_number'), "Should have step number"
165
+ assert hasattr(explanation, 'description'), "Should have description"
166
+ assert hasattr(explanation, 'code_snippet'), "Should have code snippet"
167
+
168
+ except (SyntaxError, ValueError):
169
+ # Invalid code should be handled gracefully
170
+ pass
171
+
172
+ def test_learning_system_initialization_completeness(self):
173
+ """
174
+ Property 1: Learning System should initialize properly and
175
+ provide all required functionality.
176
+
177
+ **Validates: Requirements 1.1**
178
+ """
179
+ # Test initialization
180
+ system = LearningSystem()
181
+
182
+ # Property: Should have all required methods
183
+ required_methods = [
184
+ 'start_tutorial',
185
+ 'get_step_by_step_explanation',
186
+ 'suggest_related_topics',
187
+ 'adapt_content_for_level',
188
+ 'track_progress',
189
+ 'get_user_progress'
190
+ ]
191
+
192
+ for method_name in required_methods:
193
+ assert hasattr(system, method_name), f"Should have {method_name} method"
194
+ assert callable(getattr(system, method_name)), f"{method_name} should be callable"
@@ -0,0 +1,279 @@
1
+ """
2
+ Property-based tests for Progress Tracking correctness.
3
+
4
+ Feature: fishertools-enhancements
5
+ Property 6: Progress Tracking Correctness
6
+ Validates: Requirements 6.1, 6.2, 6.3, 6.4, 6.5
7
+ """
8
+
9
+ import pytest
10
+ import tempfile
11
+ import os
12
+ from hypothesis import given, strategies as st, assume
13
+ from fishertools.learning.progress import ProgressSystem
14
+ from fishertools.learning.models import DifficultyLevel
15
+
16
+
17
+ class TestProgressTrackingCorrectness:
18
+ """Property tests for Progress Tracking correctness."""
19
+
20
+ def setup_method(self):
21
+ """Set up test fixtures with temporary storage."""
22
+ # Use temporary file for testing to avoid conflicts
23
+ self.temp_dir = tempfile.mkdtemp()
24
+ self.storage_path = os.path.join(self.temp_dir, "test_progress.json")
25
+ self.progress_system = ProgressSystem(storage_path=self.storage_path)
26
+
27
+ def teardown_method(self):
28
+ """Clean up test fixtures."""
29
+ # Clean up temporary files
30
+ if os.path.exists(self.storage_path):
31
+ os.remove(self.storage_path)
32
+ os.rmdir(self.temp_dir)
33
+
34
+ @given(
35
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
36
+ level=st.sampled_from([DifficultyLevel.BEGINNER, DifficultyLevel.INTERMEDIATE, DifficultyLevel.ADVANCED])
37
+ )
38
+ def test_user_profile_creation_correctness(self, user_id, level):
39
+ """
40
+ Property 6: For any user learning journey, the Learning_System should
41
+ create progress profiles correctly.
42
+
43
+ **Validates: Requirements 6.1**
44
+ """
45
+ assume(len(user_id.strip()) > 0)
46
+
47
+ # Test profile creation
48
+ progress = self.progress_system.create_user_profile(user_id.strip(), level)
49
+
50
+ # Property: Profile should be created with correct initial values
51
+ assert progress is not None, "Progress profile should be created"
52
+ assert progress.user_id == user_id.strip(), "User ID should match"
53
+ assert progress.current_level == level, "Level should match"
54
+ assert progress.completed_topics == [], "Should start with no completed topics"
55
+ assert progress.total_exercises_completed == 0, "Should start with 0 exercises"
56
+ assert progress.achievements == [], "Should start with no achievements"
57
+ assert progress.session_count == 0, "Should start with 0 sessions"
58
+ assert progress.total_time_spent == 0, "Should start with 0 time spent"
59
+
60
+ # Property: Profile should be retrievable
61
+ retrieved_progress = self.progress_system.get_progress(user_id.strip())
62
+ assert retrieved_progress is not None, "Should be able to retrieve created profile"
63
+ assert retrieved_progress.user_id == progress.user_id, "Retrieved profile should match"
64
+
65
+ @given(
66
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
67
+ topics=st.lists(st.text(min_size=1, max_size=30).filter(lambda x: x.strip()), min_size=1, max_size=10)
68
+ )
69
+ def test_topic_completion_tracking_correctness(self, user_id, topics):
70
+ """
71
+ Property 6: For any user learning journey, the Learning_System should
72
+ track completed topics correctly.
73
+
74
+ **Validates: Requirements 6.2**
75
+ """
76
+ assume(len(user_id.strip()) > 0)
77
+ assume(all(len(topic.strip()) > 0 for topic in topics))
78
+
79
+ user_id = user_id.strip()
80
+ topics = [topic.strip() for topic in topics]
81
+
82
+ # Create user profile
83
+ self.progress_system.create_user_profile(user_id)
84
+
85
+ # Track topic completions
86
+ unique_topics_completed = set()
87
+ for i, topic in enumerate(topics):
88
+ self.progress_system.update_progress(user_id, topic, True)
89
+
90
+ # Property: Progress should be updated correctly
91
+ progress = self.progress_system.get_progress(user_id)
92
+ assert progress is not None, "Progress should exist"
93
+ assert topic in progress.completed_topics, f"Topic {topic} should be marked as completed"
94
+
95
+ # Only count unique topics for exercise count
96
+ if topic not in unique_topics_completed:
97
+ unique_topics_completed.add(topic)
98
+
99
+ expected_count = len(unique_topics_completed)
100
+ assert progress.total_exercises_completed == expected_count, f"Exercise count should be {expected_count} for unique topics"
101
+
102
+ # Property: All unique topics should be tracked
103
+ final_progress = self.progress_system.get_progress(user_id)
104
+ assert len(final_progress.completed_topics) == len(set(topics)), "All unique topics should be tracked"
105
+
106
+ # Property: Duplicate completions should not increase count
107
+ duplicate_topic = topics[0]
108
+ initial_count = final_progress.total_exercises_completed
109
+ self.progress_system.update_progress(user_id, duplicate_topic, True)
110
+
111
+ updated_progress = self.progress_system.get_progress(user_id)
112
+ assert updated_progress.total_exercises_completed == initial_count, "Duplicate completions should not increase count"
113
+
114
+ @given(
115
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip())
116
+ )
117
+ def test_progress_persistence_correctness(self, user_id):
118
+ """
119
+ Property 6: For any user learning journey, the Learning_System should
120
+ persist progress between sessions correctly.
121
+
122
+ **Validates: Requirements 6.3**
123
+ """
124
+ assume(len(user_id.strip()) > 0)
125
+
126
+ user_id = user_id.strip()
127
+
128
+ # Create and update progress
129
+ self.progress_system.create_user_profile(user_id)
130
+ self.progress_system.update_progress(user_id, "test_topic", True)
131
+ self.progress_system.add_achievement(user_id, "test_achievement")
132
+
133
+ original_progress = self.progress_system.get_progress(user_id)
134
+
135
+ # Create new progress system instance (simulating new session)
136
+ new_progress_system = ProgressSystem(storage_path=self.storage_path)
137
+
138
+ # Property: Progress should be loaded from storage
139
+ loaded_progress = new_progress_system.get_progress(user_id)
140
+ assert loaded_progress is not None, "Progress should be loaded from storage"
141
+ assert loaded_progress.user_id == original_progress.user_id, "User ID should match"
142
+ assert loaded_progress.completed_topics == original_progress.completed_topics, "Completed topics should match"
143
+ assert loaded_progress.achievements == original_progress.achievements, "Achievements should match"
144
+ assert loaded_progress.total_exercises_completed == original_progress.total_exercises_completed, "Exercise count should match"
145
+
146
+ @given(
147
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip())
148
+ )
149
+ def test_next_topic_suggestions_correctness(self, user_id):
150
+ """
151
+ Property 6: For any user learning journey, the Learning_System should
152
+ suggest appropriate next steps based on completion status.
153
+
154
+ **Validates: Requirements 6.4**
155
+ """
156
+ assume(len(user_id.strip()) > 0)
157
+
158
+ user_id = user_id.strip()
159
+
160
+ # Test suggestions for new user
161
+ suggestions = self.progress_system.suggest_next_topics(user_id)
162
+
163
+ # Property: Should provide suggestions for new users
164
+ assert isinstance(suggestions, list), "Suggestions should be a list"
165
+ assert len(suggestions) > 0, "Should provide suggestions for new users"
166
+
167
+ # Property: All suggestions should be valid strings
168
+ for suggestion in suggestions:
169
+ assert isinstance(suggestion, str), "Each suggestion should be a string"
170
+ assert len(suggestion.strip()) > 0, "Suggestions should not be empty"
171
+
172
+ # Create user and complete some topics
173
+ self.progress_system.create_user_profile(user_id)
174
+
175
+ # Complete beginner topics
176
+ beginner_topics = ["variables", "data_types", "input_output"]
177
+ for topic in beginner_topics:
178
+ self.progress_system.update_progress(user_id, topic, True)
179
+
180
+ # Property: Suggestions should exclude completed topics
181
+ new_suggestions = self.progress_system.suggest_next_topics(user_id)
182
+ for completed_topic in beginner_topics:
183
+ assert completed_topic not in new_suggestions, f"Completed topic {completed_topic} should not be suggested"
184
+
185
+ @given(
186
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
187
+ achievements=st.lists(st.text(min_size=1, max_size=30).filter(lambda x: x.strip()), min_size=1, max_size=5)
188
+ )
189
+ def test_achievement_tracking_correctness(self, user_id, achievements):
190
+ """
191
+ Property 6: For any user learning journey, the Learning_System should
192
+ track achievements correctly.
193
+
194
+ **Validates: Requirements 6.5**
195
+ """
196
+ assume(len(user_id.strip()) > 0)
197
+ assume(all(len(achievement.strip()) > 0 for achievement in achievements))
198
+
199
+ user_id = user_id.strip()
200
+ achievements = [achievement.strip() for achievement in achievements]
201
+
202
+ # Create user profile
203
+ self.progress_system.create_user_profile(user_id)
204
+
205
+ # Add achievements
206
+ for achievement in achievements:
207
+ self.progress_system.add_achievement(user_id, achievement)
208
+
209
+ # Property: Achievement should be added
210
+ progress = self.progress_system.get_progress(user_id)
211
+ assert achievement in progress.achievements, f"Achievement {achievement} should be tracked"
212
+
213
+ # Property: All achievements should be tracked
214
+ final_progress = self.progress_system.get_progress(user_id)
215
+ assert len(final_progress.achievements) == len(set(achievements)), "All unique achievements should be tracked"
216
+
217
+ # Property: Duplicate achievements should not be added
218
+ duplicate_achievement = achievements[0]
219
+ initial_count = len(final_progress.achievements)
220
+ self.progress_system.add_achievement(user_id, duplicate_achievement)
221
+
222
+ updated_progress = self.progress_system.get_progress(user_id)
223
+ assert len(updated_progress.achievements) == initial_count, "Duplicate achievements should not be added"
224
+
225
+ @given(
226
+ user_id=st.text(min_size=1, max_size=50).filter(lambda x: x.strip())
227
+ )
228
+ def test_level_progression_correctness(self, user_id):
229
+ """
230
+ Property 6: For any user learning journey, the Learning_System should
231
+ handle level progression correctly.
232
+
233
+ **Validates: Requirements 6.4**
234
+ """
235
+ assume(len(user_id.strip()) > 0)
236
+
237
+ user_id = user_id.strip()
238
+
239
+ # Create beginner user
240
+ self.progress_system.create_user_profile(user_id, DifficultyLevel.BEGINNER)
241
+
242
+ # Complete enough beginner topics to trigger level progression
243
+ beginner_topics = ["variables", "data_types", "input_output", "operators", "conditionals", "loops", "lists", "functions"]
244
+
245
+ for topic in beginner_topics:
246
+ self.progress_system.update_progress(user_id, topic, True)
247
+
248
+ # Property: User should progress to intermediate level
249
+ progress = self.progress_system.get_progress(user_id)
250
+ # Note: Level progression happens when 80% of topics are completed
251
+ # This might or might not trigger depending on the internal logic
252
+
253
+ # Property: Progress should be consistent
254
+ assert progress.current_level in [DifficultyLevel.BEGINNER, DifficultyLevel.INTERMEDIATE], "Level should be valid"
255
+ assert len(progress.completed_topics) == len(beginner_topics), "All topics should be tracked"
256
+
257
+ def test_error_handling_correctness(self):
258
+ """
259
+ Property 6: Progress tracking should handle errors gracefully.
260
+
261
+ **Validates: Requirements 6.1, 6.2, 6.3**
262
+ """
263
+ # Property: Invalid user IDs should be handled gracefully
264
+ assert self.progress_system.get_progress("") is None, "Empty user ID should return None"
265
+ assert self.progress_system.get_progress(None) is None, "None user ID should return None"
266
+
267
+ # Property: Invalid topic updates should raise appropriate errors
268
+ with pytest.raises(ValueError):
269
+ self.progress_system.update_progress("", "topic", True)
270
+
271
+ with pytest.raises(ValueError):
272
+ self.progress_system.update_progress("user", "", True)
273
+
274
+ # Property: System should handle missing storage gracefully
275
+ non_existent_path = "/non/existent/path/progress.json"
276
+ system_with_bad_path = ProgressSystem(storage_path=non_existent_path)
277
+
278
+ # Should not crash, just return None
279
+ assert system_with_bad_path.load_progress("test_user") is None, "Should handle missing storage gracefully"
@@ -0,0 +1,3 @@
1
+ """
2
+ Tests for legacy support and backward compatibility.
3
+ """