Files
timmy-config/infra/matrix/scripts/test-local-integration.sh
Ezra (Archivist) 41e572b160
Some checks failed
Validate Matrix Scaffold / validate-scaffold (push) Has been cancelled
infra(matrix): CI validation + local integration test for #166/#183
- Add Gitea workflow to validate matrix scaffold on every push/PR (#183)
- Add docker-compose.test.yml for local Conduit testing (#166)
- Add test-local-integration.sh: end-to-end Hermes Matrix adapter
  proof without requiring public DNS/domain

This makes #183 self-enforcing and proves #166 is execution-ready
pending only the host/domain decision in #187.
2026-04-05 20:40:28 +00:00

198 lines
6.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# test-local-integration.sh — End-to-end local Matrix/Conduit + Hermes integration test
# Issue: #166
#
# Spins up a local Conduit instance, registers a test user, and proves the
# Hermes Matrix adapter can connect, sync, join rooms, and send messages.
#
# Usage:
# cd infra/matrix
# ./scripts/test-local-integration.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(dirname "$SCRIPT_DIR")"
COMPOSE_FILE="$BASE_DIR/docker-compose.test.yml"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $*"; }
fail() { echo -e "${RED}[FAIL]${NC} $*"; }
info() { echo -e "${YELLOW}[INFO]${NC} $*"; }
cleanup() {
info "Cleaning up test environment..."
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans 2>/dev/null || true
}
trap cleanup EXIT
info "=================================================="
info "Hermes Matrix Local Integration Test"
info "Target: #166 | Environment: localhost"
info "=================================================="
# --- Start test environment ---
info "Starting Conduit test environment..."
docker compose -f "$COMPOSE_FILE" up -d --wait
# --- Wait for Conduit ---
info "Waiting for Conduit to accept connections..."
for i in {1..30}; do
if curl -sf http://localhost:8448/_matrix/client/versions >/dev/null 2>&1; then
pass "Conduit is responding on localhost:8448"
break
fi
sleep 1
done
if ! curl -sf http://localhost:8448/_matrix/client/versions >/dev/null 2>&1; then
fail "Conduit failed to start within 30 seconds"
exit 1
fi
# --- Register test user ---
TEST_USER="hermes_test_$(date +%s)"
TEST_PASS="testpass_$(openssl rand -hex 8)"
HOMESERVER="http://localhost:8448"
info "Registering test user: $TEST_USER"
REG_PAYLOAD=$(cat <<EOF
{
"username": "$TEST_USER",
"password": "$TEST_PASS",
"auth": {"type": "m.login.dummy"}
}
EOF
)
REG_RESPONSE=$(curl -sf -X POST \
-H "Content-Type: application/json" \
-d "$REG_PAYLOAD" \
"$HOMESERVER/_matrix/client/v3/register" 2>/dev/null || echo '{}')
ACCESS_TOKEN=$(echo "$REG_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || true)
if [[ -z "$ACCESS_TOKEN" ]]; then
# Try login if registration failed (user might already exist somehow)
info "Registration response missing token, attempting login..."
LOGIN_RESPONSE=$(curl -sf -X POST \
-H "Content-Type: application/json" \
-d "{\"type\":\"m.login.password\",\"user\":\"$TEST_USER\",\"password\":\"$TEST_PASS\"}" \
"$HOMESERVER/_matrix/client/v3/login" 2>/dev/null || echo '{}')
ACCESS_TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || true)
fi
if [[ -z "$ACCESS_TOKEN" ]]; then
fail "Could not register or login test user"
echo "Registration response: $REG_RESPONSE"
exit 1
fi
pass "Test user authenticated"
# --- Create test room ---
info "Creating test room..."
ROOM_RESPONSE=$(curl -sf -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{"preset":"public_chat","name":"Hermes Integration Test","topic":"Automated test room"}' \
"$HOMESERVER/_matrix/client/v3/createRoom" 2>/dev/null || echo '{}')
ROOM_ID=$(echo "$ROOM_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('room_id',''))" 2>/dev/null || true)
if [[ -z "$ROOM_ID" ]]; then
fail "Could not create test room"
echo "Room response: $ROOM_RESPONSE"
exit 1
fi
pass "Test room created: $ROOM_ID"
# --- Run Hermes-style probe ---
info "Running Hermes Matrix adapter probe..."
export MATRIX_HOMESERVER="$HOMESERVER"
export MATRIX_USER_ID="@$TEST_USER:localhost"
export MATRIX_ACCESS_TOKEN="$ACCESS_TOKEN"
export MATRIX_TEST_ROOM="$ROOM_ID"
export MATRIX_ENCRYPTION="false"
python3 <<'PYEOF'
import asyncio
import os
import sys
from datetime import datetime, timezone
try:
from nio import AsyncClient, SyncResponse, RoomSendResponse
except ImportError:
print("matrix-nio not installed. Installing...")
import subprocess
subprocess.check_call([sys.executable, "-m", "pip", "install", "--quiet", "matrix-nio"])
from nio import AsyncClient, SyncResponse, RoomSendResponse
HOMESERVER = os.getenv("MATRIX_HOMESERVER", "").rstrip("/")
USER_ID = os.getenv("MATRIX_USER_ID", "")
ACCESS_TOKEN = os.getenv("MATRIX_ACCESS_TOKEN", "")
ROOM_ID = os.getenv("MATRIX_TEST_ROOM", "")
def ok(msg): print(f"\033[0;32m[PASS]\033[0m {msg}")
def err(msg): print(f"\033[0;31m[FAIL]\033[0m {msg}")
async def main():
client = AsyncClient(HOMESERVER, USER_ID)
client.access_token = ACCESS_TOKEN
client.user_id = USER_ID
try:
whoami = await client.whoami()
if hasattr(whoami, "user_id"):
ok(f"Whoami authenticated as {whoami.user_id}")
else:
err(f"Whoami failed: {whoami}")
return 1
sync_resp = await client.sync(timeout=10000)
if isinstance(sync_resp, SyncResponse):
ok(f"Initial sync complete ({len(sync_resp.rooms.join)} joined rooms)")
else:
err(f"Initial sync failed: {sync_resp}")
return 1
test_body = f"🔥 Hermes local integration probe | {datetime.now(timezone.utc).isoformat()}"
send_resp = await client.room_send(
ROOM_ID,
"m.room.message",
{"msgtype": "m.text", "body": test_body},
)
if isinstance(send_resp, RoomSendResponse):
ok(f"Test message sent (event_id: {send_resp.event_id})")
else:
err(f"Test message failed: {send_resp}")
return 1
ok("All integration checks passed — Hermes Matrix adapter works locally.")
return 0
finally:
await client.close()
sys.exit(asyncio.run(main()))
PYEOF
PROBE_EXIT=$?
if [[ $PROBE_EXIT -eq 0 ]]; then
pass "Local integration test PASSED"
info "=================================================="
info "Result: #166 is execution-ready."
info "The only remaining blocker is host/domain (#187)."
info "=================================================="
else
fail "Local integration test FAILED"
exit 1
fi