feat: add 5 tested self-improvement tools (68/68 tests pass)
This commit is contained in:
199
tests/test_skill_validator.py
Normal file
199
tests/test_skill_validator.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Tests for skill validator module."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from tools.skill_validator import SkillValidator, SkillValidationError
|
||||
|
||||
|
||||
VALID_SKILL = """---
|
||||
name: test-skill
|
||||
description: A valid test skill for validation
|
||||
version: "1.0.0"
|
||||
author: ezra
|
||||
tags: [testing, validation]
|
||||
---
|
||||
|
||||
# Test Skill
|
||||
|
||||
## Trigger
|
||||
Use when testing skill validation.
|
||||
|
||||
## Steps
|
||||
1. First step: do something
|
||||
2. Second step: verify
|
||||
3. Third step: done
|
||||
|
||||
```bash
|
||||
echo "hello world"
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
- Don't forget to test edge cases
|
||||
|
||||
## Verification
|
||||
- Check the output matches expected
|
||||
"""
|
||||
|
||||
MINIMAL_SKILL = """---
|
||||
name: minimal
|
||||
description: Minimal skill
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
## Trigger
|
||||
When needed.
|
||||
|
||||
## Steps
|
||||
1. Do it.
|
||||
2. Done.
|
||||
"""
|
||||
|
||||
BROKEN_SKILL_NO_FM = """# No Frontmatter Skill
|
||||
|
||||
## Steps
|
||||
1. This will fail validation
|
||||
"""
|
||||
|
||||
BROKEN_SKILL_BAD_YAML = """---
|
||||
name: [invalid yaml
|
||||
---
|
||||
|
||||
## Steps
|
||||
1. test
|
||||
"""
|
||||
|
||||
BROKEN_SKILL_MISSING_FIELDS = """---
|
||||
description: Missing name and version
|
||||
---
|
||||
|
||||
## Steps
|
||||
1. test
|
||||
"""
|
||||
|
||||
|
||||
class TestSkillValidationError(unittest.TestCase):
|
||||
def test_repr_error(self):
|
||||
e = SkillValidationError("ERROR", "bad thing", "frontmatter")
|
||||
self.assertIn("❌", repr(e))
|
||||
self.assertIn("bad thing", repr(e))
|
||||
|
||||
def test_repr_warning(self):
|
||||
e = SkillValidationError("WARNING", "maybe bad")
|
||||
self.assertIn("⚠️", repr(e))
|
||||
|
||||
def test_repr_info(self):
|
||||
e = SkillValidationError("INFO", "just fyi")
|
||||
self.assertIn("ℹ️", repr(e))
|
||||
|
||||
|
||||
class TestSkillValidator(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.validator = SkillValidator()
|
||||
self.tmp_dir = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
import shutil
|
||||
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
||||
|
||||
def _write_skill(self, content: str, name: str = "test-skill") -> Path:
|
||||
skill_dir = Path(self.tmp_dir) / name
|
||||
skill_dir.mkdir(parents=True, exist_ok=True)
|
||||
path = skill_dir / "SKILL.md"
|
||||
path.write_text(content)
|
||||
return path
|
||||
|
||||
def test_valid_skill_no_errors(self):
|
||||
path = self._write_skill(VALID_SKILL)
|
||||
errors = self.validator.validate_file(path)
|
||||
error_count = len([e for e in errors if e.level == "ERROR"])
|
||||
self.assertEqual(error_count, 0, f"Unexpected errors: {errors}")
|
||||
|
||||
def test_minimal_skill_warnings_only(self):
|
||||
path = self._write_skill(MINIMAL_SKILL, "minimal")
|
||||
errors = self.validator.validate_file(path)
|
||||
error_count = len([e for e in errors if e.level == "ERROR"])
|
||||
self.assertEqual(error_count, 0)
|
||||
# Should have warnings for missing recommended sections
|
||||
warning_count = len([e for e in errors if e.level == "WARNING"])
|
||||
self.assertGreater(warning_count, 0)
|
||||
|
||||
def test_no_frontmatter_error(self):
|
||||
path = self._write_skill(BROKEN_SKILL_NO_FM, "broken1")
|
||||
errors = self.validator.validate_file(path)
|
||||
fm_errors = [e for e in errors if "frontmatter" in e.field and e.level == "ERROR"]
|
||||
self.assertGreater(len(fm_errors), 0)
|
||||
|
||||
def test_bad_yaml_error(self):
|
||||
path = self._write_skill(BROKEN_SKILL_BAD_YAML, "broken2")
|
||||
errors = self.validator.validate_file(path)
|
||||
yaml_errors = [e for e in errors if "YAML" in e.message or "frontmatter" in e.field]
|
||||
self.assertGreater(len(yaml_errors), 0)
|
||||
|
||||
def test_missing_required_fields(self):
|
||||
path = self._write_skill(BROKEN_SKILL_MISSING_FIELDS, "broken3")
|
||||
errors = self.validator.validate_file(path)
|
||||
missing = [e for e in errors if "Missing required" in e.message]
|
||||
self.assertGreater(len(missing), 0)
|
||||
|
||||
def test_file_not_found(self):
|
||||
errors = self.validator.validate_file(Path("/nonexistent/SKILL.md"))
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0].level, "ERROR")
|
||||
|
||||
def test_empty_file(self):
|
||||
path = self._write_skill("", "empty")
|
||||
errors = self.validator.validate_file(path)
|
||||
self.assertTrue(any(e.message == "File is empty" for e in errors))
|
||||
|
||||
def test_invalid_name_format(self):
|
||||
skill = """---
|
||||
name: BAD NAME!
|
||||
description: test
|
||||
version: "1.0"
|
||||
---
|
||||
## Trigger
|
||||
test
|
||||
## Steps
|
||||
1. test
|
||||
2. done
|
||||
"""
|
||||
path = self._write_skill(skill, "badname")
|
||||
errors = self.validator.validate_file(path)
|
||||
name_errors = [e for e in errors if "Invalid name" in e.message]
|
||||
self.assertGreater(len(name_errors), 0)
|
||||
|
||||
def test_validate_all(self):
|
||||
self._write_skill(VALID_SKILL, "skill-a")
|
||||
self._write_skill(MINIMAL_SKILL, "skill-b")
|
||||
results = self.validator.validate_all(Path(self.tmp_dir))
|
||||
self.assertEqual(len(results), 2)
|
||||
self.assertIn("skill-a", results)
|
||||
self.assertIn("skill-b", results)
|
||||
|
||||
def test_format_report(self):
|
||||
self._write_skill(VALID_SKILL, "good")
|
||||
self._write_skill(BROKEN_SKILL_NO_FM, "bad")
|
||||
results = self.validator.validate_all(Path(self.tmp_dir))
|
||||
report = self.validator.format_report(results)
|
||||
self.assertIn("Skill Validation Report", report)
|
||||
self.assertIn("good", report)
|
||||
self.assertIn("bad", report)
|
||||
|
||||
def test_nonstandard_subdir_warning(self):
|
||||
skill_dir = Path(self.tmp_dir) / "weirdskill"
|
||||
skill_dir.mkdir()
|
||||
(skill_dir / "SKILL.md").write_text(VALID_SKILL)
|
||||
(skill_dir / "random_dir").mkdir()
|
||||
errors = self.validator.validate_file(skill_dir / "SKILL.md")
|
||||
dir_warnings = [e for e in errors if "Non-standard" in e.message]
|
||||
self.assertGreater(len(dir_warnings), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user