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 gateway/channel_directory.py — channel resolution and display."""
|
|
|
|
|
|
|
|
|
|
import json
|
fix(cli): respect HERMES_HOME in all remaining hardcoded ~/.hermes paths
Several files resolved paths via Path.home() / ".hermes" or
os.path.expanduser("~/.hermes/..."), bypassing the HERMES_HOME
environment variable. This broke isolation when running multiple
Hermes instances with distinct HERMES_HOME directories.
Replace all hardcoded paths with calls to get_hermes_home() from
hermes_cli.config, consistent with the rest of the codebase.
Files fixed:
- tools/process_registry.py (processes.json)
- gateway/pairing.py (pairing/)
- gateway/sticker_cache.py (sticker_cache.json)
- gateway/channel_directory.py (channel_directory.json, sessions.json)
- gateway/config.py (gateway.json, config.yaml, sessions_dir)
- gateway/mirror.py (sessions/)
- gateway/hooks.py (hooks/)
- gateway/platforms/base.py (image_cache/, audio_cache/, document_cache/)
- gateway/platforms/whatsapp.py (whatsapp/session)
- gateway/delivery.py (cron/output)
- agent/auxiliary_client.py (auth.json)
- agent/prompt_builder.py (SOUL.md)
- cli.py (config.yaml, images/, pastes/, history)
- run_agent.py (logs/)
- tools/environments/base.py (sandboxes/)
- tools/environments/modal.py (modal_snapshots.json)
- tools/environments/singularity.py (singularity_snapshots.json)
- tools/tts_tool.py (audio_cache)
- hermes_cli/status.py (cron/jobs.json, sessions.json)
- hermes_cli/gateway.py (logs/, whatsapp session)
- hermes_cli/main.py (whatsapp/session)
Tests updated to use HERMES_HOME env var instead of patching Path.home().
Closes #892
(cherry picked from commit 78ac1bba43b8b74a934c6172f2c29bb4d03164b9)
2026-03-11 07:31:41 +01:00
|
|
|
import os
|
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
|
|
|
from pathlib import Path
|
|
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
|
|
from gateway.channel_directory import (
|
|
|
|
|
resolve_channel_name,
|
|
|
|
|
format_directory_for_display,
|
|
|
|
|
load_directory,
|
|
|
|
|
_build_from_sessions,
|
|
|
|
|
DIRECTORY_PATH,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _write_directory(tmp_path, platforms):
|
|
|
|
|
"""Helper to write a fake channel directory."""
|
|
|
|
|
data = {"updated_at": "2026-01-01T00:00:00", "platforms": platforms}
|
|
|
|
|
cache_file = tmp_path / "channel_directory.json"
|
|
|
|
|
cache_file.write_text(json.dumps(data))
|
|
|
|
|
return cache_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestLoadDirectory:
|
|
|
|
|
def test_missing_file(self, tmp_path):
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", tmp_path / "nope.json"):
|
|
|
|
|
result = load_directory()
|
|
|
|
|
assert result["updated_at"] is None
|
|
|
|
|
assert result["platforms"] == {}
|
|
|
|
|
|
|
|
|
|
def test_valid_file(self, tmp_path):
|
|
|
|
|
cache_file = _write_directory(tmp_path, {
|
|
|
|
|
"telegram": [{"id": "123", "name": "John", "type": "dm"}]
|
|
|
|
|
})
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", cache_file):
|
|
|
|
|
result = load_directory()
|
|
|
|
|
assert result["platforms"]["telegram"][0]["name"] == "John"
|
|
|
|
|
|
|
|
|
|
def test_corrupt_file(self, tmp_path):
|
|
|
|
|
cache_file = tmp_path / "channel_directory.json"
|
|
|
|
|
cache_file.write_text("{bad json")
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", cache_file):
|
|
|
|
|
result = load_directory()
|
|
|
|
|
assert result["updated_at"] is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestResolveChannelName:
|
|
|
|
|
def _setup(self, tmp_path, platforms):
|
|
|
|
|
cache_file = _write_directory(tmp_path, platforms)
|
|
|
|
|
return patch("gateway.channel_directory.DIRECTORY_PATH", cache_file)
|
|
|
|
|
|
|
|
|
|
def test_exact_match(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"discord": [
|
|
|
|
|
{"id": "111", "name": "bot-home", "guild": "MyServer", "type": "channel"},
|
|
|
|
|
{"id": "222", "name": "general", "guild": "MyServer", "type": "channel"},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("discord", "bot-home") == "111"
|
|
|
|
|
assert resolve_channel_name("discord", "#bot-home") == "111"
|
|
|
|
|
|
|
|
|
|
def test_case_insensitive(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"slack": [{"id": "C01", "name": "Engineering", "type": "channel"}]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("slack", "engineering") == "C01"
|
|
|
|
|
assert resolve_channel_name("slack", "ENGINEERING") == "C01"
|
|
|
|
|
|
|
|
|
|
def test_guild_qualified_match(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"discord": [
|
|
|
|
|
{"id": "111", "name": "general", "guild": "ServerA", "type": "channel"},
|
|
|
|
|
{"id": "222", "name": "general", "guild": "ServerB", "type": "channel"},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("discord", "ServerA/general") == "111"
|
|
|
|
|
assert resolve_channel_name("discord", "ServerB/general") == "222"
|
|
|
|
|
|
|
|
|
|
def test_prefix_match_unambiguous(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"slack": [
|
|
|
|
|
{"id": "C01", "name": "engineering-backend", "type": "channel"},
|
|
|
|
|
{"id": "C02", "name": "design-team", "type": "channel"},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
# "engineering" prefix matches only one channel
|
|
|
|
|
assert resolve_channel_name("slack", "engineering") == "C01"
|
|
|
|
|
|
|
|
|
|
def test_prefix_match_ambiguous_returns_none(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"slack": [
|
|
|
|
|
{"id": "C01", "name": "eng-backend", "type": "channel"},
|
|
|
|
|
{"id": "C02", "name": "eng-frontend", "type": "channel"},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("slack", "eng") is None
|
|
|
|
|
|
|
|
|
|
def test_no_channels_returns_none(self, tmp_path):
|
|
|
|
|
with self._setup(tmp_path, {}):
|
|
|
|
|
assert resolve_channel_name("telegram", "someone") is None
|
|
|
|
|
|
|
|
|
|
def test_no_match_returns_none(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"telegram": [{"id": "123", "name": "John", "type": "dm"}]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("telegram", "nonexistent") is None
|
|
|
|
|
|
2026-03-11 09:15:34 +01:00
|
|
|
def test_topic_name_resolves_to_composite_id(self, tmp_path):
|
|
|
|
|
platforms = {
|
|
|
|
|
"telegram": [{"id": "-1001:17585", "name": "Coaching Chat / topic 17585", "type": "group"}]
|
|
|
|
|
}
|
|
|
|
|
with self._setup(tmp_path, platforms):
|
|
|
|
|
assert resolve_channel_name("telegram", "Coaching Chat / topic 17585") == "-1001:17585"
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
class TestBuildFromSessions:
|
|
|
|
|
def _write_sessions(self, tmp_path, sessions_data):
|
|
|
|
|
"""Write sessions.json at the path _build_from_sessions expects."""
|
fix(cli): respect HERMES_HOME in all remaining hardcoded ~/.hermes paths
Several files resolved paths via Path.home() / ".hermes" or
os.path.expanduser("~/.hermes/..."), bypassing the HERMES_HOME
environment variable. This broke isolation when running multiple
Hermes instances with distinct HERMES_HOME directories.
Replace all hardcoded paths with calls to get_hermes_home() from
hermes_cli.config, consistent with the rest of the codebase.
Files fixed:
- tools/process_registry.py (processes.json)
- gateway/pairing.py (pairing/)
- gateway/sticker_cache.py (sticker_cache.json)
- gateway/channel_directory.py (channel_directory.json, sessions.json)
- gateway/config.py (gateway.json, config.yaml, sessions_dir)
- gateway/mirror.py (sessions/)
- gateway/hooks.py (hooks/)
- gateway/platforms/base.py (image_cache/, audio_cache/, document_cache/)
- gateway/platforms/whatsapp.py (whatsapp/session)
- gateway/delivery.py (cron/output)
- agent/auxiliary_client.py (auth.json)
- agent/prompt_builder.py (SOUL.md)
- cli.py (config.yaml, images/, pastes/, history)
- run_agent.py (logs/)
- tools/environments/base.py (sandboxes/)
- tools/environments/modal.py (modal_snapshots.json)
- tools/environments/singularity.py (singularity_snapshots.json)
- tools/tts_tool.py (audio_cache)
- hermes_cli/status.py (cron/jobs.json, sessions.json)
- hermes_cli/gateway.py (logs/, whatsapp session)
- hermes_cli/main.py (whatsapp/session)
Tests updated to use HERMES_HOME env var instead of patching Path.home().
Closes #892
(cherry picked from commit 78ac1bba43b8b74a934c6172f2c29bb4d03164b9)
2026-03-11 07:31:41 +01:00
|
|
|
sessions_path = tmp_path / "sessions" / "sessions.json"
|
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
|
|
|
sessions_path.parent.mkdir(parents=True)
|
|
|
|
|
sessions_path.write_text(json.dumps(sessions_data))
|
|
|
|
|
|
|
|
|
|
def test_builds_from_sessions_json(self, tmp_path):
|
|
|
|
|
self._write_sessions(tmp_path, {
|
|
|
|
|
"session_1": {
|
|
|
|
|
"origin": {
|
|
|
|
|
"platform": "telegram",
|
|
|
|
|
"chat_id": "12345",
|
|
|
|
|
"chat_name": "Alice",
|
|
|
|
|
},
|
|
|
|
|
"chat_type": "dm",
|
|
|
|
|
},
|
|
|
|
|
"session_2": {
|
|
|
|
|
"origin": {
|
|
|
|
|
"platform": "telegram",
|
|
|
|
|
"chat_id": "67890",
|
|
|
|
|
"user_name": "Bob",
|
|
|
|
|
},
|
|
|
|
|
"chat_type": "group",
|
|
|
|
|
},
|
|
|
|
|
"session_3": {
|
|
|
|
|
"origin": {
|
|
|
|
|
"platform": "discord",
|
|
|
|
|
"chat_id": "99999",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
fix(cli): respect HERMES_HOME in all remaining hardcoded ~/.hermes paths
Several files resolved paths via Path.home() / ".hermes" or
os.path.expanduser("~/.hermes/..."), bypassing the HERMES_HOME
environment variable. This broke isolation when running multiple
Hermes instances with distinct HERMES_HOME directories.
Replace all hardcoded paths with calls to get_hermes_home() from
hermes_cli.config, consistent with the rest of the codebase.
Files fixed:
- tools/process_registry.py (processes.json)
- gateway/pairing.py (pairing/)
- gateway/sticker_cache.py (sticker_cache.json)
- gateway/channel_directory.py (channel_directory.json, sessions.json)
- gateway/config.py (gateway.json, config.yaml, sessions_dir)
- gateway/mirror.py (sessions/)
- gateway/hooks.py (hooks/)
- gateway/platforms/base.py (image_cache/, audio_cache/, document_cache/)
- gateway/platforms/whatsapp.py (whatsapp/session)
- gateway/delivery.py (cron/output)
- agent/auxiliary_client.py (auth.json)
- agent/prompt_builder.py (SOUL.md)
- cli.py (config.yaml, images/, pastes/, history)
- run_agent.py (logs/)
- tools/environments/base.py (sandboxes/)
- tools/environments/modal.py (modal_snapshots.json)
- tools/environments/singularity.py (singularity_snapshots.json)
- tools/tts_tool.py (audio_cache)
- hermes_cli/status.py (cron/jobs.json, sessions.json)
- hermes_cli/gateway.py (logs/, whatsapp session)
- hermes_cli/main.py (whatsapp/session)
Tests updated to use HERMES_HOME env var instead of patching Path.home().
Closes #892
(cherry picked from commit 78ac1bba43b8b74a934c6172f2c29bb4d03164b9)
2026-03-11 07:31:41 +01:00
|
|
|
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
|
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
|
|
|
entries = _build_from_sessions("telegram")
|
|
|
|
|
|
|
|
|
|
assert len(entries) == 2
|
|
|
|
|
names = {e["name"] for e in entries}
|
|
|
|
|
assert "Alice" in names
|
|
|
|
|
assert "Bob" in names
|
|
|
|
|
|
|
|
|
|
def test_missing_sessions_file(self, tmp_path):
|
fix(cli): respect HERMES_HOME in all remaining hardcoded ~/.hermes paths
Several files resolved paths via Path.home() / ".hermes" or
os.path.expanduser("~/.hermes/..."), bypassing the HERMES_HOME
environment variable. This broke isolation when running multiple
Hermes instances with distinct HERMES_HOME directories.
Replace all hardcoded paths with calls to get_hermes_home() from
hermes_cli.config, consistent with the rest of the codebase.
Files fixed:
- tools/process_registry.py (processes.json)
- gateway/pairing.py (pairing/)
- gateway/sticker_cache.py (sticker_cache.json)
- gateway/channel_directory.py (channel_directory.json, sessions.json)
- gateway/config.py (gateway.json, config.yaml, sessions_dir)
- gateway/mirror.py (sessions/)
- gateway/hooks.py (hooks/)
- gateway/platforms/base.py (image_cache/, audio_cache/, document_cache/)
- gateway/platforms/whatsapp.py (whatsapp/session)
- gateway/delivery.py (cron/output)
- agent/auxiliary_client.py (auth.json)
- agent/prompt_builder.py (SOUL.md)
- cli.py (config.yaml, images/, pastes/, history)
- run_agent.py (logs/)
- tools/environments/base.py (sandboxes/)
- tools/environments/modal.py (modal_snapshots.json)
- tools/environments/singularity.py (singularity_snapshots.json)
- tools/tts_tool.py (audio_cache)
- hermes_cli/status.py (cron/jobs.json, sessions.json)
- hermes_cli/gateway.py (logs/, whatsapp session)
- hermes_cli/main.py (whatsapp/session)
Tests updated to use HERMES_HOME env var instead of patching Path.home().
Closes #892
(cherry picked from commit 78ac1bba43b8b74a934c6172f2c29bb4d03164b9)
2026-03-11 07:31:41 +01:00
|
|
|
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
|
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
|
|
|
entries = _build_from_sessions("telegram")
|
|
|
|
|
assert entries == []
|
|
|
|
|
|
|
|
|
|
def test_deduplication_by_chat_id(self, tmp_path):
|
|
|
|
|
self._write_sessions(tmp_path, {
|
|
|
|
|
"s1": {"origin": {"platform": "telegram", "chat_id": "123", "chat_name": "X"}},
|
|
|
|
|
"s2": {"origin": {"platform": "telegram", "chat_id": "123", "chat_name": "X"}},
|
|
|
|
|
})
|
|
|
|
|
|
fix(cli): respect HERMES_HOME in all remaining hardcoded ~/.hermes paths
Several files resolved paths via Path.home() / ".hermes" or
os.path.expanduser("~/.hermes/..."), bypassing the HERMES_HOME
environment variable. This broke isolation when running multiple
Hermes instances with distinct HERMES_HOME directories.
Replace all hardcoded paths with calls to get_hermes_home() from
hermes_cli.config, consistent with the rest of the codebase.
Files fixed:
- tools/process_registry.py (processes.json)
- gateway/pairing.py (pairing/)
- gateway/sticker_cache.py (sticker_cache.json)
- gateway/channel_directory.py (channel_directory.json, sessions.json)
- gateway/config.py (gateway.json, config.yaml, sessions_dir)
- gateway/mirror.py (sessions/)
- gateway/hooks.py (hooks/)
- gateway/platforms/base.py (image_cache/, audio_cache/, document_cache/)
- gateway/platforms/whatsapp.py (whatsapp/session)
- gateway/delivery.py (cron/output)
- agent/auxiliary_client.py (auth.json)
- agent/prompt_builder.py (SOUL.md)
- cli.py (config.yaml, images/, pastes/, history)
- run_agent.py (logs/)
- tools/environments/base.py (sandboxes/)
- tools/environments/modal.py (modal_snapshots.json)
- tools/environments/singularity.py (singularity_snapshots.json)
- tools/tts_tool.py (audio_cache)
- hermes_cli/status.py (cron/jobs.json, sessions.json)
- hermes_cli/gateway.py (logs/, whatsapp session)
- hermes_cli/main.py (whatsapp/session)
Tests updated to use HERMES_HOME env var instead of patching Path.home().
Closes #892
(cherry picked from commit 78ac1bba43b8b74a934c6172f2c29bb4d03164b9)
2026-03-11 07:31:41 +01:00
|
|
|
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
|
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
|
|
|
entries = _build_from_sessions("telegram")
|
|
|
|
|
|
|
|
|
|
assert len(entries) == 1
|
|
|
|
|
|
2026-03-11 09:15:34 +01:00
|
|
|
def test_keeps_distinct_topics_with_same_chat_id(self, tmp_path):
|
|
|
|
|
self._write_sessions(tmp_path, {
|
|
|
|
|
"group_root": {
|
|
|
|
|
"origin": {"platform": "telegram", "chat_id": "-1001", "chat_name": "Coaching Chat"},
|
|
|
|
|
"chat_type": "group",
|
|
|
|
|
},
|
|
|
|
|
"topic_a": {
|
|
|
|
|
"origin": {
|
|
|
|
|
"platform": "telegram",
|
|
|
|
|
"chat_id": "-1001",
|
|
|
|
|
"chat_name": "Coaching Chat",
|
|
|
|
|
"thread_id": "17585",
|
|
|
|
|
},
|
|
|
|
|
"chat_type": "group",
|
|
|
|
|
},
|
|
|
|
|
"topic_b": {
|
|
|
|
|
"origin": {
|
|
|
|
|
"platform": "telegram",
|
|
|
|
|
"chat_id": "-1001",
|
|
|
|
|
"chat_name": "Coaching Chat",
|
|
|
|
|
"thread_id": "17587",
|
|
|
|
|
},
|
|
|
|
|
"chat_type": "group",
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-13 21:35:07 -07:00
|
|
|
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
|
2026-03-11 09:15:34 +01:00
|
|
|
entries = _build_from_sessions("telegram")
|
|
|
|
|
|
|
|
|
|
ids = {entry["id"] for entry in entries}
|
|
|
|
|
names = {entry["name"] for entry in entries}
|
|
|
|
|
assert ids == {"-1001", "-1001:17585", "-1001:17587"}
|
|
|
|
|
assert "Coaching Chat" in names
|
|
|
|
|
assert "Coaching Chat / topic 17585" in names
|
|
|
|
|
assert "Coaching Chat / topic 17587" in names
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
class TestFormatDirectoryForDisplay:
|
|
|
|
|
def test_empty_directory(self, tmp_path):
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", tmp_path / "nope.json"):
|
|
|
|
|
result = format_directory_for_display()
|
|
|
|
|
assert "No messaging platforms" in result
|
|
|
|
|
|
|
|
|
|
def test_telegram_display(self, tmp_path):
|
|
|
|
|
cache_file = _write_directory(tmp_path, {
|
|
|
|
|
"telegram": [
|
|
|
|
|
{"id": "123", "name": "Alice", "type": "dm"},
|
|
|
|
|
{"id": "456", "name": "Dev Group", "type": "group"},
|
2026-03-11 09:15:34 +01:00
|
|
|
{"id": "-1001:17585", "name": "Coaching Chat / topic 17585", "type": "group"},
|
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
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", cache_file):
|
|
|
|
|
result = format_directory_for_display()
|
|
|
|
|
|
|
|
|
|
assert "Telegram:" in result
|
|
|
|
|
assert "telegram:Alice" in result
|
|
|
|
|
assert "telegram:Dev Group" in result
|
2026-03-11 09:15:34 +01:00
|
|
|
assert "telegram:Coaching Chat / topic 17585" in result
|
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_discord_grouped_by_guild(self, tmp_path):
|
|
|
|
|
cache_file = _write_directory(tmp_path, {
|
|
|
|
|
"discord": [
|
|
|
|
|
{"id": "1", "name": "general", "guild": "Server1", "type": "channel"},
|
|
|
|
|
{"id": "2", "name": "bot-home", "guild": "Server1", "type": "channel"},
|
|
|
|
|
{"id": "3", "name": "chat", "guild": "Server2", "type": "channel"},
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
with patch("gateway.channel_directory.DIRECTORY_PATH", cache_file):
|
|
|
|
|
result = format_directory_for_display()
|
|
|
|
|
|
|
|
|
|
assert "Discord (Server1):" in result
|
|
|
|
|
assert "Discord (Server2):" in result
|
|
|
|
|
assert "discord:#general" in result
|