fix(plugin): bind adapter to real Hermes gateway API; add contract-test gate
- Resolve the Hermes gateway API lazily via get_adapter_class() so the adapter
always inherits the real gateway.platforms.base.BasePlatformAdapter instead
of the ImportError fallback stub (root cause of the AttributeError:
'set_message_handler' crash at gateway startup).
- Pass Platform('meshtastic') enum to super().__init__ and align
connect(*, is_reconnect=False) -> bool with the gateway contract.
- Split pure formatting utils into formatting.py; remove the stub fallback.
- Add tests/contract (L0-L3): real-base binding, ABC instantiation, gateway
run.py wiring replay, signature and event-shape conformance, run against
hermes-agent pinned to the deployed image source (v2026.8.31).
- Add scripts/contract-test.sh and a contract-test CI job; release publish
now requires contract tests to pass.
- Untrack stray __pycache__ artifacts.
This commit is contained in:
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Gateway contract tests for hermes-meshtastic.
|
||||
#
|
||||
# Runs the plugin's contract suite (tests/contract/) against the REAL Hermes
|
||||
# gateway API, pinned to the same upstream commit the deployed container image
|
||||
# is built from (NousResearch/hermes-agent @ v2026.8.31 — the image tag used in
|
||||
# k8s-flux apps/base/coder/hermes-agent).
|
||||
#
|
||||
# Why: the adapter used to be developed by guessing the gateway API and only
|
||||
# failing at gateway startup after a release (AttributeError: ... no attribute
|
||||
# 'set_message_handler'). These tests replay the gateway's adapter bootstrap
|
||||
# (registry registration -> factory -> set_* wiring) so API drift fails here,
|
||||
# in seconds, without deploying.
|
||||
#
|
||||
# Requires: uv (https://docs.astral.sh/uv/) and network on first run.
|
||||
#
|
||||
# Usage: scripts/contract-test.sh
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PLUGIN_DIR="$REPO_ROOT/plugin"
|
||||
VENV="$PLUGIN_DIR/.venv-contract"
|
||||
HERMES_SRC="$PLUGIN_DIR/.hermes-src"
|
||||
HERMES_TAG="${HERMES_TAG:-v2026.8.31}" # must match k8s-flux hermes image tag
|
||||
HERMES_COMMIT="${HERMES_COMMIT:-29112bef099274229cadff79cdff7bf7b99c4b77}"
|
||||
|
||||
if ! command -v uv >/dev/null 2>&1; then
|
||||
echo "error: uv is required (https://docs.astral.sh/uv/)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$HERMES_SRC/.git" ]; then
|
||||
echo "== cloning hermes-agent @ $HERMES_TAG (deployed image source) =="
|
||||
git clone --depth 1 --branch "$HERMES_TAG" \
|
||||
https://github.com/NousResearch/hermes-agent.git "$HERMES_SRC"
|
||||
fi
|
||||
actual="$(git -C "$HERMES_SRC" rev-parse HEAD)"
|
||||
if [ "$actual" != "$HERMES_COMMIT" ]; then
|
||||
echo "error: $HERMES_SRC is at $actual, expected $HERMES_COMMIT ($HERMES_TAG)." >&2
|
||||
echo " Refresh deliberately: HERMES_TAG=<new tag> scripts/contract-test.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$VENV/bin/python" ]; then
|
||||
echo "== creating contract venv (python 3.13, matching the Hermes image) =="
|
||||
uv venv --python 3.13 "$VENV"
|
||||
fi
|
||||
uv pip install --python "$VENV/bin/python" -q -e "$HERMES_SRC"
|
||||
uv pip install --python "$VENV/bin/python" -q pytest
|
||||
|
||||
echo "== running contract tests (HERMES_CONTRACT_REQUIRED=1) =="
|
||||
(
|
||||
cd "$PLUGIN_DIR"
|
||||
HERMES_CONTRACT_REQUIRED=1 "$VENV/bin/python" -m pytest tests/contract -q "$@"
|
||||
)
|
||||
|
||||
echo "== running standalone unit tests (no gateway needed) =="
|
||||
(
|
||||
cd "$PLUGIN_DIR"
|
||||
"$VENV/bin/python" -m pytest tests/test_adapter.py -q
|
||||
)
|
||||
Reference in New Issue
Block a user