170 lines
5.9 KiB
Bash
Executable File
170 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Memory OS Smoke Test
|
|
# Verifies the entire stack is functional without needing to know what to check.
|
|
#
|
|
# Usage:
|
|
# ./setup/smoke_test.sh # All checks
|
|
# ./setup/smoke_test.sh --quick # Skip ingestion test (faster)
|
|
# ./setup/smoke_test.sh --help # Show help
|
|
#
|
|
# Environment:
|
|
# REDIS_PASSWORD Redis password
|
|
# QDRANT_API_KEY Qdrant API key (default: "")
|
|
# REDIS_HOST Redis host (default: localhost)
|
|
# REDIS_PORT Redis port (default: 6379)
|
|
# QDRANT_HOST Qdrant host (default: localhost)
|
|
# QDRANT_PORT Qdrant port (default: 6333)
|
|
# COLLECTION_NAME Qdrant collection (default: knowledge_base)
|
|
|
|
set -euo pipefail
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
QUICK_MODE=false
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--quick) QUICK_MODE=true ;;
|
|
--help) echo "Usage: ./setup/smoke_test.sh [--quick]"; exit 0 ;;
|
|
esac
|
|
done
|
|
|
|
RED=''
|
|
GREEN=''
|
|
NC=''
|
|
if [ -t 1 ]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
NC='\033[0m'
|
|
fi
|
|
|
|
check() {
|
|
local label="$1"
|
|
local cmd="$2"
|
|
printf " %-40s " "$label"
|
|
if eval "$cmd" >/dev/null 2>&1; then
|
|
printf "${GREEN}✅${NC}\n"
|
|
PASS=$((PASS + 1))
|
|
else
|
|
printf "${RED}❌${NC}\n"
|
|
FAIL=$((FAIL + 1))
|
|
fi
|
|
}
|
|
|
|
# ── Resolve env vars ─────────────────────────────────────────────────────────
|
|
REDIS_HOST="${REDIS_HOST:-localhost}"
|
|
REDIS_PORT="${REDIS_PORT:-6379}"
|
|
REDIS_PASSWORD="${REDIS_PASSWORD:-}"
|
|
QDRANT_HOST="${QDRANT_HOST:-localhost}"
|
|
QDRANT_PORT="${QDRANT_PORT:-6333}"
|
|
QDRANT_API_KEY="${QDRANT_API_KEY:-}"
|
|
COLLECTION_NAME="${COLLECTION_NAME:-knowledge_base}"
|
|
|
|
echo "=== Memory OS Smoke Test ==="
|
|
echo " Redis: ${REDIS_HOST}:${REDIS_PORT}"
|
|
echo " Qdrant: ${QDRANT_HOST}:${QDRANT_PORT}"
|
|
echo " Collection: ${COLLECTION_NAME}"
|
|
echo ""
|
|
|
|
# ── 1. Infrastructure ────────────────────────────────────────────────────────
|
|
echo "── Infrastructure ──"
|
|
|
|
check "Docker running" \
|
|
"docker info"
|
|
|
|
check "Redis reachable" \
|
|
"python3 -c \"
|
|
import redis
|
|
r = redis.Redis(host='${REDIS_HOST}', port=${REDIS_PORT},
|
|
password='${REDIS_PASSWORD}' or None)
|
|
assert r.ping()
|
|
\""
|
|
|
|
# Note: Qdrant healthcheck uses grep on /proc/net/tcp (shell built-in) because
|
|
# the qdrant/qdrant image does not include curl, wget, or python3.
|
|
# Port 6333 = 0x18BD in hex.
|
|
check "Qdrant health" \
|
|
"python3 -c \"
|
|
from qdrant_client import QdrantClient
|
|
c = QdrantClient(host='${QDRANT_HOST}', port=${QDRANT_PORT},
|
|
api_key='${QDRANT_API_KEY}' or None, https=False)
|
|
collections = c.get_collections()
|
|
assert len(collections.collections) >= 1
|
|
\""
|
|
|
|
check "Qdrant collection '${COLLECTION_NAME}'" \
|
|
"python3 -c \"
|
|
from qdrant_client import QdrantClient
|
|
c = QdrantClient(host='${QDRANT_HOST}', port=${QDRANT_PORT},
|
|
api_key='${QDRANT_API_KEY}' or None, https=False)
|
|
info = c.get_collection('${COLLECTION_NAME}')
|
|
assert info.config.params.vectors is not None
|
|
\""
|
|
|
|
# ── 2. Icarus plugin ─────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Icarus Plugin ──"
|
|
|
|
check "Icarus plugin installed" \
|
|
"test -f ~/.hermes/plugins/icarus/__init__.py"
|
|
|
|
check "Icarus plugin loaded" \
|
|
"hermes plugins list 2>/dev/null | grep -q icarus"
|
|
|
|
# ── 3. Embedding ─────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Embedding ──"
|
|
|
|
check "Embedding produces 4096d vectors" \
|
|
"python3 << 'PYEOF'
|
|
from qdrant_client import QdrantClient
|
|
c = QdrantClient(host='${QDRANT_HOST}', port=${QDRANT_PORT},
|
|
api_key='${QDRANT_API_KEY}' or None, https=False)
|
|
points, _ = c.scroll('${COLLECTION_NAME}', limit=1, with_vectors=True)
|
|
assert len(points) > 0, 'no points found in collection'
|
|
assert len(points[0].vector['dense']) == 4096, \\
|
|
f'expected 4096 dims, got {len(points[0].vector[\"dense\"])}'
|
|
PYEOF"
|
|
|
|
# ── 4. Ingestion pipeline ────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Ingestion Pipeline ──"
|
|
|
|
if [ "$QUICK_MODE" = true ]; then
|
|
echo " (skipped — --quick mode)"
|
|
else
|
|
check "End-to-end ingestion" \
|
|
"python3 scripts/test_ingestion.py"
|
|
fi
|
|
|
|
# ── 5. Cron jobs ─────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Cron Jobs ──"
|
|
|
|
check "Cron jobs active (≥3)" \
|
|
"python3 -c \"
|
|
import subprocess, json
|
|
out = subprocess.run(['hermes', 'cron', 'list'],
|
|
capture_output=True, text=True).stdout
|
|
# Count lines with '[active]'
|
|
count = out.count('[active]')
|
|
assert count >= 3, f'expected >=3 active cron jobs, got {count}'
|
|
\""
|
|
|
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "──────────────────────────────────────────"
|
|
if [ "$QUICK_MODE" = true ]; then
|
|
echo "Result (quick mode): $PASS passed, $FAIL failed, 1 skipped"
|
|
else
|
|
echo "Result: $PASS passed, $FAIL failed"
|
|
fi
|
|
|
|
if [ "$FAIL" -eq 0 ]; then
|
|
echo "✅ All checks passed — Memory OS is operational."
|
|
exit 0
|
|
else
|
|
echo "❌ $FAIL check(s) failed — review output above."
|
|
exit 1
|
|
fi
|