Files
JWeBot/tests/test_kernel_extension_dispatch.py
T

247 lines
7.8 KiB
Python

#!/usr/bin/env python3
import shutil
import subprocess
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
ExpectedExtension,
HostedExtensionError,
HostedExtensionRuntime,
SignedExtensionInventory,
build_release,
install_release,
sign_release_record,
)
from jwebot_sdk import ExtensionResult, ReplyPlan # noqa: E402
PUBLISHER = "test-publisher"
def publisher_key(root: Path):
private_key = root / "publisher"
result = subprocess.run(
[
"/usr/bin/ssh-keygen",
"-q",
"-t",
"ed25519",
"-N",
"",
"-f",
str(private_key),
],
capture_output=True,
check=False,
)
if result.returncode != 0:
raise RuntimeError("cannot generate test publisher key")
allowed_signers = root / "allowed_signers"
allowed_signers.write_text(
f"{PUBLISHER} "
+ private_key.with_suffix(".pub").read_text(encoding="utf-8"),
encoding="utf-8",
)
return private_key, allowed_signers
def install_example(base: Path, private_key: Path, allowed_signers: Path):
source = base / "source"
shutil.copytree(PROJECT_ROOT / "examples" / "hello-command", source)
artifact, record_path, _ = build_release(source, base / "dist")
signature_path, _ = sign_release_record(
record_path,
private_key,
PUBLISHER,
)
state_root = base / "state"
release_dir = install_release(
artifact,
record_path,
state_root / "hello-command",
signature=signature_path,
allowed_signers=allowed_signers,
expected_publisher=PUBLISHER,
health_check=lambda release: None,
)
return state_root, release_dir
def expected(author: str = "example"):
return ExpectedExtension(
plugin_id="hello-command",
author=author,
commands=("/hello",),
service_profiles=(),
)
@unittest.skipUnless(
Path("/usr/bin/ssh-keygen").is_file(),
"/usr/bin/ssh-keygen is unavailable",
)
class SignedInventoryTests(unittest.TestCase):
def test_signed_inventory_loads_and_freezes_current_release(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
private_key, allowed_signers = publisher_key(base)
state_root, release_dir = install_example(
base,
private_key,
allowed_signers,
)
inventory = SignedExtensionInventory.load(
state_root,
allowed_signers,
[expected()],
expected_publisher=PUBLISHER,
require_all=True,
)
self.assertTrue(inventory.catalog.frozen)
self.assertEqual(
inventory.releases["hello-command"].release_dir,
release_dir,
)
match = inventory.catalog.resolve_command("/hello world")
self.assertEqual(match.plugin_id, "hello-command")
self.assertEqual(match.arguments, "world")
def test_signed_inventory_rejects_expected_author_mismatch(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
private_key, allowed_signers = publisher_key(base)
state_root, _ = install_example(
base,
private_key,
allowed_signers,
)
with self.assertRaisesRegex(
HostedExtensionError,
"inventory mismatch",
):
SignedExtensionInventory.load(
state_root,
allowed_signers,
[expected(author="wrong")],
expected_publisher=PUBLISHER,
)
def test_signed_inventory_can_require_every_release(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
_, allowed_signers = publisher_key(base)
with self.assertRaisesRegex(
HostedExtensionError,
"are missing",
):
SignedExtensionInventory.load(
base / "state",
allowed_signers,
[expected()],
require_all=True,
)
def test_hosted_runtime_health_gates_and_invokes_result(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
private_key, allowed_signers = publisher_key(base)
state_root, _ = install_example(
base,
private_key,
allowed_signers,
)
inventory = SignedExtensionInventory.load(
state_root,
allowed_signers,
[expected()],
expected_publisher=PUBLISHER,
require_all=True,
)
calls = []
def invoke(request, service_handler):
calls.append((dict(request), service_handler))
if request["op"] == "health":
return {
"status": "ok",
"api_version": "1",
"plugin_id": "hello-command",
"author": "example",
"description": "Return a local greeting",
"help": ["/hello [name]"],
"commands": ["/hello"],
"service_profiles": [],
}
return {
"status": "ok",
"api_version": "1",
"plugin_id": "hello-command",
"result": ExtensionResult(
reply=ReplyPlan.text("hosted")
).to_mapping(),
}
runtime = HostedExtensionRuntime(
inventory,
{"hello-command": invoke},
lambda profile, operation, payload: {},
require_all_healthy=True,
)
result = runtime.invoke(
"hello-command",
"/hello",
"world",
{
"event_id": "event-1",
"observed_at": "2026-10-14T10:00:00+08:00",
"sender": "Alice",
"text": "@JWeBot /hello world",
"group_fingerprint": "group-a",
"mention_bot": True,
},
)
self.assertEqual(result.reply.message, "hosted")
self.assertEqual(calls[1][0]["arguments"], "world")
def test_unhealthy_host_is_not_active_in_preferred_mode(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
private_key, allowed_signers = publisher_key(base)
state_root, _ = install_example(
base,
private_key,
allowed_signers,
)
inventory = SignedExtensionInventory.load(
state_root,
allowed_signers,
[expected()],
expected_publisher=PUBLISHER,
)
runtime = HostedExtensionRuntime(
inventory,
{"hello-command": lambda request, service: {"status": "error"}},
lambda profile, operation, payload: {},
)
self.assertFalse(runtime.available("hello-command"))
self.assertIn("hello-command", runtime.failures)
if __name__ == "__main__":
unittest.main()