test: add unit tests for 8 untested modules (batch 3) (#191)
* test: add unit tests for 8 untested modules (batch 3)
New test files (143 tests total):
- tools/debug_helpers.py: DebugSession enable/disable, log, save, session info
- tools/skills_guard.py: scan_file, scan_skill, trust levels, install policy, structural checks
- tools/skills_sync.py: manifest read/write, skill discovery, sync logic
- gateway/sticker_cache.py: cache CRUD, sticker injection text builders
- gateway/channel_directory.py: channel resolution, display formatting, session building
- gateway/hooks.py: hook discovery, sync/async emit, wildcard matching
- gateway/mirror.py: session lookup, JSONL append, mirror_to_session
- honcho_integration/client.py: config from env/file, session name resolution, linked workspaces
Also documents a gap in skills_guard: multi-word prompt injection
variants like "ignore all prior instructions" bypass the regex scanner.
* test: strengthen sticker injection tests with exact format assertions
Replace loose "contains" checks with exact output matching for
build_sticker_injection and build_animated_sticker_injection.
Add edge cases: set_name without emoji, empty description, empty emoji.
* test: remove skills_guard gap-documenting test to avoid conflict with fix PR
2026-03-01 16:28:12 +03:00
|
|
|
"""Tests for tools/debug_helpers.py — DebugSession class."""
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
|
|
from tools.debug_helpers import DebugSession
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestDebugSessionDisabled:
|
|
|
|
|
"""When the env var is not set, DebugSession should be a cheap no-op."""
|
|
|
|
|
|
|
|
|
|
def test_not_active_by_default(self):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="FAKE_DEBUG_VAR_XYZ")
|
|
|
|
|
assert ds.active is False
|
|
|
|
|
assert ds.enabled is False
|
|
|
|
|
|
|
|
|
|
def test_session_id_empty_when_disabled(self):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="FAKE_DEBUG_VAR_XYZ")
|
|
|
|
|
assert ds.session_id == ""
|
|
|
|
|
|
|
|
|
|
def test_log_call_noop(self):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="FAKE_DEBUG_VAR_XYZ")
|
|
|
|
|
ds.log_call("search", {"query": "hello"})
|
|
|
|
|
assert ds._calls == []
|
|
|
|
|
|
|
|
|
|
def test_save_noop(self, tmp_path):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="FAKE_DEBUG_VAR_XYZ")
|
2026-03-02 04:34:21 -08:00
|
|
|
log_dir = tmp_path / "debug_logs"
|
|
|
|
|
log_dir.mkdir()
|
|
|
|
|
ds.log_dir = log_dir
|
test: add unit tests for 8 untested modules (batch 3) (#191)
* test: add unit tests for 8 untested modules (batch 3)
New test files (143 tests total):
- tools/debug_helpers.py: DebugSession enable/disable, log, save, session info
- tools/skills_guard.py: scan_file, scan_skill, trust levels, install policy, structural checks
- tools/skills_sync.py: manifest read/write, skill discovery, sync logic
- gateway/sticker_cache.py: cache CRUD, sticker injection text builders
- gateway/channel_directory.py: channel resolution, display formatting, session building
- gateway/hooks.py: hook discovery, sync/async emit, wildcard matching
- gateway/mirror.py: session lookup, JSONL append, mirror_to_session
- honcho_integration/client.py: config from env/file, session name resolution, linked workspaces
Also documents a gap in skills_guard: multi-word prompt injection
variants like "ignore all prior instructions" bypass the regex scanner.
* test: strengthen sticker injection tests with exact format assertions
Replace loose "contains" checks with exact output matching for
build_sticker_injection and build_animated_sticker_injection.
Add edge cases: set_name without emoji, empty description, empty emoji.
* test: remove skills_guard gap-documenting test to avoid conflict with fix PR
2026-03-01 16:28:12 +03:00
|
|
|
ds.save()
|
2026-03-02 04:34:21 -08:00
|
|
|
assert list(log_dir.iterdir()) == []
|
test: add unit tests for 8 untested modules (batch 3) (#191)
* test: add unit tests for 8 untested modules (batch 3)
New test files (143 tests total):
- tools/debug_helpers.py: DebugSession enable/disable, log, save, session info
- tools/skills_guard.py: scan_file, scan_skill, trust levels, install policy, structural checks
- tools/skills_sync.py: manifest read/write, skill discovery, sync logic
- gateway/sticker_cache.py: cache CRUD, sticker injection text builders
- gateway/channel_directory.py: channel resolution, display formatting, session building
- gateway/hooks.py: hook discovery, sync/async emit, wildcard matching
- gateway/mirror.py: session lookup, JSONL append, mirror_to_session
- honcho_integration/client.py: config from env/file, session name resolution, linked workspaces
Also documents a gap in skills_guard: multi-word prompt injection
variants like "ignore all prior instructions" bypass the regex scanner.
* test: strengthen sticker injection tests with exact format assertions
Replace loose "contains" checks with exact output matching for
build_sticker_injection and build_animated_sticker_injection.
Add edge cases: set_name without emoji, empty description, empty emoji.
* test: remove skills_guard gap-documenting test to avoid conflict with fix PR
2026-03-01 16:28:12 +03:00
|
|
|
|
|
|
|
|
def test_get_session_info_disabled(self):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="FAKE_DEBUG_VAR_XYZ")
|
|
|
|
|
info = ds.get_session_info()
|
|
|
|
|
assert info["enabled"] is False
|
|
|
|
|
assert info["session_id"] is None
|
|
|
|
|
assert info["log_path"] is None
|
|
|
|
|
assert info["total_calls"] == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestDebugSessionEnabled:
|
|
|
|
|
"""When the env var is set to 'true', DebugSession records and saves."""
|
|
|
|
|
|
|
|
|
|
def _make_enabled(self, tmp_path):
|
|
|
|
|
with patch.dict(os.environ, {"TEST_DEBUG": "true"}):
|
|
|
|
|
ds = DebugSession("test_tool", env_var="TEST_DEBUG")
|
|
|
|
|
ds.log_dir = tmp_path
|
|
|
|
|
return ds
|
|
|
|
|
|
|
|
|
|
def test_active_when_env_set(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
assert ds.active is True
|
|
|
|
|
assert ds.enabled is True
|
|
|
|
|
|
|
|
|
|
def test_session_id_generated(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
assert len(ds.session_id) > 0
|
|
|
|
|
|
|
|
|
|
def test_log_call_appends(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
ds.log_call("search", {"query": "hello"})
|
|
|
|
|
ds.log_call("extract", {"url": "http://x.com"})
|
|
|
|
|
assert len(ds._calls) == 2
|
|
|
|
|
assert ds._calls[0]["tool_name"] == "search"
|
|
|
|
|
assert ds._calls[0]["query"] == "hello"
|
|
|
|
|
assert "timestamp" in ds._calls[0]
|
|
|
|
|
|
|
|
|
|
def test_save_creates_json_file(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
ds.log_call("search", {"query": "test"})
|
|
|
|
|
ds.save()
|
|
|
|
|
|
|
|
|
|
files = list(tmp_path.glob("*.json"))
|
|
|
|
|
assert len(files) == 1
|
|
|
|
|
assert "test_tool_debug_" in files[0].name
|
|
|
|
|
|
|
|
|
|
data = json.loads(files[0].read_text())
|
|
|
|
|
assert data["session_id"] == ds.session_id
|
|
|
|
|
assert data["debug_enabled"] is True
|
|
|
|
|
assert data["total_calls"] == 1
|
|
|
|
|
assert data["tool_calls"][0]["tool_name"] == "search"
|
|
|
|
|
|
|
|
|
|
def test_get_session_info_enabled(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
ds.log_call("a", {})
|
|
|
|
|
ds.log_call("b", {})
|
|
|
|
|
info = ds.get_session_info()
|
|
|
|
|
assert info["enabled"] is True
|
|
|
|
|
assert info["session_id"] == ds.session_id
|
|
|
|
|
assert info["total_calls"] == 2
|
|
|
|
|
assert "test_tool_debug_" in info["log_path"]
|
|
|
|
|
|
|
|
|
|
def test_env_var_case_insensitive(self, tmp_path):
|
|
|
|
|
with patch.dict(os.environ, {"TEST_DEBUG": "True"}):
|
|
|
|
|
ds = DebugSession("t", env_var="TEST_DEBUG")
|
|
|
|
|
assert ds.enabled is True
|
|
|
|
|
|
|
|
|
|
with patch.dict(os.environ, {"TEST_DEBUG": "TRUE"}):
|
|
|
|
|
ds = DebugSession("t", env_var="TEST_DEBUG")
|
|
|
|
|
assert ds.enabled is True
|
|
|
|
|
|
|
|
|
|
def test_env_var_false_disables(self):
|
|
|
|
|
with patch.dict(os.environ, {"TEST_DEBUG": "false"}):
|
|
|
|
|
ds = DebugSession("t", env_var="TEST_DEBUG")
|
|
|
|
|
assert ds.enabled is False
|
|
|
|
|
|
|
|
|
|
def test_save_empty_log(self, tmp_path):
|
|
|
|
|
ds = self._make_enabled(tmp_path)
|
|
|
|
|
ds.save()
|
|
|
|
|
files = list(tmp_path.glob("*.json"))
|
|
|
|
|
assert len(files) == 1
|
|
|
|
|
data = json.loads(files[0].read_text())
|
|
|
|
|
assert data["total_calls"] == 0
|
|
|
|
|
assert data["tool_calls"] == []
|