Files
javis_bot/tests/test_set_broadcast.py
javis-bot ca86390407 feat: couple broadcast to voice + voice-controlled broadcast toggle
Completes the STREAM_BROWSER=true behaviour:
- handleJoin auto-starts the broadcast on voice join and wires the session to
  the guild streamer; each turn reports the live state to the brain so search
  routes Chrome (live) vs Gemini (off).
- New setBroadcast tool lets the user toggle the broadcast by voice ("방송
  켜줘/꺼줘") via the LLM (no hardcoded phrases); it refuses when
  STREAM_BROWSER=false. The directive flows brain -> bridge (broadcast_action)
  -> bot streamer.start/stop, guarded by isActive() so it's idempotent.
- Per-turn IPC uses a thread-local (reply/turn_state.py) instead of threading
  params through the whole engine chain: bridge sets broadcasting in, tool
  records the directive out; Tool.execute exposes broadcasting on ToolContext.

Bot typecheck clean; brain covered by tests/test_set_broadcast.py (+ existing
routing tests). Specs + docs updated.

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

71 lines
2.3 KiB
Python

"""Behaviour tests for the setBroadcast tool + per-turn directive plumbing."""
from types import SimpleNamespace
from jarvis.reply import turn_state
from jarvis.tools.base import ToolContext
from jarvis.tools.builtin.set_broadcast import SetBroadcastTool
def _ctx(stream_browser):
cfg = SimpleNamespace(stream_browser=stream_browser, voice_debug=True)
return ToolContext(
db=None, cfg=cfg, system_prompt="", original_prompt="",
redacted_text="", max_retries=0, user_print=lambda *a, **k: None,
)
def setup_function():
turn_state.reset()
def test_start_records_directive_in_true_mode():
res = SetBroadcastTool().run({"action": "start"}, _ctx(True))
assert res.success is True
assert turn_state.get_broadcast_action() == "start"
def test_stop_records_directive_in_true_mode():
res = SetBroadcastTool().run({"action": "stop"}, _ctx(True))
assert res.success is True
assert turn_state.get_broadcast_action() == "stop"
def test_false_mode_refuses_and_records_nothing():
res = SetBroadcastTool().run({"action": "start"}, _ctx(False))
assert res.success is False
assert turn_state.get_broadcast_action() is None
def test_invalid_action_records_nothing():
res = SetBroadcastTool().run({"action": "sideways"}, _ctx(True))
assert res.success is False
assert turn_state.get_broadcast_action() is None
def test_reset_clears_directive():
SetBroadcastTool().run({"action": "start"}, _ctx(True))
assert turn_state.get_broadcast_action() == "start"
turn_state.reset()
assert turn_state.get_broadcast_action() is None
def test_execute_threads_broadcasting_from_turn_state_into_context():
# Tool.execute should read the per-turn broadcasting flag and expose it on
# the ToolContext so webSearch routing can see the live broadcast state.
captured = {}
class _Probe(SetBroadcastTool):
def run(self, args, context):
captured["broadcasting"] = context.broadcasting
return super().run(args, context)
turn_state.reset()
turn_state.set_broadcasting(True)
_Probe().execute(
db=None, cfg=SimpleNamespace(stream_browser=True, voice_debug=True),
tool_args={"action": "stop"}, system_prompt="", original_prompt="",
redacted_text="", max_retries=0, user_print=lambda *a, **k: None,
)
assert captured["broadcasting"] is True