#!/usr/bin/env python3 import os import sys import tempfile import unittest from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[1] SDK_SRC = PROJECT_ROOT / "packages" / "jwebot-sdk" / "src" KERNEL_SRC = PROJECT_ROOT / "packages" / "jwebot-kernel" / "src" sys.path[:0] = [str(SDK_SRC), str(KERNEL_SRC)] from jwebot_kernel import ( # noqa: E402 ActivationError, ReleaseError, build_release, install_release, parse_manifest, rollback_release, ) def manifest(): return parse_manifest( { "id": "hello-command", "author": "tester", "description": "Say hello", "help": ["/hello"], "version": "1.0.0", "api_version": "1", "entrypoint": "hello_command:plugin", "type": "command", "capabilities": { "commands": ["/hello"], "storage": False, "network_profiles": [], }, } ) def extension(root: Path, reply: str) -> Path: root.mkdir() (root / "src").mkdir() (root / "plugin.toml").write_text( 'id = "hello-command"\n', encoding="utf-8", ) (root / "src" / "hello_command.py").write_text( "\n".join( ( "from jwebot_sdk import ExtensionResult, ReplyPlan", "", "def handle(event, arguments):", f" return ExtensionResult(reply=ReplyPlan.text({reply!r}))", ) ), encoding="utf-8", ) return root class ActivationTests(unittest.TestCase): def build(self, base: Path, name: str, reply: str): return build_release( extension(base / name, reply), base / f"{name}-dist", manifest=manifest(), ) def test_install_creates_verified_release_and_current_pointer(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, record = self.build(base, "first", "one") release_dir = install_release( artifact, record_path, base / "state", allow_unsigned=True, ) self.assertEqual(release_dir.name, record.source_digest) self.assertEqual( os.readlink(base / "state" / "current"), f"releases/{record.source_digest}", ) self.assertTrue((release_dir / ".release.json").is_file()) self.assertEqual( (release_dir / "src" / "hello_command.py").stat().st_mode & 0o777, 0o600, ) def test_install_rejects_unsigned_release_by_default(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, _ = self.build(base, "first", "one") state = base / "state" with self.assertRaisesRegex( ActivationError, "signed release activation is required", ): install_release(artifact, record_path, state) self.assertFalse(state.exists()) def test_unsigned_local_install_rejects_publisher_policy(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, _ = self.build(base, "first", "one") with self.assertRaisesRegex( ActivationError, "cannot include signature inputs", ): install_release( artifact, record_path, base / "state", expected_publisher="test-publisher", allow_unsigned=True, ) def test_second_install_preserves_previous_and_rollback_swaps(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) first_artifact, first_record_path, first_record = self.build( base, "first", "one", ) second_artifact, second_record_path, second_record = self.build( base, "second", "two", ) state = base / "state" install_release( first_artifact, first_record_path, state, allow_unsigned=True, ) install_release( second_artifact, second_record_path, state, allow_unsigned=True, ) self.assertEqual( os.readlink(state / "previous"), f"releases/{first_record.source_digest}", ) with self.assertRaisesRegex( ActivationError, "signed release rollback is required", ): rollback_release(state) rollback_release(state, allow_unsigned=True) self.assertEqual( os.readlink(state / "current"), f"releases/{first_record.source_digest}", ) self.assertEqual( os.readlink(state / "previous"), f"releases/{second_record.source_digest}", ) def test_tampered_artifact_does_not_create_activation_state(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, _ = self.build(base, "first", "one") artifact.write_bytes(artifact.read_bytes() + b"tampered") state = base / "state" with self.assertRaises(ReleaseError): install_release( artifact, record_path, state, allow_unsigned=True, ) self.assertFalse((state / "current").exists()) def test_existing_release_content_is_reverified(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, _ = self.build(base, "first", "one") state = base / "state" release_dir = install_release( artifact, record_path, state, allow_unsigned=True, ) (release_dir / "src" / "hello_command.py").write_text( "tampered\n", encoding="utf-8", ) with self.assertRaisesRegex(ActivationError, "source digest mismatch"): install_release( artifact, record_path, state, allow_unsigned=True, ) def test_rollback_requires_a_previous_verified_release(self): with tempfile.TemporaryDirectory() as directory: state = Path(directory) / "state" with self.assertRaisesRegex(ActivationError, "no verified previous"): rollback_release(state) def test_unsafe_current_pointer_is_rejected(self): with tempfile.TemporaryDirectory() as directory: base = Path(directory) artifact, record_path, _ = self.build(base, "first", "one") state = base / "state" state.mkdir() (state / "current").symlink_to("/tmp") with self.assertRaisesRegex(ActivationError, "unsafe target"): install_release( artifact, record_path, state, allow_unsigned=True, ) if __name__ == "__main__": unittest.main()