Files
skill-seekers-reference/tests/test_bootstrap_skill_e2e.py
yusyus 38e8969ae7 feat: Merge PR #249 - Bootstrap skill with fixes and MCP optionality
Merged PR #249 from @MiaoDX with enhancements:

Bootstrap Feature:
- Self-bootstrap: Generate skill-seekers as Claude Code skill
- Robust frontmatter detection (dynamic line finding)
- SKILL.md validation (YAML + Markdown structure)
- Comprehensive error handling (uv check, permission checks)
- 6 E2E tests with venv isolation

MCP Optionality (User Feature):
- MCP removed from core dependencies
- Optional install: pip install skill-seekers[mcp]
- Lazy loading with helpful error messages
- Interactive setup wizard on first run
- Backward compatible

Bug Fixes:
- Fixed codebase_scraper.py AttributeError (line 1193)
- Fixed test_bootstrap_skill_e2e.py Path vs str issue
- Updated test version expectations to 2.7.0
- Added httpx to core (required for async scraping)
- Added anthropic to core (required for AI enhancement)

Testing:
- 6 new bootstrap E2E tests (all passing)
- 1207/1217 tests passing (99.2% pass rate)
- All bootstrap and enhancement tests pass
- Remaining failures are pre-existing test infrastructure issues

Documentation:
- Updated CHANGELOG.md with v2.7.0 notes
- Updated README.md with bootstrap and installation options
- Added setup wizard guide

Files Modified (9):
- CHANGELOG.md, README.md - Documentation updates
- pyproject.toml - MCP optional, httpx/anthropic core, markers, entry points
- scripts/bootstrap_skill.sh - Dynamic frontmatter, validation, error handling
- src/skill_seekers/cli/install_skill.py - Lazy MCP loading
- tests/test_cli_paths.py - Version 2.7.0
- uv.lock - Dependency updates

New Files (2):
- src/skill_seekers/cli/setup_wizard.py - Interactive installation guide (95 lines)
- tests/test_bootstrap_skill_e2e.py - E2E bootstrap tests (169 lines)

Credits: @MiaoDX for PR #249

Co-Authored-By: MiaoDX <MiaoDX@hotmail.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 20:37:30 +03:00

172 lines
5.7 KiB
Python

"""
End-to-end tests for bootstrap skill feature (PR #249)
Tests verify:
1. Bootstrap script creates proper skill structure
2. Generated SKILL.md is valid and usable
3. Skill is installable in isolated virtual environment
4. Output works with all platform adaptors
5. Error cases handled gracefully
Coverage: 8-12 tests
Execution time: Fast tests ~2-3 min, Full tests ~5-10 min
Requires: Python 3.10+, bash, uv
Run fast tests:
pytest tests/test_bootstrap_skill_e2e.py -v -k "not venv"
Run full suite:
pytest tests/test_bootstrap_skill_e2e.py -v -m "e2e"
Run with venv tests:
pytest tests/test_bootstrap_skill_e2e.py -v -m "venv"
"""
import subprocess
import sys
from pathlib import Path
import pytest
@pytest.fixture
def project_root():
"""Get project root directory."""
return Path(__file__).parent.parent
@pytest.fixture
def run_bootstrap(project_root):
"""Execute bootstrap script and return result"""
def _run(timeout=600):
script = project_root / "scripts" / "bootstrap_skill.sh"
result = subprocess.run(
["bash", str(script)],
cwd=project_root,
capture_output=True,
text=True,
timeout=timeout
)
return result
return _run
@pytest.fixture
def output_skill_dir(project_root):
"""Get path to bootstrap output directory"""
return project_root / "output" / "skill-seekers"
@pytest.mark.e2e
class TestBootstrapSkillE2E:
"""End-to-end tests for bootstrap skill"""
def test_bootstrap_creates_output_structure(self, run_bootstrap, output_skill_dir):
"""Verify bootstrap creates correct directory structure"""
result = run_bootstrap()
assert result.returncode == 0, f"Bootstrap failed: {result.stderr}"
assert output_skill_dir.exists(), "Output directory not created"
assert (output_skill_dir / "SKILL.md").exists(), "SKILL.md not created"
assert (output_skill_dir / "SKILL.md").stat().st_size > 0, "SKILL.md is empty"
def test_bootstrap_prepends_header(self, run_bootstrap, output_skill_dir):
"""Verify header template prepended to SKILL.md"""
result = run_bootstrap()
assert result.returncode == 0
content = (output_skill_dir / "SKILL.md").read_text()
# Check header sections present
assert "## Prerequisites" in content, "Missing Prerequisites section"
assert "pip install skill-seekers" in content, "Missing install instruction"
assert "## Commands" in content, "Missing Commands section"
def test_bootstrap_validates_yaml_frontmatter(self, run_bootstrap, output_skill_dir):
"""Verify generated SKILL.md has valid YAML frontmatter"""
result = run_bootstrap()
assert result.returncode == 0
content = (output_skill_dir / "SKILL.md").read_text()
# Check frontmatter structure
assert content.startswith("---"), "Missing frontmatter start"
# Find closing delimiter
lines = content.split('\n')
closing_found = False
for i, line in enumerate(lines[1:], 1):
if line.strip() == "---":
closing_found = True
break
assert closing_found, "Missing frontmatter closing delimiter"
# Check required fields
assert "name:" in content[:500], "Missing name field"
assert "description:" in content[:500], "Missing description field"
def test_bootstrap_output_line_count(self, run_bootstrap, output_skill_dir):
"""Verify output SKILL.md has reasonable line count"""
result = run_bootstrap()
assert result.returncode == 0
line_count = len((output_skill_dir / "SKILL.md").read_text().splitlines())
# Should be substantial (header ~44 + auto-generated ~200+)
assert line_count > 100, f"SKILL.md too short: {line_count} lines"
assert line_count < 2000, f"SKILL.md suspiciously long: {line_count} lines"
@pytest.mark.slow
@pytest.mark.venv
def test_skill_installable_in_venv(self, run_bootstrap, output_skill_dir, tmp_path):
"""Test skill is installable in clean virtual environment"""
# First run bootstrap
result = run_bootstrap()
assert result.returncode == 0
# Create venv
venv_path = tmp_path / "test_venv"
subprocess.run(
[sys.executable, "-m", "venv", str(venv_path)],
check=True,
timeout=60
)
# Install skill in venv
pip_path = venv_path / "bin" / "pip"
result = subprocess.run(
[str(pip_path), "install", "-e", "."],
cwd=output_skill_dir.parent.parent,
capture_output=True,
text=True,
timeout=120
)
# Should install successfully
assert result.returncode == 0, f"Install failed: {result.stderr}"
def test_skill_packageable_with_adaptors(self, run_bootstrap, output_skill_dir, tmp_path):
"""Verify bootstrap output works with all platform adaptors"""
result = run_bootstrap()
assert result.returncode == 0
# Try to package with claude adaptor (simplest)
from skill_seekers.cli.adaptors import get_adaptor
adaptor = get_adaptor('claude')
# Should be able to package without errors
try:
package_path = adaptor.package(
skill_dir=output_skill_dir, # Path object, not str
output_path=tmp_path # Path object, not str
)
assert Path(package_path).exists(), "Package not created"
assert Path(package_path).stat().st_size > 0, "Package is empty"
except Exception as e:
pytest.fail(f"Packaging failed: {e}")