feat: refresh shared skills catalog
This commit is contained in:
1 parent
98bb35e6fb
commit
418792d1ec
49 files changed
+2273
-2390
No files matched your search
@@ -0,0 +1,180 @@
|
||||
"""Behavioral checks for sync ownership, read-only modes, and delivery commands."""
|
||||
from pathlib import Path
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
loader = importlib.machinery.SourceFileLoader('catalog_validator', str(REPO / 'bin/validate-skills'))
|
||||
spec = importlib.util.spec_from_loader(loader.name, loader)
|
||||
validator = importlib.util.module_from_spec(spec)
|
||||
loader.exec_module(validator)
|
||||
|
||||
|
||||
class ToolTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory(prefix='global-skills-test-')
|
||||
self.addCleanup(self.temp.cleanup)
|
||||
self.root = Path(self.temp.name)
|
||||
self.global_dir = self.root / 'global'
|
||||
self.jev_dir = self.root / 'jev'
|
||||
self.target = self.root / 'target'
|
||||
self.global_dir.mkdir()
|
||||
self.jev_dir.mkdir()
|
||||
|
||||
def skill(self, root, name):
|
||||
path = root / name
|
||||
path.mkdir()
|
||||
(path / 'SKILL.md').write_text(f'---\nname: {name}\ndescription: fixture\nscope: global\n---\n')
|
||||
return path
|
||||
|
||||
def sync(self, *flags):
|
||||
return subprocess.run(['bash', str(REPO / 'sync-skills.sh'), '--global-dir', str(self.global_dir),
|
||||
'--jev-dir', str(self.jev_dir), '--target', str(self.target), *flags],
|
||||
text=True, capture_output=True)
|
||||
|
||||
def test_sync_rejects_target_overlapping_catalog(self):
|
||||
source = self.skill(self.global_dir, 'example')
|
||||
self.target = self.global_dir
|
||||
self.assertEqual(self.sync().returncode, 2)
|
||||
self.assertTrue((source / 'SKILL.md').is_file())
|
||||
|
||||
def test_metadata_rejects_invalid_yaml_scalar(self):
|
||||
for field in ['name: "unterminated', 'description: bad: scalar']:
|
||||
with self.assertRaises(ValueError):
|
||||
validator.metadata('---\n' + field + '\n---\n')
|
||||
|
||||
def test_install_and_idempotence(self):
|
||||
source = self.skill(self.global_dir, 'example')
|
||||
self.assertEqual(self.sync().returncode, 0)
|
||||
self.assertEqual((self.target / 'example').resolve(), source)
|
||||
self.assertIn('0 changes', self.sync().stdout)
|
||||
self.assertEqual(self.sync('--check').returncode, 0)
|
||||
|
||||
def test_external_symlink_collision_preserves_user_link(self):
|
||||
self.skill(self.global_dir, 'example')
|
||||
self.target.mkdir()
|
||||
external = self.root / 'user-skill'
|
||||
external.mkdir()
|
||||
dest = self.target / 'example'
|
||||
dest.symlink_to(external)
|
||||
self.assertEqual(self.sync().returncode, 1)
|
||||
self.assertEqual(dest.resolve(), external)
|
||||
|
||||
def test_dangling_external_symlink_is_preserved(self):
|
||||
self.skill(self.global_dir, 'example')
|
||||
self.target.mkdir()
|
||||
dest = self.target / 'example'
|
||||
dest.symlink_to(self.root / 'missing-user-skill')
|
||||
self.assertEqual(self.sync().returncode, 1)
|
||||
self.assertTrue(dest.is_symlink())
|
||||
self.assertEqual(dest.readlink(), self.root / 'missing-user-skill')
|
||||
|
||||
def test_unrelated_directory_is_preserved(self):
|
||||
self.skill(self.global_dir, 'example')
|
||||
(self.target / 'example').mkdir(parents=True)
|
||||
file = self.target / 'example' / 'user.txt'
|
||||
file.write_text('user work')
|
||||
self.assertEqual(self.sync().returncode, 1)
|
||||
self.assertEqual(file.read_text(), 'user work')
|
||||
|
||||
def test_deleted_skill_link_is_pruned(self):
|
||||
source = self.skill(self.global_dir, 'example')
|
||||
self.sync()
|
||||
(source / 'SKILL.md').unlink()
|
||||
source.rmdir()
|
||||
self.assertEqual(self.sync().returncode, 0)
|
||||
self.assertFalse((self.target / 'example').is_symlink())
|
||||
|
||||
def test_relative_managed_link_is_pruned(self):
|
||||
self.target.mkdir()
|
||||
dest = self.target / 'gone'
|
||||
dest.symlink_to('../global/gone')
|
||||
self.assertEqual(self.sync().returncode, 0)
|
||||
self.assertFalse(dest.is_symlink())
|
||||
|
||||
def test_move_between_managed_sources(self):
|
||||
old = self.skill(self.jev_dir, 'example')
|
||||
self.sync()
|
||||
(old / 'SKILL.md').unlink()
|
||||
old.rmdir()
|
||||
new = self.skill(self.global_dir, 'example')
|
||||
self.assertEqual(self.sync().returncode, 0)
|
||||
self.assertEqual((self.target / 'example').resolve(), new)
|
||||
|
||||
def test_unavailable_jev_does_not_prune_its_links(self):
|
||||
self.target.mkdir()
|
||||
dest = self.target / 'example'
|
||||
dest.symlink_to(self.jev_dir / 'example')
|
||||
self.jev_dir.rmdir()
|
||||
self.assertEqual(self.sync().returncode, 0)
|
||||
self.assertTrue(dest.is_symlink())
|
||||
|
||||
def test_dry_run_and_check_do_not_create_targets(self):
|
||||
self.skill(self.global_dir, 'example')
|
||||
self.assertEqual(self.sync('--dry-run').returncode, 0)
|
||||
self.assertFalse(self.target.exists())
|
||||
self.assertEqual(self.sync('--check').returncode, 1)
|
||||
self.assertFalse(self.target.exists())
|
||||
|
||||
def test_read_only_modes_do_not_remove_stale_links(self):
|
||||
self.target.mkdir()
|
||||
dest = self.target / 'gone'
|
||||
dest.symlink_to(self.global_dir / 'gone')
|
||||
self.assertEqual(self.sync('--check').returncode, 1)
|
||||
self.assertEqual(self.sync('--dry-run').returncode, 0)
|
||||
self.assertTrue(dest.is_symlink())
|
||||
|
||||
def test_source_collision_fails_before_writing(self):
|
||||
self.skill(self.global_dir, 'example')
|
||||
self.skill(self.jev_dir, 'example')
|
||||
self.assertEqual(self.sync().returncode, 2)
|
||||
self.assertFalse(self.target.exists())
|
||||
|
||||
def test_invalid_source_fails_before_writing(self):
|
||||
(self.global_dir / 'empty').mkdir()
|
||||
self.assertEqual(self.sync().returncode, 2)
|
||||
self.assertFalse(self.target.exists())
|
||||
|
||||
def test_file_and_directory_delivery_support_spaces(self):
|
||||
file = self.root / 'report with spaces.md'
|
||||
file.write_text('report')
|
||||
result = subprocess.run(['bash', str(REPO / 'bin/t3-deliver'), str(file)], text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertEqual(result.stdout.splitlines()[-1], f'scp "vasily@msi.tail0b014a.ts.net:{file}" .')
|
||||
result = subprocess.run(['bash', str(REPO / 'bin/t3-deliver'), str(self.root)], text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertEqual(result.stdout.splitlines()[-1], f'scp -r "vasily@msi.tail0b014a.ts.net:{self.root}" .')
|
||||
|
||||
def test_delivery_rejects_shell_expansion_in_names(self):
|
||||
for name in ['cash$report', 'percent%report', 'tick`report', 'quote"report', 'bang!report', 'newline\nreport']:
|
||||
path = self.root / name
|
||||
path.write_text('fixture')
|
||||
result = subprocess.run(['bash', str(REPO / 'bin/t3-deliver'), str(path)], text=True, capture_output=True)
|
||||
self.assertNotEqual(result.returncode, 0, name)
|
||||
self.assertNotIn('scp ', result.stdout)
|
||||
|
||||
def test_validator_reports_missing_links_and_collision(self):
|
||||
source = self.skill(self.global_dir, 'example')
|
||||
self.skill(self.jev_dir, 'example')
|
||||
(source / 'SKILL.md').write_text((source / 'SKILL.md').read_text() + '\n[missing](no-such-file.md)\n')
|
||||
result = subprocess.run(['python3', str(REPO / 'bin/validate-skills'), '--global-dir', str(self.global_dir),
|
||||
'--jev-dir', str(self.jev_dir)], text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 1)
|
||||
self.assertIn('collides', result.stderr)
|
||||
self.assertIn('missing link target', result.stderr)
|
||||
|
||||
def test_metadata_rejects_duplicate_keys(self):
|
||||
with self.assertRaises(ValueError):
|
||||
validator.metadata('---\nname: example\nname: other\n---\n')
|
||||
|
||||
def test_validator_ignores_example_links_inside_fences(self):
|
||||
path = self.root / 'reference.md'
|
||||
path.write_text('```markdown\n[example](not-a-real-file.md)\n```\n')
|
||||
self.assertEqual(list(validator.local_links(path)), [])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user