Merge branch 'development' into ruff-and-mypy

This commit is contained in:
Pablo Nicolás Estevez
2026-01-17 17:41:55 +00:00
committed by GitHub
12 changed files with 742 additions and 19 deletions

View File

@@ -0,0 +1,83 @@
"""Tests for the bootstrap skill script."""
import subprocess
import pytest
from pathlib import Path
@pytest.fixture
def project_root():
"""Get project root directory."""
return Path(__file__).parent.parent
class TestBootstrapSkillScript:
"""Tests for scripts/bootstrap_skill.sh"""
def test_script_exists(self, project_root):
"""Test that bootstrap script exists and is executable."""
script = project_root / "scripts" / "bootstrap_skill.sh"
assert script.exists(), "bootstrap_skill.sh should exist"
assert script.stat().st_mode & 0o111, "bootstrap_skill.sh should be executable"
def test_header_template_exists(self, project_root):
"""Test that skill header template exists."""
header = project_root / "scripts" / "skill_header.md"
assert header.exists(), "skill_header.md should exist"
def test_header_has_required_sections(self, project_root):
"""Test that header template has required operational sections."""
header = project_root / "scripts" / "skill_header.md"
content = header.read_text()
# Must have prerequisites
assert "## Prerequisites" in content, "Header must have Prerequisites section"
assert "pip install skill-seekers" in content, "Header must have pip install instruction"
# Must have commands table
assert "## Commands" in content, "Header must have Commands section"
assert "skill-seekers-codebase" in content, "Header must mention codebase command"
assert "skill-seekers scrape" in content, "Header must mention scrape command"
assert "skill-seekers github" in content, "Header must mention github command"
def test_header_has_yaml_frontmatter(self, project_root):
"""Test that header has valid YAML frontmatter."""
header = project_root / "scripts" / "skill_header.md"
content = header.read_text()
assert content.startswith("---"), "Header must start with YAML frontmatter"
assert "name: skill-seekers" in content, "Header must have skill name"
assert "description:" in content, "Header must have description"
@pytest.mark.slow
def test_bootstrap_script_runs(self, project_root, tmp_path):
"""Test that bootstrap script runs successfully.
Note: This test is slow as it runs full codebase analysis.
Run with: pytest -m slow
"""
script = project_root / "scripts" / "bootstrap_skill.sh"
# Run script (skip if uv not available)
result = subprocess.run(
["bash", str(script)],
cwd=project_root,
capture_output=True,
text=True,
timeout=600, # 10 minute timeout
)
# Check script completed
assert result.returncode == 0, f"Script failed: {result.stderr}"
# Check outputs exist (directory named 'skill-seekers' for Claude Code)
output_dir = project_root / "output" / "skill-seekers"
assert output_dir.exists(), "Output directory should be created"
skill_md = output_dir / "SKILL.md"
assert skill_md.exists(), "SKILL.md should be created"
# Check SKILL.md has header prepended
content = skill_md.read_text()
assert "## Prerequisites" in content, "SKILL.md should have header prepended"
assert "pip install skill-seekers" in content, "SKILL.md should have install instructions"

View File

@@ -0,0 +1,171 @@
"""
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}")

View File

@@ -114,7 +114,7 @@ class TestUnifiedCLIEntryPoints(unittest.TestCase):
# Should show version
output = result.stdout + result.stderr
self.assertIn("2.5.1", output)
self.assertIn('2.7.0', output)
except FileNotFoundError:
# If skill-seekers is not installed, skip this test