fishertools 0.2.1__py3-none-any.whl → 0.4.0__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 +16 -5
- fishertools/errors/__init__.py +11 -3
- fishertools/errors/exception_types.py +282 -0
- fishertools/errors/explainer.py +87 -1
- fishertools/errors/models.py +73 -1
- fishertools/errors/patterns.py +40 -0
- fishertools/examples/cli_example.py +156 -0
- fishertools/examples/learn_example.py +65 -0
- fishertools/examples/logger_example.py +176 -0
- fishertools/examples/menu_example.py +101 -0
- fishertools/examples/storage_example.py +175 -0
- fishertools/input_utils.py +185 -0
- fishertools/learn/__init__.py +19 -2
- fishertools/learn/examples.py +88 -1
- fishertools/learn/knowledge_engine.py +321 -0
- fishertools/learn/repl/__init__.py +19 -0
- fishertools/learn/repl/cli.py +31 -0
- fishertools/learn/repl/code_sandbox.py +229 -0
- fishertools/learn/repl/command_handler.py +544 -0
- fishertools/learn/repl/command_parser.py +165 -0
- fishertools/learn/repl/engine.py +479 -0
- fishertools/learn/repl/models.py +121 -0
- fishertools/learn/repl/session_manager.py +284 -0
- fishertools/learn/repl/test_code_sandbox.py +261 -0
- fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
- fishertools/learn/repl/test_command_handler.py +224 -0
- fishertools/learn/repl/test_command_handler_pbt.py +189 -0
- fishertools/learn/repl/test_command_parser.py +160 -0
- fishertools/learn/repl/test_command_parser_pbt.py +100 -0
- fishertools/learn/repl/test_engine.py +190 -0
- fishertools/learn/repl/test_session_manager.py +310 -0
- fishertools/learn/repl/test_session_manager_pbt.py +182 -0
- fishertools/learn/test_knowledge_engine.py +241 -0
- fishertools/learn/test_knowledge_engine_pbt.py +180 -0
- fishertools/patterns/__init__.py +46 -0
- fishertools/patterns/cli.py +175 -0
- fishertools/patterns/logger.py +140 -0
- fishertools/patterns/menu.py +99 -0
- fishertools/patterns/storage.py +127 -0
- fishertools/readme_transformer.py +631 -0
- fishertools/safe/__init__.py +6 -1
- fishertools/safe/files.py +329 -1
- fishertools/transform_readme.py +105 -0
- fishertools-0.4.0.dist-info/METADATA +104 -0
- fishertools-0.4.0.dist-info/RECORD +131 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
- tests/test_documentation_properties.py +329 -0
- tests/test_documentation_structure.py +349 -0
- tests/test_errors/test_exception_types.py +446 -0
- tests/test_errors/test_exception_types_pbt.py +333 -0
- tests/test_errors/test_patterns.py +52 -0
- tests/test_input_utils/__init__.py +1 -0
- tests/test_input_utils/test_input_utils.py +65 -0
- tests/test_learn/test_examples.py +179 -1
- tests/test_learn/test_explain_properties.py +307 -0
- tests/test_patterns_cli.py +611 -0
- tests/test_patterns_docstrings.py +473 -0
- tests/test_patterns_logger.py +465 -0
- tests/test_patterns_menu.py +440 -0
- tests/test_patterns_storage.py +447 -0
- tests/test_readme_enhancements_v0_3_1.py +2036 -0
- tests/test_readme_transformer/__init__.py +1 -0
- tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
- tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
- tests/test_safe/test_files.py +726 -1
- fishertools-0.2.1.dist-info/METADATA +0 -256
- fishertools-0.2.1.dist-info/RECORD +0 -81
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for documentation structure and correctness.
|
|
3
|
+
|
|
4
|
+
These tests verify universal properties that should hold across all documentation files.
|
|
5
|
+
Uses Hypothesis for property-based testing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from hypothesis import given, strategies as st
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestDocumentationProperties:
|
|
14
|
+
"""Property-based tests for documentation correctness."""
|
|
15
|
+
|
|
16
|
+
@given(st.just(None))
|
|
17
|
+
def test_property_1_all_sections_created(self, _):
|
|
18
|
+
"""
|
|
19
|
+
**Validates: Requirements 1.1, 1.3**
|
|
20
|
+
|
|
21
|
+
Property 1: All documentation sections are created
|
|
22
|
+
For each of the 7 required sections (Getting Started, Features, Installation,
|
|
23
|
+
API Reference, Examples, Limitations, Contributing), the corresponding file
|
|
24
|
+
should exist in docs/ folder with .md extension.
|
|
25
|
+
"""
|
|
26
|
+
docs_dir = Path("docs")
|
|
27
|
+
required_sections = [
|
|
28
|
+
"getting-started.md",
|
|
29
|
+
"features.md",
|
|
30
|
+
"installation.md",
|
|
31
|
+
"api-reference.md",
|
|
32
|
+
"examples.md",
|
|
33
|
+
"limitations.md",
|
|
34
|
+
"contributing.md",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
for section_file in required_sections:
|
|
38
|
+
filepath = docs_dir / section_file
|
|
39
|
+
assert filepath.exists(), f"Section file {section_file} does not exist"
|
|
40
|
+
assert filepath.suffix == ".md", f"Section file {section_file} is not markdown"
|
|
41
|
+
|
|
42
|
+
@given(st.just(None))
|
|
43
|
+
def test_property_2_all_sections_linked_from_readme(self, _):
|
|
44
|
+
"""
|
|
45
|
+
**Validates: Requirements 2.3, 3.3, 4.3, 5.3, 6.3, 7.3, 8.3, 9.3, 11.3**
|
|
46
|
+
|
|
47
|
+
Property 2: All sections are accessible by links from README
|
|
48
|
+
For each of the 7 documentation sections in docs/, the main README.md
|
|
49
|
+
should contain a working link to that section.
|
|
50
|
+
"""
|
|
51
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
52
|
+
readme_content = f.read()
|
|
53
|
+
|
|
54
|
+
required_links = [
|
|
55
|
+
"docs/getting-started.md",
|
|
56
|
+
"docs/features.md",
|
|
57
|
+
"docs/installation.md",
|
|
58
|
+
"docs/api-reference.md",
|
|
59
|
+
"docs/examples.md",
|
|
60
|
+
"docs/limitations.md",
|
|
61
|
+
"docs/contributing.md",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
for link in required_links:
|
|
65
|
+
assert link in readme_content, f"README missing link to {link}"
|
|
66
|
+
|
|
67
|
+
@given(st.just(None))
|
|
68
|
+
def test_property_3_all_links_work(self, _):
|
|
69
|
+
"""
|
|
70
|
+
**Validates: Requirements 11.1, 11.2**
|
|
71
|
+
|
|
72
|
+
Property 3: All links in documentation work
|
|
73
|
+
For any link in documentation files (in docs/ and main README),
|
|
74
|
+
that link should point to an existing file or section.
|
|
75
|
+
"""
|
|
76
|
+
docs_dir = Path("docs")
|
|
77
|
+
doc_files = [
|
|
78
|
+
"index.md",
|
|
79
|
+
"getting-started.md",
|
|
80
|
+
"features.md",
|
|
81
|
+
"installation.md",
|
|
82
|
+
"api-reference.md",
|
|
83
|
+
"examples.md",
|
|
84
|
+
"limitations.md",
|
|
85
|
+
"contributing.md",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
# Pattern to find markdown links: [text](path)
|
|
89
|
+
link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
|
|
90
|
+
|
|
91
|
+
for filename in doc_files:
|
|
92
|
+
filepath = docs_dir / filename
|
|
93
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
94
|
+
content = f.read()
|
|
95
|
+
|
|
96
|
+
# Find all links
|
|
97
|
+
links = re.findall(link_pattern, content)
|
|
98
|
+
|
|
99
|
+
for link_text, link_path in links:
|
|
100
|
+
# Skip external links (http, https, mailto)
|
|
101
|
+
if link_path.startswith(('http://', 'https://', 'mailto:')):
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
# Skip anchor-only links
|
|
105
|
+
if link_path.startswith('#'):
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
# Check if file exists
|
|
109
|
+
if link_path.startswith('../'):
|
|
110
|
+
# Relative path going up
|
|
111
|
+
target_path = Path(filepath.parent) / link_path
|
|
112
|
+
else:
|
|
113
|
+
# Relative path in same directory
|
|
114
|
+
target_path = Path(filepath.parent) / link_path
|
|
115
|
+
|
|
116
|
+
# Normalize path
|
|
117
|
+
target_path = target_path.resolve()
|
|
118
|
+
|
|
119
|
+
# For links like "index.md", check if file exists
|
|
120
|
+
if not link_path.startswith('#'):
|
|
121
|
+
assert target_path.exists() or target_path.with_suffix('').exists(), \
|
|
122
|
+
f"Link {link_path} in {filename} points to non-existent file"
|
|
123
|
+
|
|
124
|
+
@given(st.just(None))
|
|
125
|
+
def test_property_4_readme_brief_description(self, _):
|
|
126
|
+
"""
|
|
127
|
+
**Validates: Requirements 9.1**
|
|
128
|
+
|
|
129
|
+
Property 4: Main README contains brief description
|
|
130
|
+
For the main README.md, the project description should contain
|
|
131
|
+
no more than 50 words.
|
|
132
|
+
"""
|
|
133
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
134
|
+
content = f.read()
|
|
135
|
+
|
|
136
|
+
# Extract the main description (first paragraph after title)
|
|
137
|
+
lines = content.split('\n')
|
|
138
|
+
description_lines = []
|
|
139
|
+
in_description = False
|
|
140
|
+
|
|
141
|
+
for line in lines:
|
|
142
|
+
if line.startswith('# '):
|
|
143
|
+
in_description = True
|
|
144
|
+
continue
|
|
145
|
+
if in_description:
|
|
146
|
+
if line.startswith('##') or line.startswith('```'):
|
|
147
|
+
break
|
|
148
|
+
if line.strip():
|
|
149
|
+
description_lines.append(line)
|
|
150
|
+
|
|
151
|
+
description = ' '.join(description_lines).strip()
|
|
152
|
+
word_count = len(description.split())
|
|
153
|
+
|
|
154
|
+
assert word_count <= 50, f"README description has {word_count} words, should be <= 50"
|
|
155
|
+
|
|
156
|
+
@given(st.just(None))
|
|
157
|
+
def test_property_5_readme_contains_all_section_links(self, _):
|
|
158
|
+
"""
|
|
159
|
+
**Validates: Requirements 9.3, 11.3**
|
|
160
|
+
|
|
161
|
+
Property 5: Main README contains links to all sections
|
|
162
|
+
For the main README.md, it should contain links to all 7 documentation
|
|
163
|
+
sections (Getting Started, Features, Installation, API Reference,
|
|
164
|
+
Examples, Limitations, Contributing).
|
|
165
|
+
"""
|
|
166
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
167
|
+
content = f.read()
|
|
168
|
+
|
|
169
|
+
required_sections = [
|
|
170
|
+
"Getting Started",
|
|
171
|
+
"Features",
|
|
172
|
+
"Installation",
|
|
173
|
+
"API Reference",
|
|
174
|
+
"Examples",
|
|
175
|
+
"Limitations",
|
|
176
|
+
"Contributing",
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
for section in required_sections:
|
|
180
|
+
assert section in content, f"README missing reference to {section}"
|
|
181
|
+
|
|
182
|
+
@given(st.just(None))
|
|
183
|
+
def test_property_6_each_section_links_to_index(self, _):
|
|
184
|
+
"""
|
|
185
|
+
**Validates: Requirements 11.4**
|
|
186
|
+
|
|
187
|
+
Property 6: Each section contains link to main page
|
|
188
|
+
For each file in docs/ folder, that file should contain a link to
|
|
189
|
+
the main documentation page (index.md or README.md).
|
|
190
|
+
"""
|
|
191
|
+
docs_dir = Path("docs")
|
|
192
|
+
doc_files = [
|
|
193
|
+
"getting-started.md",
|
|
194
|
+
"features.md",
|
|
195
|
+
"installation.md",
|
|
196
|
+
"api-reference.md",
|
|
197
|
+
"examples.md",
|
|
198
|
+
"limitations.md",
|
|
199
|
+
"contributing.md",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
for filename in doc_files:
|
|
203
|
+
filepath = docs_dir / filename
|
|
204
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
205
|
+
content = f.read()
|
|
206
|
+
|
|
207
|
+
# Check for link back to index or documentation
|
|
208
|
+
has_return_link = (
|
|
209
|
+
"index.md" in content or
|
|
210
|
+
"Documentation Index" in content or
|
|
211
|
+
"Return to" in content or
|
|
212
|
+
"Back to" in content
|
|
213
|
+
)
|
|
214
|
+
assert has_return_link, f"{filename} missing return link to index"
|
|
215
|
+
|
|
216
|
+
@given(st.just(None))
|
|
217
|
+
def test_property_7_readme_no_duplication(self, _):
|
|
218
|
+
"""
|
|
219
|
+
**Validates: Requirements 9.5, 10.3**
|
|
220
|
+
|
|
221
|
+
Property 7: Main README contains no duplicated information
|
|
222
|
+
For the main README.md, it should contain only brief information and links,
|
|
223
|
+
without detailed sections that are in docs/.
|
|
224
|
+
"""
|
|
225
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
226
|
+
readme_content = f.read()
|
|
227
|
+
|
|
228
|
+
# Check that README doesn't contain detailed sections
|
|
229
|
+
# that should be in docs/
|
|
230
|
+
detailed_sections = [
|
|
231
|
+
"🚨 Объяснение ошибок", # Russian
|
|
232
|
+
"🛡️ Безопасные утилиты", # Russian
|
|
233
|
+
"📚 Обучающие инструменты", # Russian
|
|
234
|
+
"🔧 Готовые паттерны", # Russian
|
|
235
|
+
"## 🎯 Основные возможности", # Russian
|
|
236
|
+
"### 🚨 Объяснение ошибок Python", # Russian
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
# Count how many detailed sections are in README
|
|
240
|
+
detailed_count = sum(1 for section in detailed_sections if section in readme_content)
|
|
241
|
+
|
|
242
|
+
# README should have minimal detailed content (mostly links)
|
|
243
|
+
# Allow some basic feature descriptions but not full sections
|
|
244
|
+
assert detailed_count == 0, f"README contains {detailed_count} detailed sections that should be in docs/"
|
|
245
|
+
|
|
246
|
+
@given(st.just(None))
|
|
247
|
+
def test_property_all_files_have_content(self, _):
|
|
248
|
+
"""
|
|
249
|
+
Property: All documentation files have substantial content
|
|
250
|
+
For each documentation file, it should have more than 100 bytes of content.
|
|
251
|
+
"""
|
|
252
|
+
docs_dir = Path("docs")
|
|
253
|
+
doc_files = [
|
|
254
|
+
"index.md",
|
|
255
|
+
"getting-started.md",
|
|
256
|
+
"features.md",
|
|
257
|
+
"installation.md",
|
|
258
|
+
"api-reference.md",
|
|
259
|
+
"examples.md",
|
|
260
|
+
"limitations.md",
|
|
261
|
+
"contributing.md",
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
for filename in doc_files:
|
|
265
|
+
filepath = docs_dir / filename
|
|
266
|
+
size = filepath.stat().st_size
|
|
267
|
+
assert size > 100, f"{filename} is too small ({size} bytes)"
|
|
268
|
+
|
|
269
|
+
@given(st.just(None))
|
|
270
|
+
def test_property_all_files_have_headings(self, _):
|
|
271
|
+
"""
|
|
272
|
+
Property: All documentation files have proper markdown headings
|
|
273
|
+
For each documentation file, it should start with a markdown heading.
|
|
274
|
+
"""
|
|
275
|
+
docs_dir = Path("docs")
|
|
276
|
+
doc_files = [
|
|
277
|
+
"index.md",
|
|
278
|
+
"getting-started.md",
|
|
279
|
+
"features.md",
|
|
280
|
+
"installation.md",
|
|
281
|
+
"api-reference.md",
|
|
282
|
+
"examples.md",
|
|
283
|
+
"limitations.md",
|
|
284
|
+
"contributing.md",
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
for filename in doc_files:
|
|
288
|
+
filepath = docs_dir / filename
|
|
289
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
290
|
+
content = f.read()
|
|
291
|
+
|
|
292
|
+
assert content.startswith("#"), f"{filename} should start with a markdown heading"
|
|
293
|
+
assert "#" in content, f"{filename} should contain markdown headings"
|
|
294
|
+
|
|
295
|
+
@given(st.just(None))
|
|
296
|
+
def test_property_index_has_navigation(self, _):
|
|
297
|
+
"""
|
|
298
|
+
Property: Index page has navigation to all sections
|
|
299
|
+
The index.md file should contain links to all 7 documentation sections.
|
|
300
|
+
"""
|
|
301
|
+
with open("docs/index.md", "r", encoding="utf-8") as f:
|
|
302
|
+
content = f.read()
|
|
303
|
+
|
|
304
|
+
required_links = [
|
|
305
|
+
"getting-started.md",
|
|
306
|
+
"features.md",
|
|
307
|
+
"installation.md",
|
|
308
|
+
"api-reference.md",
|
|
309
|
+
"examples.md",
|
|
310
|
+
"limitations.md",
|
|
311
|
+
"contributing.md",
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
for link in required_links:
|
|
315
|
+
assert link in content, f"index.md missing link to {link}"
|
|
316
|
+
|
|
317
|
+
@given(st.just(None))
|
|
318
|
+
def test_property_readme_has_quick_reference(self, _):
|
|
319
|
+
"""
|
|
320
|
+
Property: README has quick reference table
|
|
321
|
+
The main README.md should contain a quick reference table with functions.
|
|
322
|
+
"""
|
|
323
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
324
|
+
content = f.read()
|
|
325
|
+
|
|
326
|
+
# Check for table structure
|
|
327
|
+
assert "|" in content, "README missing table structure"
|
|
328
|
+
assert "explain_error" in content, "README missing explain_error in quick reference"
|
|
329
|
+
assert "safe_get" in content, "README missing safe_get in quick reference"
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for documentation structure and content.
|
|
3
|
+
|
|
4
|
+
Tests verify that all documentation files exist, contain required sections,
|
|
5
|
+
and have proper links and navigation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestDocumentationStructure:
|
|
14
|
+
"""Test documentation file structure and existence."""
|
|
15
|
+
|
|
16
|
+
def test_all_documentation_files_exist(self):
|
|
17
|
+
"""Test that all required documentation files exist in docs/ folder."""
|
|
18
|
+
docs_dir = Path("docs")
|
|
19
|
+
required_files = [
|
|
20
|
+
"index.md",
|
|
21
|
+
"getting-started.md",
|
|
22
|
+
"features.md",
|
|
23
|
+
"installation.md",
|
|
24
|
+
"api-reference.md",
|
|
25
|
+
"examples.md",
|
|
26
|
+
"limitations.md",
|
|
27
|
+
"contributing.md",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
for filename in required_files:
|
|
31
|
+
filepath = docs_dir / filename
|
|
32
|
+
assert filepath.exists(), f"Documentation file {filename} not found in docs/"
|
|
33
|
+
assert filepath.is_file(), f"{filename} is not a file"
|
|
34
|
+
|
|
35
|
+
def test_all_files_are_markdown(self):
|
|
36
|
+
"""Test that all documentation files have .md extension."""
|
|
37
|
+
docs_dir = Path("docs")
|
|
38
|
+
required_files = [
|
|
39
|
+
"index.md",
|
|
40
|
+
"getting-started.md",
|
|
41
|
+
"features.md",
|
|
42
|
+
"installation.md",
|
|
43
|
+
"api-reference.md",
|
|
44
|
+
"examples.md",
|
|
45
|
+
"limitations.md",
|
|
46
|
+
"contributing.md",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
for filename in required_files:
|
|
50
|
+
assert filename.endswith(".md"), f"{filename} does not have .md extension"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TestGettingStartedContent:
|
|
54
|
+
"""Test Getting Started documentation content."""
|
|
55
|
+
|
|
56
|
+
def test_getting_started_contains_installation_section(self):
|
|
57
|
+
"""Test that Getting Started contains installation instructions."""
|
|
58
|
+
with open("docs/getting-started.md", "r", encoding="utf-8") as f:
|
|
59
|
+
content = f.read()
|
|
60
|
+
|
|
61
|
+
assert "Installation" in content, "Getting Started missing Installation section"
|
|
62
|
+
assert "pip install" in content, "Getting Started missing pip install command"
|
|
63
|
+
|
|
64
|
+
def test_getting_started_contains_first_example(self):
|
|
65
|
+
"""Test that Getting Started contains a first example."""
|
|
66
|
+
with open("docs/getting-started.md", "r", encoding="utf-8") as f:
|
|
67
|
+
content = f.read()
|
|
68
|
+
|
|
69
|
+
assert "example" in content.lower(), "Getting Started missing example"
|
|
70
|
+
assert "```python" in content, "Getting Started missing code example"
|
|
71
|
+
|
|
72
|
+
def test_getting_started_contains_links(self):
|
|
73
|
+
"""Test that Getting Started contains links to other sections."""
|
|
74
|
+
with open("docs/getting-started.md", "r", encoding="utf-8") as f:
|
|
75
|
+
content = f.read()
|
|
76
|
+
|
|
77
|
+
assert "[Installation]" in content, "Getting Started missing link to Installation"
|
|
78
|
+
assert "[Examples]" in content, "Getting Started missing link to Examples"
|
|
79
|
+
assert "[Features]" in content, "Getting Started missing link to Features"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestFeaturesContent:
|
|
83
|
+
"""Test Features documentation content."""
|
|
84
|
+
|
|
85
|
+
def test_features_contains_feature_list(self):
|
|
86
|
+
"""Test that Features contains a list of features."""
|
|
87
|
+
with open("docs/features.md", "r", encoding="utf-8") as f:
|
|
88
|
+
content = f.read()
|
|
89
|
+
|
|
90
|
+
assert "Error Explanation" in content, "Features missing Error Explanation"
|
|
91
|
+
assert "Safe Utilities" in content, "Features missing Safe Utilities"
|
|
92
|
+
assert "Learning Tools" in content, "Features missing Learning Tools"
|
|
93
|
+
|
|
94
|
+
def test_features_contains_descriptions(self):
|
|
95
|
+
"""Test that Features contains descriptions of each feature."""
|
|
96
|
+
with open("docs/features.md", "r", encoding="utf-8") as f:
|
|
97
|
+
content = f.read()
|
|
98
|
+
|
|
99
|
+
# Check for feature descriptions
|
|
100
|
+
assert "explain_error" in content, "Features missing explain_error description"
|
|
101
|
+
assert "safe_get" in content, "Features missing safe_get description"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestInstallationContent:
|
|
105
|
+
"""Test Installation documentation content."""
|
|
106
|
+
|
|
107
|
+
def test_installation_contains_system_requirements(self):
|
|
108
|
+
"""Test that Installation contains system requirements."""
|
|
109
|
+
with open("docs/installation.md", "r", encoding="utf-8") as f:
|
|
110
|
+
content = f.read()
|
|
111
|
+
|
|
112
|
+
assert "Python" in content, "Installation missing Python requirement"
|
|
113
|
+
assert "3.8" in content, "Installation missing Python version requirement"
|
|
114
|
+
|
|
115
|
+
def test_installation_contains_os_specific_instructions(self):
|
|
116
|
+
"""Test that Installation contains instructions for different OS."""
|
|
117
|
+
with open("docs/installation.md", "r", encoding="utf-8") as f:
|
|
118
|
+
content = f.read()
|
|
119
|
+
|
|
120
|
+
assert "Linux" in content, "Installation missing Linux instructions"
|
|
121
|
+
assert "macOS" in content, "Installation missing macOS instructions"
|
|
122
|
+
assert "Windows" in content, "Installation missing Windows instructions"
|
|
123
|
+
|
|
124
|
+
def test_installation_contains_dependencies(self):
|
|
125
|
+
"""Test that Installation mentions dependencies."""
|
|
126
|
+
with open("docs/installation.md", "r", encoding="utf-8") as f:
|
|
127
|
+
content = f.read()
|
|
128
|
+
|
|
129
|
+
assert "requests" in content, "Installation missing requests dependency"
|
|
130
|
+
assert "click" in content, "Installation missing click dependency"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class TestAPIReferenceContent:
|
|
134
|
+
"""Test API Reference documentation content."""
|
|
135
|
+
|
|
136
|
+
def test_api_reference_contains_main_functions(self):
|
|
137
|
+
"""Test that API Reference documents main functions."""
|
|
138
|
+
with open("docs/api-reference.md", "r", encoding="utf-8") as f:
|
|
139
|
+
content = f.read()
|
|
140
|
+
|
|
141
|
+
assert "explain_error" in content, "API Reference missing explain_error"
|
|
142
|
+
assert "safe_get" in content, "API Reference missing safe_get"
|
|
143
|
+
assert "explain" in content, "API Reference missing explain"
|
|
144
|
+
|
|
145
|
+
def test_api_reference_contains_parameters(self):
|
|
146
|
+
"""Test that API Reference includes function parameters."""
|
|
147
|
+
with open("docs/api-reference.md", "r", encoding="utf-8") as f:
|
|
148
|
+
content = f.read()
|
|
149
|
+
|
|
150
|
+
assert "Parameters" in content, "API Reference missing Parameters section"
|
|
151
|
+
assert "Returns" in content, "API Reference missing Returns section"
|
|
152
|
+
|
|
153
|
+
def test_api_reference_contains_examples(self):
|
|
154
|
+
"""Test that API Reference includes code examples."""
|
|
155
|
+
with open("docs/api-reference.md", "r", encoding="utf-8") as f:
|
|
156
|
+
content = f.read()
|
|
157
|
+
|
|
158
|
+
assert "```python" in content, "API Reference missing code examples"
|
|
159
|
+
assert "Example" in content, "API Reference missing Example section"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TestExamplesContent:
|
|
163
|
+
"""Test Examples documentation content."""
|
|
164
|
+
|
|
165
|
+
def test_examples_contains_code_examples(self):
|
|
166
|
+
"""Test that Examples contains code examples."""
|
|
167
|
+
with open("docs/examples.md", "r", encoding="utf-8") as f:
|
|
168
|
+
content = f.read()
|
|
169
|
+
|
|
170
|
+
assert "```python" in content, "Examples missing Python code"
|
|
171
|
+
assert "Example" in content, "Examples missing Example sections"
|
|
172
|
+
|
|
173
|
+
def test_examples_contains_multiple_examples(self):
|
|
174
|
+
"""Test that Examples contains multiple examples."""
|
|
175
|
+
with open("docs/examples.md", "r", encoding="utf-8") as f:
|
|
176
|
+
content = f.read()
|
|
177
|
+
|
|
178
|
+
# Count example sections
|
|
179
|
+
example_count = content.count("## Example")
|
|
180
|
+
assert example_count >= 3, f"Examples should have at least 3 examples, found {example_count}"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestLimitationsContent:
|
|
184
|
+
"""Test Limitations documentation content."""
|
|
185
|
+
|
|
186
|
+
def test_limitations_contains_limitation_list(self):
|
|
187
|
+
"""Test that Limitations contains a list of limitations."""
|
|
188
|
+
with open("docs/limitations.md", "r", encoding="utf-8") as f:
|
|
189
|
+
content = f.read()
|
|
190
|
+
|
|
191
|
+
assert "SyntaxError" in content, "Limitations missing SyntaxError limitation"
|
|
192
|
+
assert "OOP" in content or "Object-Oriented" in content, "Limitations missing OOP limitation"
|
|
193
|
+
|
|
194
|
+
def test_limitations_contains_explanations(self):
|
|
195
|
+
"""Test that Limitations explains each limitation."""
|
|
196
|
+
with open("docs/limitations.md", "r", encoding="utf-8") as f:
|
|
197
|
+
content = f.read()
|
|
198
|
+
|
|
199
|
+
assert "Problem" in content or "The Problem" in content, "Limitations missing problem descriptions"
|
|
200
|
+
assert "Workaround" in content, "Limitations missing workarounds"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class TestContributingContent:
|
|
204
|
+
"""Test Contributing documentation content."""
|
|
205
|
+
|
|
206
|
+
def test_contributing_contains_contribution_types(self):
|
|
207
|
+
"""Test that Contributing explains how to contribute."""
|
|
208
|
+
with open("docs/contributing.md", "r", encoding="utf-8") as f:
|
|
209
|
+
content = f.read()
|
|
210
|
+
|
|
211
|
+
assert "Report bugs" in content or "bug" in content.lower(), "Contributing missing bug reporting info"
|
|
212
|
+
assert "code" in content.lower(), "Contributing missing code contribution info"
|
|
213
|
+
|
|
214
|
+
def test_contributing_contains_development_setup(self):
|
|
215
|
+
"""Test that Contributing includes development setup instructions."""
|
|
216
|
+
with open("docs/contributing.md", "r", encoding="utf-8") as f:
|
|
217
|
+
content = f.read()
|
|
218
|
+
|
|
219
|
+
assert "Fork" in content, "Contributing missing fork instructions"
|
|
220
|
+
assert "git clone" in content, "Contributing missing clone instructions"
|
|
221
|
+
assert "pip install" in content, "Contributing missing installation instructions"
|
|
222
|
+
|
|
223
|
+
def test_contributing_contains_testing_info(self):
|
|
224
|
+
"""Test that Contributing includes testing information."""
|
|
225
|
+
with open("docs/contributing.md", "r", encoding="utf-8") as f:
|
|
226
|
+
content = f.read()
|
|
227
|
+
|
|
228
|
+
assert "pytest" in content, "Contributing missing pytest information"
|
|
229
|
+
assert "test" in content.lower(), "Contributing missing testing information"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class TestDocumentationLinks:
|
|
233
|
+
"""Test that documentation links are properly formatted."""
|
|
234
|
+
|
|
235
|
+
def test_index_contains_navigation_links(self):
|
|
236
|
+
"""Test that index.md contains links to all sections."""
|
|
237
|
+
with open("docs/index.md", "r", encoding="utf-8") as f:
|
|
238
|
+
content = f.read()
|
|
239
|
+
|
|
240
|
+
required_links = [
|
|
241
|
+
"getting-started.md",
|
|
242
|
+
"features.md",
|
|
243
|
+
"installation.md",
|
|
244
|
+
"api-reference.md",
|
|
245
|
+
"examples.md",
|
|
246
|
+
"limitations.md",
|
|
247
|
+
"contributing.md",
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
for link in required_links:
|
|
251
|
+
assert link in content, f"index.md missing link to {link}"
|
|
252
|
+
|
|
253
|
+
def test_all_files_have_return_link(self):
|
|
254
|
+
"""Test that all documentation files have a link back to index."""
|
|
255
|
+
docs_dir = Path("docs")
|
|
256
|
+
doc_files = [
|
|
257
|
+
"getting-started.md",
|
|
258
|
+
"features.md",
|
|
259
|
+
"installation.md",
|
|
260
|
+
"api-reference.md",
|
|
261
|
+
"examples.md",
|
|
262
|
+
"limitations.md",
|
|
263
|
+
"contributing.md",
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
for filename in doc_files:
|
|
267
|
+
with open(docs_dir / filename, "r", encoding="utf-8") as f:
|
|
268
|
+
content = f.read()
|
|
269
|
+
|
|
270
|
+
# Check for link back to index or documentation
|
|
271
|
+
has_return_link = (
|
|
272
|
+
"index.md" in content or
|
|
273
|
+
"Documentation Index" in content or
|
|
274
|
+
"Return to" in content
|
|
275
|
+
)
|
|
276
|
+
assert has_return_link, f"{filename} missing return link to index"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class TestREADMEContent:
|
|
280
|
+
"""Test main README.md content."""
|
|
281
|
+
|
|
282
|
+
def test_readme_contains_brief_description(self):
|
|
283
|
+
"""Test that README contains a brief project description."""
|
|
284
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
285
|
+
content = f.read()
|
|
286
|
+
|
|
287
|
+
# Check for project description
|
|
288
|
+
assert "Fishertools" in content or "fishertools" in content, "README missing project name"
|
|
289
|
+
assert "Python" in content, "README missing Python mention"
|
|
290
|
+
|
|
291
|
+
def test_readme_contains_quick_install(self):
|
|
292
|
+
"""Test that README contains quick installation instructions."""
|
|
293
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
294
|
+
content = f.read()
|
|
295
|
+
|
|
296
|
+
assert "pip install" in content, "README missing pip install command"
|
|
297
|
+
|
|
298
|
+
def test_readme_contains_documentation_links(self):
|
|
299
|
+
"""Test that README contains links to documentation sections."""
|
|
300
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
301
|
+
content = f.read()
|
|
302
|
+
|
|
303
|
+
# Check for links to docs
|
|
304
|
+
assert "docs/" in content or "documentation" in content.lower(), "README missing links to documentation"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class TestDocumentationConsistency:
|
|
308
|
+
"""Test consistency across documentation files."""
|
|
309
|
+
|
|
310
|
+
def test_all_files_have_proper_headings(self):
|
|
311
|
+
"""Test that all documentation files have proper markdown headings."""
|
|
312
|
+
docs_dir = Path("docs")
|
|
313
|
+
doc_files = [
|
|
314
|
+
"index.md",
|
|
315
|
+
"getting-started.md",
|
|
316
|
+
"features.md",
|
|
317
|
+
"installation.md",
|
|
318
|
+
"api-reference.md",
|
|
319
|
+
"examples.md",
|
|
320
|
+
"limitations.md",
|
|
321
|
+
"contributing.md",
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
for filename in doc_files:
|
|
325
|
+
with open(docs_dir / filename, "r", encoding="utf-8") as f:
|
|
326
|
+
content = f.read()
|
|
327
|
+
|
|
328
|
+
# Check for at least one heading
|
|
329
|
+
assert "#" in content, f"{filename} missing markdown headings"
|
|
330
|
+
assert content.startswith("#"), f"{filename} should start with a heading"
|
|
331
|
+
|
|
332
|
+
def test_all_files_are_not_empty(self):
|
|
333
|
+
"""Test that all documentation files have content."""
|
|
334
|
+
docs_dir = Path("docs")
|
|
335
|
+
doc_files = [
|
|
336
|
+
"index.md",
|
|
337
|
+
"getting-started.md",
|
|
338
|
+
"features.md",
|
|
339
|
+
"installation.md",
|
|
340
|
+
"api-reference.md",
|
|
341
|
+
"examples.md",
|
|
342
|
+
"limitations.md",
|
|
343
|
+
"contributing.md",
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
for filename in doc_files:
|
|
347
|
+
filepath = docs_dir / filename
|
|
348
|
+
size = filepath.stat().st_size
|
|
349
|
+
assert size > 100, f"{filename} is too small (likely empty or minimal)"
|