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.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for Example Repository quality.
|
|
3
|
+
|
|
4
|
+
Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
5
|
+
Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from hypothesis import given, strategies as st, assume
|
|
10
|
+
from fishertools.examples import ExampleRepository
|
|
11
|
+
from fishertools.examples.models import (
|
|
12
|
+
CodeExample, ExampleCategory, ProjectType, Scenario
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestExampleRepositoryQuality:
|
|
17
|
+
"""Property tests for Example Repository quality and completeness."""
|
|
18
|
+
|
|
19
|
+
def test_repository_initialization(self):
|
|
20
|
+
"""Test that repository initializes with default examples."""
|
|
21
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
22
|
+
repo = ExampleRepository()
|
|
23
|
+
|
|
24
|
+
# Should have examples for beginners
|
|
25
|
+
list_examples = repo.get_examples_by_topic("lists")
|
|
26
|
+
dict_examples = repo.get_examples_by_topic("dictionaries")
|
|
27
|
+
input_examples = repo.get_examples_by_category(ExampleCategory.USER_INPUT)
|
|
28
|
+
|
|
29
|
+
assert len(list_examples) > 0, "Should have list examples for beginners"
|
|
30
|
+
assert len(dict_examples) > 0, "Should have dictionary examples for beginners"
|
|
31
|
+
assert len(input_examples) > 0, "Should have user input examples for beginners"
|
|
32
|
+
|
|
33
|
+
@given(st.sampled_from(list(ExampleCategory)))
|
|
34
|
+
def test_category_examples_have_required_properties(self, category):
|
|
35
|
+
"""Test that all examples in a category have required properties."""
|
|
36
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
37
|
+
repo = ExampleRepository()
|
|
38
|
+
examples = repo.get_examples_by_category(category)
|
|
39
|
+
|
|
40
|
+
for example in examples:
|
|
41
|
+
# All examples should have basic required properties
|
|
42
|
+
assert example.id, "Example should have an ID"
|
|
43
|
+
assert example.title, "Example should have a title"
|
|
44
|
+
assert example.description, "Example should have a description"
|
|
45
|
+
assert example.code, "Example should have code"
|
|
46
|
+
assert example.explanation, "Example should have an explanation"
|
|
47
|
+
assert example.difficulty in ["beginner", "intermediate", "advanced"], \
|
|
48
|
+
"Example should have valid difficulty level"
|
|
49
|
+
assert example.topics, "Example should have topics"
|
|
50
|
+
assert example.category == category, "Example should match requested category"
|
|
51
|
+
|
|
52
|
+
@given(st.text(min_size=1, max_size=20).filter(lambda x: x.strip()))
|
|
53
|
+
def test_search_functionality_completeness(self, query):
|
|
54
|
+
"""Test that search finds relevant examples."""
|
|
55
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
56
|
+
repo = ExampleRepository()
|
|
57
|
+
|
|
58
|
+
# Add a test example that should match the query
|
|
59
|
+
test_example = CodeExample(
|
|
60
|
+
id=f"test_{query.lower().replace(' ', '_')}",
|
|
61
|
+
title=f"Example about {query}",
|
|
62
|
+
description=f"This example demonstrates {query}",
|
|
63
|
+
code=f"# Code for {query}\nprint('{query}')",
|
|
64
|
+
explanation=f"This explains {query}",
|
|
65
|
+
difficulty="beginner",
|
|
66
|
+
topics=[query.lower()],
|
|
67
|
+
prerequisites=[],
|
|
68
|
+
category=ExampleCategory.BASIC_SYNTAX
|
|
69
|
+
)
|
|
70
|
+
repo.add_example(test_example)
|
|
71
|
+
|
|
72
|
+
# Search should find the example
|
|
73
|
+
results = repo.search_examples(query)
|
|
74
|
+
found_ids = [ex.id for ex in results]
|
|
75
|
+
|
|
76
|
+
assert test_example.id in found_ids, \
|
|
77
|
+
f"Search for '{query}' should find the matching example"
|
|
78
|
+
|
|
79
|
+
def test_beginner_scenarios_completeness(self):
|
|
80
|
+
"""Test that beginner scenarios provide comprehensive learning paths."""
|
|
81
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
82
|
+
repo = ExampleRepository()
|
|
83
|
+
scenarios = repo.get_beginner_scenarios()
|
|
84
|
+
|
|
85
|
+
assert len(scenarios) > 0, "Should have beginner scenarios"
|
|
86
|
+
|
|
87
|
+
for scenario in scenarios:
|
|
88
|
+
assert scenario.difficulty == "beginner", "All scenarios should be beginner level"
|
|
89
|
+
assert scenario.examples, "Scenario should contain examples"
|
|
90
|
+
assert scenario.learning_objectives, "Scenario should have learning objectives"
|
|
91
|
+
assert scenario.estimated_time > 0, "Scenario should have estimated time"
|
|
92
|
+
|
|
93
|
+
# All examples in scenario should be beginner-friendly
|
|
94
|
+
for example in scenario.examples:
|
|
95
|
+
assert example.difficulty == "beginner", \
|
|
96
|
+
"All examples in beginner scenario should be beginner level"
|
|
97
|
+
|
|
98
|
+
@given(st.sampled_from(list(ProjectType)))
|
|
99
|
+
def test_project_templates_have_step_by_step_instructions(self, project_type):
|
|
100
|
+
"""Test that project templates provide step-by-step instructions."""
|
|
101
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
102
|
+
repo = ExampleRepository()
|
|
103
|
+
project = repo.create_simple_project(project_type)
|
|
104
|
+
|
|
105
|
+
assert project.project_type == project_type, "Project should match requested type"
|
|
106
|
+
assert project.difficulty == "beginner", "Simple projects should be beginner level"
|
|
107
|
+
assert project.steps, "Project should have steps"
|
|
108
|
+
assert project.final_code, "Project should have final code"
|
|
109
|
+
assert project.estimated_time > 0, "Project should have estimated time"
|
|
110
|
+
|
|
111
|
+
# Each step should have proper structure
|
|
112
|
+
for i, step in enumerate(project.steps):
|
|
113
|
+
assert step.step_number == i + 1, "Steps should be numbered sequentially"
|
|
114
|
+
assert step.title, "Step should have a title"
|
|
115
|
+
assert step.description, "Step should have a description"
|
|
116
|
+
|
|
117
|
+
def test_line_by_line_explanations_completeness(self):
|
|
118
|
+
"""Test that line-by-line explanations cover all code lines."""
|
|
119
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
120
|
+
repo = ExampleRepository()
|
|
121
|
+
|
|
122
|
+
# Get a sample example
|
|
123
|
+
examples = repo.get_examples_by_category(ExampleCategory.COLLECTIONS)
|
|
124
|
+
assume(len(examples) > 0)
|
|
125
|
+
|
|
126
|
+
example = examples[0]
|
|
127
|
+
explanation = repo.explain_example_line_by_line(example)
|
|
128
|
+
|
|
129
|
+
# Count non-empty lines in original code
|
|
130
|
+
code_lines = [line for line in example.code.strip().split('\n')]
|
|
131
|
+
explanation_lines = explanation.lines
|
|
132
|
+
|
|
133
|
+
assert len(explanation_lines) == len(code_lines), \
|
|
134
|
+
"Should have explanation for every line of code"
|
|
135
|
+
|
|
136
|
+
assert explanation.example_id == example.id, \
|
|
137
|
+
"Explanation should reference correct example"
|
|
138
|
+
assert explanation.summary, "Explanation should have a summary"
|
|
139
|
+
assert explanation.key_concepts, "Explanation should identify key concepts"
|
|
140
|
+
|
|
141
|
+
# Each line explanation should have required properties
|
|
142
|
+
for line_exp in explanation_lines:
|
|
143
|
+
assert line_exp.line_number > 0, "Line number should be positive"
|
|
144
|
+
assert line_exp.explanation, "Each line should have an explanation"
|
|
145
|
+
|
|
146
|
+
def test_safe_user_input_examples_security(self):
|
|
147
|
+
"""Test that user input examples demonstrate safe practices."""
|
|
148
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
149
|
+
repo = ExampleRepository()
|
|
150
|
+
input_examples = repo.get_examples_by_category(ExampleCategory.USER_INPUT)
|
|
151
|
+
|
|
152
|
+
assert len(input_examples) > 0, "Should have user input examples"
|
|
153
|
+
|
|
154
|
+
for example in input_examples:
|
|
155
|
+
code = example.code.lower()
|
|
156
|
+
|
|
157
|
+
# Should demonstrate input validation
|
|
158
|
+
assert 'try:' in code or 'except' in code or 'if' in code, \
|
|
159
|
+
"User input examples should show validation or error handling"
|
|
160
|
+
|
|
161
|
+
# Should use safe input practices
|
|
162
|
+
if 'input(' in code:
|
|
163
|
+
assert '.strip()' in code or 'strip(' in code, \
|
|
164
|
+
"Should demonstrate input cleaning with strip()"
|
|
165
|
+
|
|
166
|
+
def test_collection_examples_cover_basic_operations(self):
|
|
167
|
+
"""Test that collection examples cover fundamental operations."""
|
|
168
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
169
|
+
repo = ExampleRepository()
|
|
170
|
+
|
|
171
|
+
# Test list examples
|
|
172
|
+
list_examples = repo.get_examples_by_topic("lists")
|
|
173
|
+
assert len(list_examples) > 0, "Should have list examples"
|
|
174
|
+
|
|
175
|
+
list_code = " ".join([ex.code.lower() for ex in list_examples])
|
|
176
|
+
assert 'append(' in list_code, "List examples should show append operation"
|
|
177
|
+
assert '[' in list_code and ']' in list_code, "List examples should show list creation"
|
|
178
|
+
|
|
179
|
+
# Test dictionary examples
|
|
180
|
+
dict_examples = repo.get_examples_by_topic("dictionaries")
|
|
181
|
+
assert len(dict_examples) > 0, "Should have dictionary examples"
|
|
182
|
+
|
|
183
|
+
dict_code = " ".join([ex.code.lower() for ex in dict_examples])
|
|
184
|
+
assert '{' in dict_code and '}' in dict_code, "Dict examples should show dict creation"
|
|
185
|
+
assert '.get(' in dict_code or 'get(' in dict_code, \
|
|
186
|
+
"Dict examples should show safe access with get()"
|
|
187
|
+
|
|
188
|
+
@given(st.text(min_size=1, max_size=50).filter(lambda x: x.strip()))
|
|
189
|
+
def test_concept_breakdown_provides_simple_steps(self, concept):
|
|
190
|
+
"""Test that complex concepts are broken down into simple steps."""
|
|
191
|
+
# Feature: fishertools-enhancements, Property 3: Example Repository Quality
|
|
192
|
+
repo = ExampleRepository()
|
|
193
|
+
|
|
194
|
+
steps = repo.break_down_complex_concept(concept)
|
|
195
|
+
|
|
196
|
+
assert len(steps) > 0, "Should provide breakdown steps for any concept"
|
|
197
|
+
assert all(isinstance(step, str) for step in steps), \
|
|
198
|
+
"All breakdown steps should be strings"
|
|
199
|
+
assert all(len(step.strip()) > 0 for step in steps), \
|
|
200
|
+
"All breakdown steps should have content"
|
|
201
|
+
|
|
202
|
+
# Steps should mention the concept
|
|
203
|
+
concept_mentioned = any(concept.lower() in step.lower() for step in steps)
|
|
204
|
+
assert concept_mentioned, f"Breakdown should mention the concept '{concept}'"
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for specific examples in the Example Repository.
|
|
3
|
+
|
|
4
|
+
Tests security of user input examples, correctness of collection examples,
|
|
5
|
+
and completeness of step-by-step instructions.
|
|
6
|
+
Requirements: 3.2, 3.3
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from fishertools.examples import ExampleRepository
|
|
11
|
+
from fishertools.examples.models import ExampleCategory, ProjectType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestSpecificExamples:
|
|
15
|
+
"""Unit tests for specific example scenarios."""
|
|
16
|
+
|
|
17
|
+
def setup_method(self):
|
|
18
|
+
"""Set up test fixtures."""
|
|
19
|
+
self.repo = ExampleRepository()
|
|
20
|
+
|
|
21
|
+
def test_user_input_examples_security(self):
|
|
22
|
+
"""Test that user input examples demonstrate secure practices."""
|
|
23
|
+
input_examples = self.repo.get_examples_by_category(ExampleCategory.USER_INPUT)
|
|
24
|
+
|
|
25
|
+
# Should have at least one user input example
|
|
26
|
+
assert len(input_examples) > 0
|
|
27
|
+
|
|
28
|
+
# Check the safe input basics example specifically
|
|
29
|
+
safe_input_example = None
|
|
30
|
+
for example in input_examples:
|
|
31
|
+
if "safe_input_basics" in example.id:
|
|
32
|
+
safe_input_example = example
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
assert safe_input_example is not None, "Should have safe input basics example"
|
|
36
|
+
|
|
37
|
+
# Verify security practices in the code
|
|
38
|
+
code = safe_input_example.code
|
|
39
|
+
|
|
40
|
+
# Should use strip() to clean input
|
|
41
|
+
assert ".strip()" in code, "Should demonstrate input cleaning with strip()"
|
|
42
|
+
|
|
43
|
+
# Should use try-except for type conversion
|
|
44
|
+
assert "try:" in code and "except ValueError:" in code, \
|
|
45
|
+
"Should demonstrate error handling for type conversion"
|
|
46
|
+
|
|
47
|
+
# Should validate input ranges
|
|
48
|
+
assert "< 0" in code or "> 0" in code, \
|
|
49
|
+
"Should demonstrate input validation"
|
|
50
|
+
|
|
51
|
+
# Should use loops for input validation
|
|
52
|
+
assert "while True:" in code, \
|
|
53
|
+
"Should demonstrate input validation loops"
|
|
54
|
+
|
|
55
|
+
# Should provide user feedback for invalid input
|
|
56
|
+
assert "Please" in code and ("try again" in code or "enter" in code), \
|
|
57
|
+
"Should provide helpful error messages"
|
|
58
|
+
|
|
59
|
+
def test_list_examples_correctness(self):
|
|
60
|
+
"""Test that list examples demonstrate correct operations."""
|
|
61
|
+
list_examples = self.repo.get_examples_by_topic("lists")
|
|
62
|
+
|
|
63
|
+
assert len(list_examples) >= 2, "Should have multiple list examples"
|
|
64
|
+
|
|
65
|
+
# Check basic list example
|
|
66
|
+
basic_example = None
|
|
67
|
+
operations_example = None
|
|
68
|
+
|
|
69
|
+
for example in list_examples:
|
|
70
|
+
if "list_basics" in example.id:
|
|
71
|
+
basic_example = example
|
|
72
|
+
elif "list_operations" in example.id:
|
|
73
|
+
operations_example = example
|
|
74
|
+
|
|
75
|
+
assert basic_example is not None, "Should have basic list example"
|
|
76
|
+
assert operations_example is not None, "Should have list operations example"
|
|
77
|
+
|
|
78
|
+
# Test basic list operations
|
|
79
|
+
basic_code = basic_example.code
|
|
80
|
+
assert "fruits = [" in basic_code, "Should show list creation"
|
|
81
|
+
assert ".append(" in basic_code, "Should show append operation"
|
|
82
|
+
assert ".extend(" in basic_code, "Should show extend operation"
|
|
83
|
+
assert "[0]" in basic_code, "Should show indexing"
|
|
84
|
+
assert "[-1]" in basic_code, "Should show negative indexing"
|
|
85
|
+
|
|
86
|
+
# Test advanced list operations
|
|
87
|
+
ops_code = operations_example.code
|
|
88
|
+
assert ".sort()" in ops_code, "Should show in-place sorting"
|
|
89
|
+
assert "sorted(" in ops_code, "Should show non-destructive sorting"
|
|
90
|
+
assert ".index(" in ops_code, "Should show searching"
|
|
91
|
+
assert ".remove(" in ops_code, "Should show item removal"
|
|
92
|
+
assert ".pop()" in ops_code, "Should show pop operation"
|
|
93
|
+
assert "[x**2 for x in" in ops_code, "Should show list comprehension"
|
|
94
|
+
|
|
95
|
+
def test_dictionary_examples_correctness(self):
|
|
96
|
+
"""Test that dictionary examples demonstrate correct operations."""
|
|
97
|
+
dict_examples = self.repo.get_examples_by_topic("dictionaries")
|
|
98
|
+
|
|
99
|
+
assert len(dict_examples) >= 1, "Should have dictionary examples"
|
|
100
|
+
|
|
101
|
+
# Check basic dictionary example
|
|
102
|
+
basic_example = None
|
|
103
|
+
for example in dict_examples:
|
|
104
|
+
if "dict_basics" in example.id:
|
|
105
|
+
basic_example = example
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
assert basic_example is not None, "Should have basic dictionary example"
|
|
109
|
+
|
|
110
|
+
code = basic_example.code
|
|
111
|
+
|
|
112
|
+
# Should demonstrate dictionary creation
|
|
113
|
+
assert "student = {" in code, "Should show dictionary creation"
|
|
114
|
+
|
|
115
|
+
# Should show both direct access and safe access
|
|
116
|
+
assert "student['" in code, "Should show direct key access"
|
|
117
|
+
assert ".get(" in code, "Should show safe access with get()"
|
|
118
|
+
|
|
119
|
+
# Should demonstrate key existence checking
|
|
120
|
+
assert "in student" in code, "Should show key existence checking"
|
|
121
|
+
|
|
122
|
+
# Should show dictionary methods
|
|
123
|
+
assert ".keys()" in code, "Should show keys() method"
|
|
124
|
+
assert ".values()" in code, "Should show values() method"
|
|
125
|
+
|
|
126
|
+
# Should demonstrate adding new key-value pairs
|
|
127
|
+
assert "student['" in code and "=" in code, "Should show adding new keys"
|
|
128
|
+
|
|
129
|
+
def test_project_step_by_step_completeness(self):
|
|
130
|
+
"""Test that project templates have complete step-by-step instructions."""
|
|
131
|
+
calculator_project = self.repo.create_simple_project(ProjectType.CALCULATOR)
|
|
132
|
+
|
|
133
|
+
# Should have proper project structure
|
|
134
|
+
assert calculator_project.title, "Project should have a title"
|
|
135
|
+
assert calculator_project.description, "Project should have a description"
|
|
136
|
+
assert calculator_project.difficulty == "beginner", "Should be beginner level"
|
|
137
|
+
assert calculator_project.estimated_time > 0, "Should have estimated time"
|
|
138
|
+
|
|
139
|
+
# Should have multiple steps
|
|
140
|
+
assert len(calculator_project.steps) >= 2, "Should have multiple steps"
|
|
141
|
+
|
|
142
|
+
# Check first step (function definitions)
|
|
143
|
+
first_step = calculator_project.steps[0]
|
|
144
|
+
assert first_step.step_number == 1, "First step should be numbered 1"
|
|
145
|
+
assert "function" in first_step.title.lower(), "First step should be about functions"
|
|
146
|
+
assert first_step.code_snippet, "Step should have code snippet"
|
|
147
|
+
assert first_step.explanation, "Step should have explanation"
|
|
148
|
+
assert first_step.hints, "Step should have hints"
|
|
149
|
+
|
|
150
|
+
# Code should include basic arithmetic functions
|
|
151
|
+
code = first_step.code_snippet
|
|
152
|
+
assert "def add(" in code, "Should define add function"
|
|
153
|
+
assert "def subtract(" in code, "Should define subtract function"
|
|
154
|
+
assert "def multiply(" in code, "Should define multiply function"
|
|
155
|
+
assert "def divide(" in code, "Should define divide function"
|
|
156
|
+
assert "if b == 0:" in code, "Should handle division by zero"
|
|
157
|
+
|
|
158
|
+
# Check second step (main loop)
|
|
159
|
+
second_step = calculator_project.steps[1]
|
|
160
|
+
assert second_step.step_number == 2, "Second step should be numbered 2"
|
|
161
|
+
assert "loop" in second_step.title.lower() or "main" in second_step.title.lower(), \
|
|
162
|
+
"Second step should be about main loop"
|
|
163
|
+
|
|
164
|
+
main_code = second_step.code_snippet
|
|
165
|
+
assert "while True:" in main_code, "Should have main loop"
|
|
166
|
+
assert "input(" in main_code, "Should get user input"
|
|
167
|
+
assert "try:" in main_code and "except" in main_code, \
|
|
168
|
+
"Should handle input errors"
|
|
169
|
+
assert "quit" in main_code.lower(), "Should allow user to quit"
|
|
170
|
+
|
|
171
|
+
# Final code should be complete and runnable
|
|
172
|
+
final_code = calculator_project.final_code
|
|
173
|
+
assert "def add(" in final_code, "Final code should include all functions"
|
|
174
|
+
assert "def calculator(" in final_code, "Final code should include main function"
|
|
175
|
+
assert "if __name__" in final_code, "Final code should have main guard"
|
|
176
|
+
|
|
177
|
+
# Should have extension suggestions
|
|
178
|
+
assert calculator_project.extensions, "Should suggest extensions"
|
|
179
|
+
assert len(calculator_project.extensions) > 0, "Should have extension ideas"
|
|
180
|
+
|
|
181
|
+
def test_line_by_line_explanation_accuracy(self):
|
|
182
|
+
"""Test that line-by-line explanations are accurate and helpful."""
|
|
183
|
+
# Get a specific example to test
|
|
184
|
+
list_examples = self.repo.get_examples_by_topic("lists")
|
|
185
|
+
basic_example = None
|
|
186
|
+
|
|
187
|
+
for example in list_examples:
|
|
188
|
+
if "list_basics" in example.id:
|
|
189
|
+
basic_example = example
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
assert basic_example is not None, "Should have basic list example"
|
|
193
|
+
|
|
194
|
+
# Generate line-by-line explanation
|
|
195
|
+
explanation = self.repo.explain_example_line_by_line(basic_example)
|
|
196
|
+
|
|
197
|
+
# Should have explanation for each line
|
|
198
|
+
code_lines = basic_example.code.strip().split('\n')
|
|
199
|
+
assert len(explanation.lines) == len(code_lines), \
|
|
200
|
+
"Should have explanation for each line"
|
|
201
|
+
|
|
202
|
+
# Check specific line explanations
|
|
203
|
+
explanations_text = [line.explanation.lower() for line in explanation.lines]
|
|
204
|
+
|
|
205
|
+
# Should identify list creation
|
|
206
|
+
list_creation_found = any("list" in exp and ("create" in exp or "assign" in exp)
|
|
207
|
+
for exp in explanations_text)
|
|
208
|
+
assert list_creation_found, "Should identify list creation"
|
|
209
|
+
|
|
210
|
+
# Should identify indexing
|
|
211
|
+
indexing_found = any("index" in exp or "access" in exp
|
|
212
|
+
for exp in explanations_text)
|
|
213
|
+
assert indexing_found, "Should identify indexing operations"
|
|
214
|
+
|
|
215
|
+
# Should identify method calls
|
|
216
|
+
method_found = any(("append" in exp or "extend" in exp or
|
|
217
|
+
("adds" in exp and ("item" in exp or "multiple" in exp)))
|
|
218
|
+
for exp in explanations_text)
|
|
219
|
+
assert method_found, "Should identify method calls"
|
|
220
|
+
|
|
221
|
+
# Summary should be comprehensive
|
|
222
|
+
summary = explanation.summary.lower()
|
|
223
|
+
assert "list" in summary, "Summary should mention lists"
|
|
224
|
+
assert len(explanation.key_concepts) > 0, "Should identify key concepts"
|
|
225
|
+
assert "lists" in explanation.key_concepts, "Should identify lists as key concept"
|
|
226
|
+
|
|
227
|
+
def test_concept_breakdown_accuracy(self):
|
|
228
|
+
"""Test that concept breakdowns are accurate and educational."""
|
|
229
|
+
# Test specific concept breakdowns
|
|
230
|
+
list_comp_breakdown = self.repo.break_down_complex_concept("list comprehension")
|
|
231
|
+
|
|
232
|
+
assert len(list_comp_breakdown) >= 3, "Should have multiple breakdown steps"
|
|
233
|
+
|
|
234
|
+
breakdown_text = " ".join(list_comp_breakdown).lower()
|
|
235
|
+
assert "list comprehension" in breakdown_text, "Should mention the concept"
|
|
236
|
+
assert "expression" in breakdown_text, "Should explain expression part"
|
|
237
|
+
assert "iterable" in breakdown_text, "Should explain iterable part"
|
|
238
|
+
assert "for" in breakdown_text, "Should mention for loop connection"
|
|
239
|
+
|
|
240
|
+
# Test exception handling breakdown
|
|
241
|
+
exception_breakdown = self.repo.break_down_complex_concept("exception handling")
|
|
242
|
+
|
|
243
|
+
exception_text = " ".join(exception_breakdown).lower()
|
|
244
|
+
assert "exception" in exception_text or "error" in exception_text, \
|
|
245
|
+
"Should mention exceptions or errors"
|
|
246
|
+
assert "try" in exception_text, "Should mention try blocks"
|
|
247
|
+
assert "except" in exception_text, "Should mention except blocks"
|
|
248
|
+
|
|
249
|
+
# Test prerequisites
|
|
250
|
+
list_comp_prereqs = self.repo.get_concept_prerequisites("list comprehension")
|
|
251
|
+
assert "lists" in list_comp_prereqs, "List comprehension should require lists knowledge"
|
|
252
|
+
assert "for loops" in list_comp_prereqs, "List comprehension should require loop knowledge"
|
|
253
|
+
|
|
254
|
+
def test_search_functionality_specific_cases(self):
|
|
255
|
+
"""Test search functionality with specific queries."""
|
|
256
|
+
# Search for lists
|
|
257
|
+
list_results = self.repo.search_examples("list")
|
|
258
|
+
assert len(list_results) > 0, "Should find list examples"
|
|
259
|
+
|
|
260
|
+
# All results should be relevant to lists
|
|
261
|
+
for result in list_results:
|
|
262
|
+
result_text = (result.title + " " + result.description + " " +
|
|
263
|
+
" ".join(result.topics) + " " + result.code).lower()
|
|
264
|
+
assert "list" in result_text, "Search results should be relevant"
|
|
265
|
+
|
|
266
|
+
# Search for dictionaries
|
|
267
|
+
dict_results = self.repo.search_examples("dictionary")
|
|
268
|
+
assert len(dict_results) > 0, "Should find dictionary examples"
|
|
269
|
+
|
|
270
|
+
# Search with category filter
|
|
271
|
+
input_results = self.repo.search_examples("input", ExampleCategory.USER_INPUT)
|
|
272
|
+
assert len(input_results) > 0, "Should find input examples in USER_INPUT category"
|
|
273
|
+
|
|
274
|
+
for result in input_results:
|
|
275
|
+
assert result.category == ExampleCategory.USER_INPUT, \
|
|
276
|
+
"Filtered results should match category"
|
|
277
|
+
|
|
278
|
+
def test_example_safety_and_best_practices(self):
|
|
279
|
+
"""Test that examples follow safety and best practices."""
|
|
280
|
+
all_examples = []
|
|
281
|
+
for category in ExampleCategory:
|
|
282
|
+
all_examples.extend(self.repo.get_examples_by_category(category))
|
|
283
|
+
|
|
284
|
+
assert len(all_examples) > 0, "Should have examples to test"
|
|
285
|
+
|
|
286
|
+
for example in all_examples:
|
|
287
|
+
code = example.code
|
|
288
|
+
|
|
289
|
+
# Should not use dangerous practices
|
|
290
|
+
assert "eval(" not in code, "Examples should not use eval()"
|
|
291
|
+
assert "exec(" not in code, "Examples should not use exec()"
|
|
292
|
+
|
|
293
|
+
# Input examples should be safe
|
|
294
|
+
if example.category == ExampleCategory.USER_INPUT:
|
|
295
|
+
if "input(" in code:
|
|
296
|
+
# Should validate or clean input
|
|
297
|
+
assert (".strip()" in code or "try:" in code or
|
|
298
|
+
"if " in code), "Input examples should validate input"
|
|
299
|
+
|
|
300
|
+
# Should have proper error handling where needed for user input
|
|
301
|
+
if ("int(" in code or "float(" in code) and "input(" in code:
|
|
302
|
+
assert ("try:" in code and "except" in code), \
|
|
303
|
+
"Type conversion of user input should have error handling"
|