Files
mem0/openclaw/docker/test-setup.sh
T
2026-04-01 10:55:50 +05:30

281 lines
9.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ============================================================================
# Test script for OpenClaw + Mem0 Docker setup
# Validates everything works without needing real API keys
# ============================================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PASS=0
FAIL=0
TESTS=()
pass() { PASS=$((PASS + 1)); TESTS+=("✔ $1"); echo " ✔ $1"; }
fail() { FAIL=$((FAIL + 1)); TESTS+=("✖ $1: $2"); echo " ✖ $1: $2"; }
echo ""
echo "═══════════════════════════════════════════════════"
echo " OpenClaw + Mem0 Docker Setup — Test Suite"
echo "═══════════════════════════════════════════════════"
# ── Prerequisites ────────────────────────────────────────────────────────────
echo ""
echo "▸ Prerequisites"
if command -v docker &>/dev/null; then
pass "Docker installed"
else
fail "Docker installed" "docker not found"
fi
if docker compose version &>/dev/null; then
pass "Docker Compose v2 available"
else
fail "Docker Compose v2 available" "docker compose not found"
fi
if docker info &>/dev/null 2>&1; then
pass "Docker daemon running"
else
fail "Docker daemon running" "daemon not responding"
fi
# ── File structure ───────────────────────────────────────────────────────────
echo ""
echo "▸ File structure"
EXPECTED_FILES=(
"setup.sh"
"docker-compose.yml"
"docker-compose.oss.yml"
".env.example"
"config/openclaw.platform.json"
"config/openclaw.oss.json"
"skills/mem0-onboarding/SKILL.md"
"README.md"
)
for f in "${EXPECTED_FILES[@]}"; do
if [ -f "$SCRIPT_DIR/$f" ]; then
pass "File exists: $f"
else
fail "File exists: $f" "missing"
fi
done
if [ -x "$SCRIPT_DIR/setup.sh" ]; then
pass "setup.sh is executable"
else
fail "setup.sh is executable" "not executable"
fi
# ── Bash syntax ──────────────────────────────────────────────────────────────
echo ""
echo "▸ Script syntax"
if bash -n "$SCRIPT_DIR/setup.sh" 2>&1; then
pass "setup.sh syntax valid"
else
fail "setup.sh syntax valid" "syntax errors"
fi
# ── JSON validity ────────────────────────────────────────────────────────────
echo ""
echo "▸ JSON configs"
for json_file in config/openclaw.platform.json config/openclaw.oss.json; do
if python3 -c "import json; json.load(open('$SCRIPT_DIR/$json_file'))" 2>/dev/null; then
pass "$json_file is valid JSON"
else
fail "$json_file is valid JSON" "parse error"
fi
done
# Check platform config has expected fields
if python3 -c "
import json, sys
c = json.load(open('$SCRIPT_DIR/config/openclaw.platform.json'))
p = c['plugins']['openclaw-mem0']
assert p['mode'] == 'platform', 'wrong mode'
assert p['apiKey'] == '\${MEM0_API_KEY}', 'apiKey not using env var'
assert p['userId'] == '__MEM0_USER_ID__', 'userId placeholder missing'
assert p['autoCapture'] is True
assert p['autoRecall'] is True
" 2>/dev/null; then
pass "Platform config has correct structure"
else
fail "Platform config has correct structure" "missing or wrong fields"
fi
# Check OSS config has Qdrant vector store
if python3 -c "
import json
c = json.load(open('$SCRIPT_DIR/config/openclaw.oss.json'))
p = c['plugins']['openclaw-mem0']
assert p['mode'] == 'open-source', 'wrong mode'
vs = p['oss']['vectorStore']
assert vs['provider'] == 'qdrant'
assert vs['config']['host'] == 'qdrant'
assert vs['config']['port'] == 6333
" 2>/dev/null; then
pass "OSS config has Qdrant vector store"
else
fail "OSS config has Qdrant vector store" "missing or wrong fields"
fi
# ── Docker Compose validation ────────────────────────────────────────────────
echo ""
echo "▸ Docker Compose validation"
cd "$SCRIPT_DIR"
# Create a temporary .env so compose can resolve variables
TEMP_ENV=false
if [ ! -f .env ]; then
cat > .env <<'EOF'
MEM0_API_KEY=test-key-for-validation
OPENAI_API_KEY=test-key-for-validation
MEM0_USER_ID=testuser
OPENCLAW_PORT=18789
OPENCLAW_CONFIG_DIR=/tmp/openclaw-test
TZ=UTC
EOF
TEMP_ENV=true
fi
if docker compose config --quiet 2>/dev/null; then
pass "docker-compose.yml is valid"
else
fail "docker-compose.yml is valid" "$(docker compose config 2>&1 | tail -1)"
fi
if docker compose -f docker-compose.yml -f docker-compose.oss.yml config --quiet 2>/dev/null; then
pass "docker-compose.oss.yml override is valid"
else
fail "docker-compose.oss.yml override is valid" "$(docker compose -f docker-compose.yml -f docker-compose.oss.yml config 2>&1 | tail -1)"
fi
# Verify services are correctly defined
PLATFORM_SERVICES=$(docker compose config --services 2>/dev/null)
if echo "$PLATFORM_SERVICES" | grep -q "openclaw"; then
pass "Platform mode defines 'openclaw' service"
else
fail "Platform mode defines 'openclaw' service" "service not found"
fi
OSS_SERVICES=$(docker compose -f docker-compose.yml -f docker-compose.oss.yml config --services 2>/dev/null)
if echo "$OSS_SERVICES" | grep -q "qdrant"; then
pass "OSS mode adds 'qdrant' service"
else
fail "OSS mode adds 'qdrant' service" "service not found"
fi
# Verify port binding is localhost-only
PORT_CONFIG=$(docker compose config 2>/dev/null | grep -A5 "published" | head -10)
if docker compose config 2>/dev/null | grep -q "127.0.0.1"; then
pass "Port is bound to 127.0.0.1 (localhost only)"
else
fail "Port is bound to 127.0.0.1 (localhost only)" "binding not found"
fi
# Verify healthcheck is defined
if docker compose config 2>/dev/null | grep -q "healthz"; then
pass "Healthcheck is configured"
else
fail "Healthcheck is configured" "healthcheck not found"
fi
# ── Docker image pull test ───────────────────────────────────────────────────
echo ""
echo "▸ Docker image availability"
# Just check if the image reference is parseable (don't actually pull — too slow for a test)
IMAGE=$(docker compose config 2>/dev/null | grep "image:" | head -1 | awk '{print $2}')
if [ -n "$IMAGE" ]; then
pass "OpenClaw image reference: $IMAGE"
else
fail "OpenClaw image reference" "could not extract image name"
fi
QDRANT_IMAGE=$(docker compose -f docker-compose.yml -f docker-compose.oss.yml config 2>/dev/null | grep "image:" | grep qdrant | awk '{print $2}')
if [ -n "$QDRANT_IMAGE" ]; then
pass "Qdrant image reference: $QDRANT_IMAGE"
else
fail "Qdrant image reference" "could not extract image name"
fi
# ── setup.sh simulation ─────────────────────────────────────────────────────
echo ""
echo "▸ setup.sh config generation simulation"
# Test the sed substitution that setup.sh does
TEST_CONFIG_DIR=$(mktemp -d)
sed "s/__MEM0_USER_ID__/testuser/g" "$SCRIPT_DIR/config/openclaw.platform.json" > "$TEST_CONFIG_DIR/openclaw.json"
if python3 -c "
import json
c = json.load(open('$TEST_CONFIG_DIR/openclaw.json'))
p = c['plugins']['openclaw-mem0']
assert p['userId'] == 'testuser', f'expected testuser, got {p[\"userId\"]}'
assert p['apiKey'] == '\${MEM0_API_KEY}', 'apiKey should remain as env var template'
" 2>/dev/null; then
pass "sed substitution works correctly (userId baked, apiKey templated)"
else
fail "sed substitution" "userId not replaced or apiKey was wrongly replaced"
fi
rm -rf "$TEST_CONFIG_DIR"
# OSS config substitution
TEST_CONFIG_DIR=$(mktemp -d)
sed "s/__MEM0_USER_ID__/testuser/g" "$SCRIPT_DIR/config/openclaw.oss.json" > "$TEST_CONFIG_DIR/openclaw.json"
if python3 -c "
import json
c = json.load(open('$TEST_CONFIG_DIR/openclaw.json'))
p = c['plugins']['openclaw-mem0']
assert p['userId'] == 'testuser'
assert p['oss']['vectorStore']['config']['host'] == 'qdrant'
" 2>/dev/null; then
pass "OSS config sed substitution works correctly"
else
fail "OSS config sed substitution" "fields incorrect after substitution"
fi
rm -rf "$TEST_CONFIG_DIR"
# ── Cleanup ──────────────────────────────────────────────────────────────────
if [ "$TEMP_ENV" = true ]; then
rm -f "$SCRIPT_DIR/.env"
fi
# ── Summary ──────────────────────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════"
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo " All $TOTAL tests passed ✔"
else
echo " $PASS/$TOTAL passed, $FAIL failed"
echo ""
for t in "${TESTS[@]}"; do
if [[ "$t" == "✖"* ]]; then
echo " $t"
fi
done
fi
echo "═══════════════════════════════════════════════════"
echo ""
exit $FAIL