Files
skills/tests/test_news.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 367fe56dd4 feat(kvb-org-radio): split radio and post, add kvb-org-update
The radio (call, broadcast, security, pan-pan, mayday) reaches its addressee at the next tool step and wakes it. The post (post with --to, newsletter without, call receipts) arrives through a new Stop hook when the turn ends and never wakes. Legacy rows map onto the two channels and send direct stays as an alias. A PreToolUse hook holds the mayday's radio silence for writing tools and Bash. Only the sender, helpers who join with radio.py assist, bare radio.py calls and the config's allowed commands pass, and any shell operator or parse error blocks. kvb-org-update announces changed skills as one newsletter. Callers moved to send call --to.

🎯 Fix: 90 %
💥 Break: 5 %
💸 Cost: 104 %
⏱️ Time: 100 %
🛡️ Security: 100 %
💰 Value: ?
🏆 Score: 82
🚦 Gate: ⚠️ cost 104 % > 100: ask the operator before the commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:08:10 +00:00

91 lines
4.0 KiB
Python

"""Exercise the news.py shim: it forwards post, hook, mailbox and show to radio.py, and a missing radio.py never blocks a prompt."""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
NEWS = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "news.py"
class TestNews(unittest.TestCase):
def setUp(self):
tmp = tempfile.TemporaryDirectory()
self.addCleanup(tmp.cleanup)
self.tmp = Path(tmp.name)
self.home = self.tmp / "home"
self.root = self.tmp / "council"
sessions = self.home / ".claude" / "sessions"
sessions.mkdir(parents=True)
for sid, name in (("s1", "One"), ("s2", "Two"), ("s3", "Three")):
(sessions / f"{sid}.json").write_text(json.dumps({"sessionId": sid, "name": name}), encoding="utf-8")
def _exec(self, news: Path, sid: str | None, stdin: str, args: tuple[str, ...]) -> subprocess.CompletedProcess:
env = {
"PATH": "/usr/bin:/bin",
"HOME": str(self.home),
"AGENTS_COUNCIL_ROOT": str(self.root),
"AGENTS_COUNCIL_CONFIG": str(self.tmp / "config.json"),
}
if sid:
env["CLAUDE_CODE_SESSION_ID"] = sid
return subprocess.run(
[sys.executable, str(news), *args], input=stdin, capture_output=True, text=True, env=env, check=False
)
def _cli(self, sid: str | None, *args: str) -> subprocess.CompletedProcess:
return self._exec(NEWS, sid, "", args)
def _hook(self, sid: str) -> list[str]:
result = self._exec(NEWS, None, json.dumps({"session_id": sid, "prompt": "x"}), ("hook",))
self.assertEqual(result.returncode, 0, result.stderr)
return [line.split(" ", 3)[3] for line in result.stdout.splitlines()]
def _mailbox(self, sid: str) -> list[str]:
result = self._exec(NEWS, None, json.dumps({"session_id": sid}), ("mailbox",))
self.assertIn(result.returncode, (0, 2), result.stderr)
return [line.split(" ", 3)[3] for line in result.stderr.splitlines()]
def test_every_session_sees_a_post_once_when_its_turn_ends_except_its_author(self):
self.assertEqual(self._cli("s1", "post", "core #1358 merged").stdout, "sent newsletter to all\n")
self.assertEqual(self._hook("s2"), [])
self.assertEqual(self._mailbox("s2"), ["One: core #1358 merged"])
self.assertEqual(self._mailbox("s2"), [])
self.assertEqual(self._mailbox("s3"), ["One: core #1358 merged"])
self.assertEqual(self._mailbox("s1"), [])
def test_addressed_post_reaches_only_its_target(self):
self._cli("s1", "post", "your gate is granted", "--to", "Two")
self.assertEqual(self._mailbox("s3"), [])
self.assertEqual(self._mailbox("s2"), ["One to Two: your gate is granted"])
self.assertEqual(len(self._cli("s1", "show", "--all").stdout.splitlines()), 1)
def test_hook_never_fails_and_post_needs_a_session(self):
self.assertEqual(self._exec(NEWS, None, "not json", ("hook",)).returncode, 0)
self.assertEqual(self._exec(NEWS, None, "{}", ("hook",)).stdout, "")
self.assertEqual(self._cli(None, "post", "x").returncode, 2)
self.assertEqual(self._cli("s1", "post", " ").returncode, 2)
def test_without_radio_the_hook_passes_and_the_rest_fail(self):
lone = self.tmp / "skills" / "kvb-org-organize" / "scripts" / "news.py"
lone.parent.mkdir(parents=True)
shutil.copy(NEWS, lone)
hook = self._exec(lone, None, json.dumps({"session_id": "s2"}), ("hook",))
self.assertEqual((hook.returncode, hook.stdout), (0, ""))
for args in (("post", "x"), ("show",)):
with self.subTest(args=args):
result = self._exec(lone, "s1", "", args)
self.assertEqual(result.returncode, 2)
self.assertIn("kvb-org-radio", result.stderr)
self.assertFalse(self.root.exists())
if __name__ == "__main__":
unittest.main()