8Tests for the skeleton exporter: core extraction, docstring stripping,
9non-Python placeholders, and integration.
13- Verify extract_skeleton produces correct output for various Python patterns.
14- Verify _strip_docstring_section removes targeted sections.
15- Verify non-Python files produce one-line placeholders.
16- Verify write_skeleton_file_for_dir and clean_skeleton_dirs work end-to-end.
28- All tests are self-contained with no external dependencies beyond pytest.
32from pathlib
import Path
36from oct.tools.skeleton_exporter
import (
38 _strip_docstring_section,
39 _skeleton_for_non_python,
40 write_skeleton_file_for_dir,
49SIMPLE_MODULE = textwrap.dedent(
'''\
50 #!/usr/bin/env python3
51 # -*- coding: utf-8 -*-
79 def hello(name: str) -> str:
81 return f"Hello, {name}!"
90CLASS_MODULE = textwrap.dedent(
'''\
91 #!/usr/bin/env python3
92 # -*- coding: utf-8 -*-
110 def save(self) -> None:
111 """Persist to storage."""
115 class User(Base, metaclass=type):
119 def name(self) -> str:
124 def create(data: dict) -> "User":
125 """Factory method."""
127 u._name = data["name"]
131 """Inner meta class."""
135ASYNC_MODULE = textwrap.dedent(
'''\
136 #!/usr/bin/env python3
137 # -*- coding: utf-8 -*-
138 # myproject/async_mod.py
140 async def fetch(url: str, *, timeout: int = 30) -> bytes:
141 """Fetch data from URL."""
142 async with session.get(url, timeout=timeout) as resp:
143 return await resp.read()
146DECORATOR_MODULE = textwrap.dedent(
'''\
147 #!/usr/bin/env python3
148 # -*- coding: utf-8 -*-
149 # myproject/routes.py
151 @app.route("/api/v1", methods=["GET", "POST"])
152 def api_handler(request):
153 """Handle API requests."""
154 return process(request)
163 """Unit tests for extract_skeleton()."""
166 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
167 assert "#!/usr/bin/env python3" in skeleton
168 assert "# -*- coding: utf-8 -*-" in skeleton
169 assert "# myproject/simple.py" in skeleton
172 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
173 assert "Purpose" in skeleton
174 assert "A simple module." in skeleton
177 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py",
179 assert "Purpose" in skeleton
180 assert "Responsibilities" in skeleton
181 assert "Domain: TEST" not in skeleton
182 assert "L2" not in skeleton
185 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py",
187 assert "Contracts" in skeleton
188 assert "Inputs: none" in skeleton
191 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
192 assert "def hello(name: str) -> str:" in skeleton
193 assert "def _private(x):" in skeleton
196 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
197 assert "Greet someone." in skeleton
200 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
201 assert 'f"Hello, {name}!"' not in skeleton
202 assert "range(x)" not in skeleton
203 assert "print(i)" not in skeleton
204 assert "return x * 2" not in skeleton
207 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
208 assert "MAX_RETRIES = ..." in skeleton
211 skeleton = extract_skeleton(CLASS_MODULE,
"myproject/models.py")
212 assert "class Base:" in skeleton
213 assert "class User(Base, metaclass=type):" in skeleton
214 assert "def save(self) -> None:" in skeleton
215 assert "User model." in skeleton
218 skeleton = extract_skeleton(CLASS_MODULE,
"myproject/models.py")
219 assert "@property" in skeleton
220 assert "@staticmethod" in skeleton
223 skeleton = extract_skeleton(DECORATOR_MODULE,
"myproject/routes.py")
225 assert "@app.route(" in skeleton
226 assert "methods=" in skeleton
229 skeleton = extract_skeleton(ASYNC_MODULE,
"myproject/async_mod.py")
231 assert "async def fetch(" in skeleton
232 assert "url: str" in skeleton
233 assert "timeout: int" in skeleton
234 assert "-> bytes:" in skeleton
237 skeleton = extract_skeleton(CLASS_MODULE,
"myproject/models.py")
238 assert "class Meta:" in skeleton
239 assert "Inner meta class." in skeleton
242 skeleton = extract_skeleton(CLASS_MODULE,
"myproject/models.py")
243 assert "_write_to_db" not in skeleton
244 assert "_update_cache" not in skeleton
247 bad_code =
"#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# bad.py\ndef ("
248 skeleton = extract_skeleton(bad_code,
"bad.py")
249 assert "#!/usr/bin/env python3" in skeleton
250 assert "syntax error" in skeleton.lower()
253 skeleton = extract_skeleton(
"",
"empty.py")
254 assert skeleton.strip() ==
""
257 skeleton = extract_skeleton(SIMPLE_MODULE,
"myproject/simple.py")
258 assert " ..." in skeleton
266 """Unit tests for _strip_docstring_section."""
268 DOCSTRING = textwrap.dedent(
"""\
288 result = _strip_docstring_section(self.
DOCSTRING,
"Diagnostics")
289 assert "Purpose" in result
290 assert "Responsibilities" in result
291 assert "Domain: TEST" not in result
292 assert "Contracts" in result
295 result = _strip_docstring_section(self.
DOCSTRING,
"Contracts")
296 assert "Diagnostics" in result
297 assert "Inputs: none" not in result
300 result = _strip_docstring_section(self.
DOCSTRING,
"NonExistent")
301 assert "Purpose" in result
302 assert "Diagnostics" in result
303 assert "Contracts" in result
306 result = _strip_docstring_section(self.
DOCSTRING,
"Responsibilities")
307 assert "Purpose" in result
308 assert "Thing one." not in result
309 assert "Diagnostics" in result
312 """OI-415: section headers starting with digits must be handled."""
313 docstring = textwrap.dedent(
"""\
327 result = _strip_docstring_section(docstring,
"Purpose")
328 assert "Stuff." not in result
329 assert "Layer 3" in result
330 assert "Contracts" in result
333 """OI-415: non-ASCII section headers must still terminate matching."""
334 docstring = textwrap.dedent(
"""\
347 result = _strip_docstring_section(docstring,
"Purpose")
348 assert "Do stuff." not in result
349 assert "Résumé" in result
350 assert "Summary line." in result
353 """OI-415: headers with trailing whitespace should still be matched."""
367 result = _strip_docstring_section(docstring,
"Diagnostics")
368 assert "Domain: TEST" not in result
369 assert "Purpose" in result
370 assert "Contracts" in result
378 """Tests for non-Python file placeholder output."""
381 f = tmp_path /
"config.json"
382 f.write_text(
'{"key": "value"}\n', encoding=
"utf-8")
383 result = _skeleton_for_non_python(f)
384 assert "config.json" in result
385 assert "line" in result
386 assert "KB" in result
389 f = tmp_path /
"README.md"
390 f.write_text(
"# Title\n\nSome content.\n", encoding=
"utf-8")
391 result = _skeleton_for_non_python(f)
392 assert "README.md" in result
393 assert "KB" in result
401 """Integration tests using write_skeleton_file_for_dir and clean."""
405 """Create a minimal project structure."""
406 src = tmp_path /
"myproject"
408 (src /
"docs").mkdir()
409 (src /
"tests").mkdir()
410 (src /
"oc_diagnostics").mkdir()
412 py_file = src /
"main.py"
413 py_file.write_text(textwrap.dedent(
'''\
414 #!/usr/bin/env python3
415 # -*- coding: utf-8 -*-
421 '''), encoding=
"utf-8")
423 json_file = src /
"config.json"
424 json_file.write_text(
'{"debug": true}\n', encoding=
"utf-8")
430 "include_extensions": [
".py",
".json"],
431 "exclude_dirs": set(),
432 "wildcard_exclude_dirs": [],
433 "header_text":
"SKELETON\n",
434 "separator_file":
"--- {num}: {path}\n",
435 "separator_subdir":
"=== {num}: {dirname}\n",
436 "root_header":
"ROOT\n",
437 "max_safe_path_len": 240,
439 out = tmp_path /
"output.txt"
440 count, *_ = write_skeleton_file_for_dir(project, out, project, cfg)
442 content = out.read_text(encoding=
"utf-8")
443 assert "def run():" in content
444 assert "config.json" in content
446 assert 'print("running")' not in content
449 skel_dir = project /
"_skeleton_code-20260408-1500"
451 (skel_dir /
"test.txt").write_text(
"test", encoding=
"utf-8")
453 clean_skeleton_dirs(project)
455 assert not skel_dir.exists()
459 venv = project /
".git"
461 (venv /
"config").write_text(
"secret", encoding=
"utf-8")
463 env_file = project /
".env"
464 env_file.write_text(
"SECRET=abc", encoding=
"utf-8")
467 "include_extensions": [
".py",
".json",
".env"],
468 "exclude_dirs": {
".git"},
469 "wildcard_exclude_dirs": [],
470 "header_text":
"SKELETON\n",
471 "separator_file":
"--- {num}: {path}\n",
472 "separator_subdir":
"=== {num}: {dirname}\n",
473 "root_header":
"ROOT\n",
474 "max_safe_path_len": 240,
476 out = tmp_path /
"output.txt"
477 write_skeleton_file_for_dir(project, out, project, cfg)
478 content = out.read_text(encoding=
"utf-8")
479 assert ".git" not in content
480 assert ".env" not in content
481 assert "SECRET=abc" not in content