test_quality_checker.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #!/usr/bin/env python3
  2. """
  3. Tests for cli/quality_checker.py functionality
  4. """
  5. import unittest
  6. import tempfile
  7. from pathlib import Path
  8. import os
  9. from skill_seekers.cli.quality_checker import SkillQualityChecker, QualityReport
  10. class TestQualityChecker(unittest.TestCase):
  11. """Test quality checker functionality"""
  12. def create_test_skill(self, tmpdir, skill_md_content, create_references=True):
  13. """Helper to create a test skill directory"""
  14. skill_dir = Path(tmpdir) / "test-skill"
  15. skill_dir.mkdir()
  16. # Create SKILL.md
  17. skill_md = skill_dir / "SKILL.md"
  18. skill_md.write_text(skill_md_content, encoding='utf-8')
  19. # Create references directory
  20. if create_references:
  21. refs_dir = skill_dir / "references"
  22. refs_dir.mkdir()
  23. (refs_dir / "index.md").write_text("# Index\n\nTest reference.", encoding='utf-8')
  24. (refs_dir / "getting_started.md").write_text("# Getting Started\n\nHow to start.", encoding='utf-8')
  25. return skill_dir
  26. def test_checker_detects_missing_skill_md(self):
  27. """Test that checker detects missing SKILL.md"""
  28. with tempfile.TemporaryDirectory() as tmpdir:
  29. skill_dir = Path(tmpdir) / "test-skill"
  30. skill_dir.mkdir()
  31. checker = SkillQualityChecker(skill_dir)
  32. report = checker.check_all()
  33. # Should have error about missing SKILL.md
  34. self.assertTrue(report.has_errors)
  35. self.assertTrue(any('SKILL.md' in issue.message for issue in report.errors))
  36. def test_checker_detects_missing_references(self):
  37. """Test that checker warns about missing references"""
  38. with tempfile.TemporaryDirectory() as tmpdir:
  39. skill_md = """---
  40. name: test
  41. ---
  42. # Test Skill
  43. This is a test.
  44. """
  45. skill_dir = self.create_test_skill(tmpdir, skill_md, create_references=False)
  46. checker = SkillQualityChecker(skill_dir)
  47. report = checker.check_all()
  48. # Should have warning about missing references
  49. self.assertTrue(report.has_warnings)
  50. self.assertTrue(any('references' in issue.message.lower() for issue in report.warnings))
  51. def test_checker_detects_invalid_frontmatter(self):
  52. """Test that checker detects invalid YAML frontmatter"""
  53. with tempfile.TemporaryDirectory() as tmpdir:
  54. skill_md = """# Test Skill
  55. No frontmatter here!
  56. """
  57. skill_dir = self.create_test_skill(tmpdir, skill_md)
  58. checker = SkillQualityChecker(skill_dir)
  59. report = checker.check_all()
  60. # Should have error about missing frontmatter
  61. self.assertTrue(report.has_errors)
  62. self.assertTrue(any('frontmatter' in issue.message.lower() for issue in report.errors))
  63. def test_checker_detects_missing_name_field(self):
  64. """Test that checker detects missing name field in frontmatter"""
  65. with tempfile.TemporaryDirectory() as tmpdir:
  66. skill_md = """---
  67. description: test
  68. ---
  69. # Test Skill
  70. """
  71. skill_dir = self.create_test_skill(tmpdir, skill_md)
  72. checker = SkillQualityChecker(skill_dir)
  73. report = checker.check_all()
  74. # Should have error about missing name field
  75. self.assertTrue(report.has_errors)
  76. self.assertTrue(any('name' in issue.message.lower() for issue in report.errors))
  77. def test_checker_detects_code_without_language(self):
  78. """Test that checker warns about code blocks without language tags"""
  79. with tempfile.TemporaryDirectory() as tmpdir:
  80. skill_md = """---
  81. name: test
  82. ---
  83. # Test Skill
  84. Here's some code:
  85. ```
  86. print("hello")
  87. ```
  88. """
  89. skill_dir = self.create_test_skill(tmpdir, skill_md)
  90. checker = SkillQualityChecker(skill_dir)
  91. report = checker.check_all()
  92. # Should have warning about code without language
  93. self.assertTrue(report.has_warnings)
  94. self.assertTrue(any('language' in issue.message.lower() for issue in report.warnings))
  95. def test_checker_approves_good_skill(self):
  96. """Test that checker gives high score to well-formed skill"""
  97. with tempfile.TemporaryDirectory() as tmpdir:
  98. skill_md = """---
  99. name: test
  100. description: A test skill
  101. ---
  102. # Test Skill
  103. ## When to Use This Skill
  104. Use this when you need to test.
  105. ## Quick Reference
  106. Here are some examples:
  107. ```python
  108. def hello():
  109. print("hello")
  110. ```
  111. ```javascript
  112. console.log("hello");
  113. ```
  114. ## Example: Basic Usage
  115. This shows how to use it.
  116. ## Reference Files
  117. See the references directory for more:
  118. - [Getting Started](references/getting_started.md)
  119. - [Index](references/index.md)
  120. """
  121. skill_dir = self.create_test_skill(tmpdir, skill_md)
  122. checker = SkillQualityChecker(skill_dir)
  123. report = checker.check_all()
  124. # Should have no errors
  125. self.assertFalse(report.has_errors)
  126. # Quality score should be high
  127. self.assertGreaterEqual(report.quality_score, 80.0)
  128. def test_checker_detects_broken_links(self):
  129. """Test that checker detects broken internal links"""
  130. with tempfile.TemporaryDirectory() as tmpdir:
  131. skill_md = """---
  132. name: test
  133. ---
  134. # Test Skill
  135. See [this file](nonexistent.md) for more info.
  136. """
  137. skill_dir = self.create_test_skill(tmpdir, skill_md)
  138. checker = SkillQualityChecker(skill_dir)
  139. report = checker.check_all()
  140. # Should have warning about broken link
  141. self.assertTrue(report.has_warnings)
  142. self.assertTrue(any('broken link' in issue.message.lower() for issue in report.warnings))
  143. def test_quality_score_calculation(self):
  144. """Test that quality score is calculated correctly"""
  145. with tempfile.TemporaryDirectory() as tmpdir:
  146. report = QualityReport("test", Path(tmpdir))
  147. # Perfect score to start
  148. self.assertEqual(report.quality_score, 100.0)
  149. # Add an error (should deduct 15 points)
  150. report.add_error('test', 'Test error')
  151. self.assertEqual(report.quality_score, 85.0)
  152. # Add a warning (should deduct 5 points)
  153. report.add_warning('test', 'Test warning')
  154. self.assertEqual(report.quality_score, 80.0)
  155. # Add more errors
  156. report.add_error('test', 'Another error')
  157. report.add_error('test', 'Yet another error')
  158. self.assertEqual(report.quality_score, 50.0)
  159. def test_quality_grade_calculation(self):
  160. """Test that quality grades are assigned correctly"""
  161. with tempfile.TemporaryDirectory() as tmpdir:
  162. report = QualityReport("test", Path(tmpdir))
  163. # Grade A (90-100)
  164. self.assertEqual(report.quality_grade, 'A')
  165. # Grade B (80-89)
  166. report.add_error('test', 'Error 1')
  167. self.assertEqual(report.quality_grade, 'B')
  168. # Grade C (70-79)
  169. report.add_warning('test', 'Warning 1')
  170. report.add_warning('test', 'Warning 2')
  171. self.assertEqual(report.quality_grade, 'C')
  172. # Grade D (60-69)
  173. report.add_warning('test', 'Warning 3')
  174. report.add_warning('test', 'Warning 4')
  175. self.assertEqual(report.quality_grade, 'D')
  176. # Grade F (below 60)
  177. report.add_error('test', 'Error 2')
  178. report.add_error('test', 'Error 3')
  179. self.assertEqual(report.quality_grade, 'F')
  180. def test_is_excellent_property(self):
  181. """Test is_excellent property"""
  182. with tempfile.TemporaryDirectory() as tmpdir:
  183. report = QualityReport("test", Path(tmpdir))
  184. # Should be excellent with no issues
  185. self.assertTrue(report.is_excellent)
  186. # Adding an error should make it not excellent
  187. report.add_error('test', 'Test error')
  188. self.assertFalse(report.is_excellent)
  189. # Clean report
  190. report2 = QualityReport("test", Path(tmpdir))
  191. # Adding a warning should also make it not excellent
  192. report2.add_warning('test', 'Test warning')
  193. self.assertFalse(report2.is_excellent)
  194. class TestQualityCheckerCLI(unittest.TestCase):
  195. """Test quality checker CLI"""
  196. def test_cli_help_output(self):
  197. """Test that CLI help works"""
  198. import subprocess
  199. try:
  200. result = subprocess.run(
  201. ['python3', '-m', 'skill_seekers.cli.quality_checker', '--help'],
  202. capture_output=True,
  203. text=True,
  204. timeout=5
  205. )
  206. # Should include usage info
  207. output = result.stdout + result.stderr
  208. self.assertTrue('usage:' in output.lower() or 'quality' in output.lower())
  209. except FileNotFoundError:
  210. self.skipTest("Module not installed")
  211. def test_cli_with_nonexistent_directory(self):
  212. """Test CLI behavior with nonexistent directory"""
  213. import subprocess
  214. result = subprocess.run(
  215. ['python3', '-m', 'skill_seekers.cli.quality_checker', '/nonexistent/path'],
  216. capture_output=True,
  217. text=True
  218. )
  219. # Should fail
  220. self.assertNotEqual(result.returncode, 0)
  221. if __name__ == '__main__':
  222. unittest.main()