| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- """Fresh-install and preservation tests using the integrity-checked official artifact."""
- import argparse
- import importlib.util
- import io
- import json
- from pathlib import Path
- import tarfile
- import tempfile
- import unittest
- from unittest.mock import patch
- ROOT = Path(__file__).resolve().parents[1]
- RELEASE = ROOT / 'docs/workshop-onboarding-20261109'
- spec = importlib.util.spec_from_file_location('ksf_onboarding', ROOT / 'scripts/workshop-onboarding/install.py')
- installer = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(installer)
- class OnboardingTests(unittest.TestCase):
- def setUp(self):
- self.temp = tempfile.TemporaryDirectory(prefix='ksf-onboarding-test-')
- self.addCleanup(self.temp.cleanup)
- self.root = Path(self.temp.name)
- self.skills = self.root / 'host-skills'
- self.args = argparse.Namespace(skills_dir=str(self.skills), out=str(self.root / 'result'),
- voc_archive=str(RELEASE / 'source-audit/voc-skill-0.3.42.tgz'), apply=False, prepare_runtime=False)
- def run_install(self):
- return installer.run(self.args, RELEASE / 'package')
- def test_dry_checks_both_complete_sources_without_creating_targets(self):
- result = self.run_install()
- self.assertTrue(result['readOnly'])
- self.assertEqual([a['files'] for a in result['actions']], [43, 169])
- self.assertFalse(self.skills.exists())
- self.assertFalse(Path(self.args.out).exists())
- def test_fresh_install_complete_and_repeat_is_verify_only(self):
- self.args.apply = True
- result = self.run_install()
- self.assertTrue(result['filesInstalled'])
- self.assertEqual(result['hostSkillsLoaded'], 'not-verified')
- self.assertEqual(result['vocLiveAccess'], 'not-verified')
- config = json.loads((self.skills / 'voc-intelligence/.mcp.json').read_bytes())
- self.assertEqual(config['mcpServers']['voc']['args'],
- [str(self.skills / 'voc-intelligence/mcp/src/server.js')])
- marker = self.skills / 'ksf-workshop-runtime/credential-binding.json'
- marker.write_text('fixture-only-do-not-copy')
- before = (self.skills / 'voc-intelligence/package.json').read_bytes()
- self.args.apply = False
- repeated = self.run_install()
- self.assertTrue(all(a['action'] == 'verify-only' for a in repeated['actions']))
- self.assertEqual(marker.read_text(), 'fixture-only-do-not-copy')
- self.assertEqual((self.skills / 'voc-intelligence/package.json').read_bytes(), before)
- self.assertEqual(len(list(self.skills.glob('.ksf-init-*'))), 0)
- def test_existing_different_skill_blocks_before_other_skill_or_receipt_created(self):
- target = self.skills / 'ksf-workshop-runtime'
- target.mkdir(parents=True)
- (target / 'SKILL.md').write_text('unrecognized-existing-skill')
- self.args.apply = True
- with self.assertRaisesRegex(installer.InstallError, 'EXISTING_SKILL_DIFFERS'):
- self.run_install()
- self.assertEqual((target / 'SKILL.md').read_text(), 'unrecognized-existing-skill')
- self.assertFalse((self.skills / 'voc-intelligence').exists())
- self.assertFalse(Path(self.args.out).exists())
- def test_corrupt_archive_stops_before_writes(self):
- archive = self.root / 'bad.tgz'
- archive.write_bytes(b'bad official artifact')
- self.args.voc_archive = str(archive)
- self.args.apply = True
- with self.assertRaisesRegex(installer.InstallError, 'VOC_ARCHIVE_HASH_MISMATCH'):
- self.run_install()
- self.assertFalse(self.skills.exists())
- def test_archive_traversal_and_symlinks_rejected(self):
- for name, kind in [('package/../outside', tarfile.REGTYPE), ('package/link', tarfile.SYMTYPE)]:
- buffer = io.BytesIO()
- with tarfile.open(fileobj=buffer, mode='w:gz') as tar:
- info = tarfile.TarInfo(name)
- info.type = kind
- info.linkname = '../outside' if kind == tarfile.SYMTYPE else ''
- tar.addfile(info, io.BytesIO(b''))
- raw = buffer.getvalue()
- expected = {'bytes': len(raw), 'sha256': installer.digest(raw), 'files': {}}
- with self.assertRaises(installer.InstallError):
- installer.voc_archive_files(raw, expected)
- def test_failure_committing_second_skill_restores_first_and_preserves_unrelated(self):
- self.skills.mkdir()
- unrelated = self.skills / 'another-skill'
- unrelated.mkdir()
- (unrelated / 'SKILL.md').write_text('keep me')
- original = Path.rename
- def rename(source, dest):
- if source.name == 'voc-intelligence':
- raise OSError('synthetic failure')
- return original(source, dest)
- self.args.apply = True
- with patch.object(Path, 'rename', rename), self.assertRaises(OSError):
- self.run_install()
- self.assertFalse((self.skills / 'ksf-workshop-runtime').exists())
- self.assertFalse((self.skills / 'voc-intelligence').exists())
- self.assertEqual((unrelated / 'SKILL.md').read_text(), 'keep me')
- self.assertEqual(len(list(self.skills.glob('.ksf-init-*'))), 0)
- receipt = json.loads((Path(self.args.out) / 'install-receipt.json').read_bytes())
- self.assertFalse(receipt['ok'])
- def test_missing_node_does_not_claim_runtime_ready(self):
- self.args.apply = self.args.prepare_runtime = True
- with patch.object(installer, 'node_and_npm', return_value=None):
- result = self.run_install()
- self.assertTrue(result['filesInstalled'])
- self.assertEqual(result['vocRuntime'], {'status': 'pending', 'reason': 'NODE_OR_NPM_NOT_AVAILABLE'})
- self.assertEqual(result['businessSaveAccess'], 'not-verified')
- def test_result_directory_reuse_and_target_overlap_stop(self):
- self.args.apply = True
- Path(self.args.out).mkdir()
- with self.assertRaisesRegex(installer.InstallError, 'RESULT_DIRECTORY_EXISTS'):
- self.run_install()
- self.assertFalse(self.skills.exists())
- self.args.out = str(self.skills / 'results')
- with self.assertRaisesRegex(installer.InstallError, 'RESULT_TARGET_OVERLAP'):
- self.run_install()
- if __name__ == '__main__':
- unittest.main()
|