|
@@ -117,6 +117,12 @@ def _registered_tool_names(ctx) -> list[str]:
|
|
|
return [tool["name"] for tool in ctx.registered_tools]
|
|
return [tool["name"] for tool in ctx.registered_tools]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _read_repo_file(name):
|
|
|
|
|
+ """One of this repo's own documents (README.md, AGENTS.md), as text."""
|
|
|
|
|
+ with open(os.path.join(PLUGIN_ROOT, name)) as handle:
|
|
|
|
|
+ return handle.read()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _load_plugin_tools():
|
|
def _load_plugin_tools():
|
|
|
"""Load the plugin's ``tools.py`` under a private module name.
|
|
"""Load the plugin's ``tools.py`` under a private module name.
|
|
|
|
|
|
|
@@ -416,6 +422,55 @@ class TestRegistration:
|
|
|
with patch.object(ChattoAdapter, "send_video", BasePlatformAdapter.send_video):
|
|
with patch.object(ChattoAdapter, "send_video", BasePlatformAdapter.send_video):
|
|
|
assert "video" not in _capabilities()
|
|
assert "video" not in _capabilities()
|
|
|
|
|
|
|
|
|
|
+ def test_readme_yaml_example_is_shaped_like_a_config_hermes_reads(self):
|
|
|
|
|
+ """The example is what users paste into ~/.hermes/config.yaml. Two ways
|
|
|
|
|
+ it silently did nothing: `platforms:` nested under `gateway:` (only the
|
|
|
|
|
+ flat form is read — GatewayConfig.from_dict picks that key top-level),
|
|
|
|
|
+ and a platform key that resolves to no member (unknown keys are skipped
|
|
|
|
|
+ without an error). Shape is asserted without Hermes resolution, because
|
|
|
|
|
+ a checkout is not necessarily installed in a Hermes tree.
|
|
|
|
|
+ """
|
|
|
|
|
+ import re
|
|
|
|
|
+
|
|
|
|
|
+ import yaml
|
|
|
|
|
+
|
|
|
|
|
+ readme = _read_repo_file("README.md")
|
|
|
|
|
+ block = re.search(r"```yaml\n(platforms:.*?)```", readme, re.DOTALL)
|
|
|
|
|
+ assert block, "README has no config.yaml example block"
|
|
|
|
|
+ parsed = yaml.safe_load(block.group(1))
|
|
|
|
|
+ assert "platforms" in parsed, (
|
|
|
|
|
+ "config.yaml example must carry platforms: at the top level — nested "
|
|
|
|
|
+ "under gateway: it is silently ignored"
|
|
|
|
|
+ )
|
|
|
|
|
+ assert parsed["platforms"], "example configures no platform"
|
|
|
|
|
+ (platform_key,) = parsed["platforms"]
|
|
|
|
|
+ assert platform_key in ("hermes-chatto-plugin", "chatto-platform"), (
|
|
|
|
|
+ f"documented platform key {platform_key!r} is neither the plugin's "
|
|
|
|
|
+ "directory name nor its plugin.yaml name, so it configures nothing"
|
|
|
|
|
+ )
|
|
|
|
|
+ assert parsed["platforms"][platform_key]["extra"]["home_channel"]
|
|
|
|
|
+
|
|
|
|
|
+ def test_documented_config_path_names_a_platform_key(self):
|
|
|
|
|
+ """The docs name a `platforms.<key>.extra` path. `chatto` is neither the
|
|
|
|
|
+ plugin's directory name nor its plugin.yaml name, so it matches no
|
|
|
|
|
+ member and configures nothing."""
|
|
|
|
|
+ for doc in ("README.md", "AGENTS.md"):
|
|
|
|
|
+ text = _read_repo_file(doc)
|
|
|
|
|
+ assert "platforms.hermes-chatto-plugin.extra" in text, doc
|
|
|
|
|
+ assert "platforms.chatto.extra" not in text, doc
|
|
|
|
|
+
|
|
|
|
|
+ def test_documented_install_path_matches_the_documented_config_key(self):
|
|
|
|
|
+ """Both install paths must name the same directory: Hermes keys the
|
|
|
|
|
+ platform by it, so a README that installs into `chatto/` documents a
|
|
|
|
|
+ config path that will not match what it installs."""
|
|
|
|
|
+ readme = _read_repo_file("README.md")
|
|
|
|
|
+ assert "plugins/platforms/hermes-chatto-plugin/" in readme
|
|
|
|
|
+ # Only our own install instruction — the acknowledgements quote a
|
|
|
|
|
+ # third-party repo URL whose path is theirs and must stay verbatim.
|
|
|
|
|
+ install_steps = readme.split("## Acknowledgements")[0]
|
|
|
|
|
+ assert install_steps, "README lost its Acknowledgements heading"
|
|
|
|
|
+ assert "platforms/chatto/" not in install_steps
|
|
|
|
|
+
|
|
|
def test_provides_tools_is_deliverable_in_a_cli_session(self):
|
|
def test_provides_tools_is_deliverable_in_a_cli_session(self):
|
|
|
"""``provides_tools`` asks Hermes to register the tools without the
|
|
"""``provides_tools`` asks Hermes to register the tools without the
|
|
|
adapter, which it can only do through a ``tools.py``. A manifest that
|
|
adapter, which it can only do through a ``tools.py``. A manifest that
|