mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
refactor(test): share onboarding e2e helpers
This commit is contained in:
@@ -52,7 +52,8 @@ if ! docker run --rm \
|
||||
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
eval "$(printf "%s" "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}" | base64 -d)"
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
export OPENAI_API_KEY="sk-openclaw-npm-onboard-e2e"
|
||||
@@ -63,6 +64,7 @@ PORT="18789"
|
||||
MOCK_PORT="44080"
|
||||
SUCCESS_MARKER="OPENCLAW_AGENT_E2E_OK_ASSISTANT"
|
||||
MOCK_REQUEST_LOG="/tmp/openclaw-mock-openai-requests.jsonl"
|
||||
export SUCCESS_MARKER MOCK_REQUEST_LOG
|
||||
mock_pid=""
|
||||
|
||||
case "$CHANNEL" in
|
||||
@@ -81,17 +83,14 @@ case "$CHANNEL" in
|
||||
esac
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${mock_pid:-}" ] && kill -0 "$mock_pid" 2>/dev/null; then
|
||||
kill "$mock_pid" 2>/dev/null || true
|
||||
wait "$mock_pid" 2>/dev/null || true
|
||||
fi
|
||||
openclaw_e2e_stop_process "${mock_pid:-}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
dump_debug_logs() {
|
||||
local status="$1"
|
||||
echo "npm onboard/channel/agent scenario failed with exit code $status" >&2
|
||||
for file in \
|
||||
openclaw_e2e_dump_logs \
|
||||
/tmp/openclaw-install.log \
|
||||
/tmp/openclaw-onboard.json \
|
||||
/tmp/openclaw-channel-add.log \
|
||||
@@ -100,12 +99,7 @@ dump_debug_logs() {
|
||||
/tmp/openclaw-agent.err \
|
||||
/tmp/openclaw-agent.json \
|
||||
/tmp/openclaw-mock-openai.log \
|
||||
"$MOCK_REQUEST_LOG"; do
|
||||
if [ -f "$file" ]; then
|
||||
echo "--- $file ---" >&2
|
||||
sed -n '1,220p' "$file" >&2 || true
|
||||
fi
|
||||
done
|
||||
"$MOCK_REQUEST_LOG"
|
||||
}
|
||||
trap 'status=$?; dump_debug_logs "$status"; exit "$status"' ERR
|
||||
|
||||
@@ -136,15 +130,8 @@ assert_dep_present() {
|
||||
fi
|
||||
}
|
||||
|
||||
MOCK_PORT="$MOCK_PORT" SUCCESS_MARKER="$SUCCESS_MARKER" MOCK_REQUEST_LOG="$MOCK_REQUEST_LOG" node scripts/e2e/mock-openai-server.mjs >/tmp/openclaw-mock-openai.log 2>&1 &
|
||||
mock_pid="$!"
|
||||
for _ in $(seq 1 80); do
|
||||
if node -e "fetch('http://127.0.0.1:${MOCK_PORT}/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"; then
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
node -e "fetch('http://127.0.0.1:${MOCK_PORT}/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||
mock_pid="$(openclaw_e2e_start_mock_openai "$MOCK_PORT" /tmp/openclaw-mock-openai.log)"
|
||||
openclaw_e2e_wait_mock_openai "$MOCK_PORT"
|
||||
|
||||
echo "Running non-interactive onboarding..."
|
||||
openclaw onboard --non-interactive --accept-risk \
|
||||
|
||||
@@ -4,54 +4,27 @@ set -euo pipefail
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-onboard-e2e" OPENCLAW_ONBOARD_E2E_IMAGE)"
|
||||
OPENCLAW_TEST_STATE_FUNCTION_B64="$(
|
||||
node "$ROOT_DIR/scripts/lib/openclaw-test-state.mjs" shell-function \
|
||||
| base64 \
|
||||
| tr -d '\n'
|
||||
)"
|
||||
OPENCLAW_TEST_STATE_FUNCTION_B64="$(docker_e2e_test_state_function_b64)"
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" onboard
|
||||
docker_e2e_harness_mount_args
|
||||
|
||||
echo "Running onboarding E2E..."
|
||||
docker run --rm -t \
|
||||
-e "OPENCLAW_TEST_STATE_FUNCTION_B64=$OPENCLAW_TEST_STATE_FUNCTION_B64" \
|
||||
"${DOCKER_E2E_HARNESS_ARGS[@]}" \
|
||||
"$IMAGE_NAME" bash -lc '
|
||||
set -euo pipefail
|
||||
trap "" PIPE
|
||||
export TERM=xterm-256color
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
eval "$(printf "%s" "${OPENCLAW_TEST_STATE_FUNCTION_B64:?missing OPENCLAW_TEST_STATE_FUNCTION_B64}" | base64 -d)"
|
||||
ONBOARD_FLAGS="--flow quickstart --auth-choice skip --skip-channels --skip-skills --skip-daemon --skip-ui"
|
||||
# tsdown may emit dist/index.js or dist/index.mjs depending on runtime/bundler.
|
||||
if [ -f dist/index.mjs ]; then
|
||||
OPENCLAW_ENTRY="dist/index.mjs"
|
||||
elif [ -f dist/index.js ]; then
|
||||
OPENCLAW_ENTRY="dist/index.js"
|
||||
else
|
||||
echo "Missing dist/index.(m)js (build output):"
|
||||
ls -la dist || true
|
||||
exit 1
|
||||
fi
|
||||
OPENCLAW_ENTRY="$(openclaw_e2e_resolve_entrypoint)"
|
||||
export OPENCLAW_ENTRY
|
||||
|
||||
# Provide a minimal trash shim to avoid noisy "missing trash" logs in containers.
|
||||
export PATH="/tmp/openclaw-bin:$PATH"
|
||||
mkdir -p /tmp/openclaw-bin
|
||||
cat > /tmp/openclaw-bin/trash <<'"'"'TRASH'"'"'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
trash_dir="$HOME/.Trash"
|
||||
mkdir -p "$trash_dir"
|
||||
for target in "$@"; do
|
||||
[ -e "$target" ] || continue
|
||||
base="$(basename "$target")"
|
||||
dest="$trash_dir/$base"
|
||||
if [ -e "$dest" ]; then
|
||||
dest="$trash_dir/${base}-$(date +%s)-$$"
|
||||
fi
|
||||
mv "$target" "$dest"
|
||||
done
|
||||
TRASH
|
||||
chmod +x /tmp/openclaw-bin/trash
|
||||
openclaw_e2e_install_trash_shim
|
||||
|
||||
send() {
|
||||
local payload="$1"
|
||||
@@ -120,29 +93,12 @@ TRASH
|
||||
}
|
||||
|
||||
start_gateway() {
|
||||
node "$OPENCLAW_ENTRY" gateway --port 18789 --bind loopback --allow-unconfigured > /tmp/gateway-e2e.log 2>&1 &
|
||||
GATEWAY_PID="$!"
|
||||
GATEWAY_PID="$(openclaw_e2e_start_gateway "$OPENCLAW_ENTRY" 18789 /tmp/gateway-e2e.log)"
|
||||
}
|
||||
|
||||
wait_for_gateway() {
|
||||
for _ in $(seq 1 20); do
|
||||
if node --input-type=module -e "
|
||||
import net from 'node:net';
|
||||
const socket = net.createConnection({ host: '127.0.0.1', port: 18789 });
|
||||
const timeout = setTimeout(() => {
|
||||
socket.destroy();
|
||||
process.exit(1);
|
||||
}, 500);
|
||||
socket.on('connect', () => {
|
||||
clearTimeout(timeout);
|
||||
socket.end();
|
||||
process.exit(0);
|
||||
});
|
||||
socket.on('error', () => {
|
||||
clearTimeout(timeout);
|
||||
process.exit(1);
|
||||
});
|
||||
" >/dev/null 2>&1; then
|
||||
if openclaw_e2e_probe_tcp 127.0.0.1 18789 500 >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if [ -f /tmp/gateway-e2e.log ] && grep -E -q "listening on ws://[^ ]+:18789" /tmp/gateway-e2e.log; then
|
||||
@@ -158,11 +114,7 @@ TRASH
|
||||
}
|
||||
|
||||
stop_gateway() {
|
||||
local gw_pid="$1"
|
||||
if [ -n "$gw_pid" ]; then
|
||||
kill "$gw_pid" 2>/dev/null || true
|
||||
wait "$gw_pid" || true
|
||||
fi
|
||||
openclaw_e2e_stop_process "$1"
|
||||
}
|
||||
|
||||
run_wizard_cmd() {
|
||||
@@ -229,32 +181,6 @@ TRASH
|
||||
openclaw_test_state_create "$state_ref" empty
|
||||
}
|
||||
|
||||
assert_file() {
|
||||
local file_path="$1"
|
||||
if [ ! -f "$file_path" ]; then
|
||||
echo "Missing file: $file_path"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_dir() {
|
||||
local dir_path="$1"
|
||||
if [ ! -d "$dir_path" ]; then
|
||||
echo "Missing dir: $dir_path"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_case_logged() {
|
||||
local label="$1"
|
||||
shift
|
||||
local log_path="/tmp/openclaw-onboard-${label}.log"
|
||||
if ! "$@" >"$log_path" 2>&1; then
|
||||
cat "$log_path"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
select_skip_hooks() {
|
||||
# Hooks multiselect: pick "Skip for now".
|
||||
wait_for_log "Enable hooks?" 60
|
||||
@@ -306,7 +232,7 @@ TRASH
|
||||
|
||||
run_case_local_basic() {
|
||||
set_isolated_openclaw_env local-basic
|
||||
run_case_logged local-basic node "$OPENCLAW_ENTRY" onboard \
|
||||
openclaw_e2e_run_logged local-basic node "$OPENCLAW_ENTRY" onboard \
|
||||
--non-interactive \
|
||||
--accept-risk \
|
||||
--flow quickstart \
|
||||
@@ -322,10 +248,10 @@ TRASH
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
sessions_dir="$OPENCLAW_STATE_DIR/agents/main/sessions"
|
||||
|
||||
assert_file "$config_path"
|
||||
assert_dir "$sessions_dir"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
openclaw_e2e_assert_dir "$sessions_dir"
|
||||
for file in AGENTS.md BOOTSTRAP.md IDENTITY.md SOUL.md TOOLS.md USER.md; do
|
||||
assert_file "$workspace_dir/$file"
|
||||
openclaw_e2e_assert_file "$workspace_dir/$file"
|
||||
done
|
||||
|
||||
CONFIG_PATH="$config_path" WORKSPACE_DIR="$workspace_dir" node --input-type=module - <<'"'"'NODE'"'"'
|
||||
@@ -380,7 +306,7 @@ NODE
|
||||
run_case_remote_non_interactive() {
|
||||
set_isolated_openclaw_env remote-non-interactive
|
||||
# Smoke test non-interactive remote config write.
|
||||
run_case_logged remote-non-interactive node "$OPENCLAW_ENTRY" onboard --non-interactive --accept-risk \
|
||||
openclaw_e2e_run_logged remote-non-interactive node "$OPENCLAW_ENTRY" onboard --non-interactive --accept-risk \
|
||||
--mode remote \
|
||||
--remote-url ws://gateway.local:18789 \
|
||||
--remote-token remote-token \
|
||||
@@ -388,7 +314,7 @@ NODE
|
||||
--skip-health
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
assert_file "$config_path"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
CONFIG_PATH="$config_path" node --input-type=module - <<'"'"'NODE'"'"'
|
||||
import fs from "node:fs";
|
||||
@@ -431,7 +357,7 @@ NODE
|
||||
}
|
||||
JSON
|
||||
|
||||
run_case_logged reset-config node "$OPENCLAW_ENTRY" onboard \
|
||||
openclaw_e2e_run_logged reset-config node "$OPENCLAW_ENTRY" onboard \
|
||||
--non-interactive \
|
||||
--accept-risk \
|
||||
--flow quickstart \
|
||||
@@ -444,7 +370,7 @@ JSON
|
||||
--skip-health
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
assert_file "$config_path"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
CONFIG_PATH="$config_path" node --input-type=module - <<'"'"'NODE'"'"'
|
||||
import fs from "node:fs";
|
||||
@@ -475,7 +401,7 @@ NODE
|
||||
run_wizard_cmd channels channels "node \"$OPENCLAW_ENTRY\" configure --section channels" send_channels_flow
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
assert_file "$config_path"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
CONFIG_PATH="$config_path" node --input-type=module - <<'"'"'NODE'"'"'
|
||||
import fs from "node:fs";
|
||||
@@ -526,7 +452,7 @@ JSON
|
||||
run_wizard_cmd skills "$home_dir" "node \"$OPENCLAW_ENTRY\" configure --section skills" send_skills_flow
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
assert_file "$config_path"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
CONFIG_PATH="$config_path" node --input-type=module - <<'"'"'NODE'"'"'
|
||||
import fs from "node:fs";
|
||||
@@ -552,18 +478,9 @@ if (errors.length > 0) {
|
||||
NODE
|
||||
}
|
||||
|
||||
assert_log_not_contains() {
|
||||
local file_path="$1"
|
||||
local needle="$2"
|
||||
if grep -q "$needle" "$file_path"; then
|
||||
echo "Unexpected log output: $needle"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_local_basic_log() {
|
||||
local log_path="$1"
|
||||
assert_log_not_contains "$log_path" "systemctl --user unavailable"
|
||||
openclaw_e2e_assert_log_not_contains "$log_path" "systemctl --user unavailable"
|
||||
}
|
||||
|
||||
run_case_local_basic
|
||||
|
||||
@@ -20,6 +20,24 @@ openclaw_e2e_write_state_env() {
|
||||
printf 'export PI_CODING_AGENT_DIR=%q\n' "${PI_CODING_AGENT_DIR-}"
|
||||
} >"$target"
|
||||
}
|
||||
openclaw_e2e_install_trash_shim() {
|
||||
export PATH="/tmp/openclaw-bin:$PATH"
|
||||
mkdir -p /tmp/openclaw-bin
|
||||
cat >/tmp/openclaw-bin/trash <<'TRASH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
trash_dir="$HOME/.Trash"
|
||||
mkdir -p "$trash_dir"
|
||||
for target in "$@"; do
|
||||
[ -e "$target" ] || continue
|
||||
base="$(basename "$target")"
|
||||
dest="$trash_dir/$base"
|
||||
[ -e "$dest" ] && dest="$trash_dir/${base}-$(date +%s)-$$"
|
||||
mv "$target" "$dest"
|
||||
done
|
||||
TRASH
|
||||
chmod +x /tmp/openclaw-bin/trash
|
||||
}
|
||||
openclaw_e2e_stop_process() {
|
||||
local pid="${1:-}" _
|
||||
[ -n "$pid" ] || return 0
|
||||
@@ -71,6 +89,16 @@ openclaw_e2e_probe_tcp() {
|
||||
openclaw_e2e_probe_http_status() {
|
||||
node -e 'fetch(process.argv[1]).then(r=>process.exit(r.status===Number(process.argv[2])?0:1)).catch(()=>process.exit(1))' "$1" "${2:-200}"
|
||||
}
|
||||
openclaw_e2e_assert_file() { [ -f "$1" ] || { echo "Missing file: $1"; exit 1; }; }
|
||||
openclaw_e2e_assert_dir() { [ -d "$1" ] || { echo "Missing dir: $1"; exit 1; }; }
|
||||
openclaw_e2e_assert_log_not_contains() {
|
||||
! grep -q "$2" "$1" || { echo "Unexpected log output: $2"; exit 1; }
|
||||
}
|
||||
openclaw_e2e_run_logged() {
|
||||
local label="$1" log_path="/tmp/openclaw-onboard-${1}.log"
|
||||
shift
|
||||
"$@" >"$log_path" 2>&1 || { cat "$log_path"; exit 1; }
|
||||
}
|
||||
openclaw_e2e_dump_logs() {
|
||||
local path
|
||||
for path in "$@"; do
|
||||
|
||||
Reference in New Issue
Block a user