From d22a851253b0225e72847dc6b1a7b74d8035e214 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Apr 2026 19:47:03 +0100 Subject: [PATCH] test: reuse Docker test-state in core E2E lanes --- scripts/e2e/browser-cdp-snapshot-docker.sh | 16 +++++- scripts/e2e/config-reload-source-docker.sh | 23 +++++--- scripts/e2e/crestodian-first-run-docker.sh | 5 +- scripts/e2e/crestodian-planner-docker.sh | 5 +- scripts/e2e/crestodian-rescue-docker.sh | 5 +- scripts/e2e/cron-mcp-cleanup-docker.sh | 5 +- scripts/e2e/mcp-channels-docker.sh | 5 +- scripts/e2e/pi-bundle-mcp-tools-docker.sh | 4 +- scripts/e2e/plugin-update-unchanged-docker.sh | 3 ++ scripts/lib/docker-e2e-scenarios.mjs | 36 ++++++++++--- test/scripts/docker-build-helper.test.ts | 6 ++- test/scripts/docker-e2e-plan.test.ts | 52 +++++++++++++++++++ 12 files changed, 136 insertions(+), 29 deletions(-) diff --git a/scripts/e2e/browser-cdp-snapshot-docker.sh b/scripts/e2e/browser-cdp-snapshot-docker.sh index 543f9cebff8..9953ad13970 100755 --- a/scripts/e2e/browser-cdp-snapshot-docker.sh +++ b/scripts/e2e/browser-cdp-snapshot-docker.sh @@ -41,6 +41,7 @@ EOF echo "Building Docker image: $IMAGE_NAME" docker_build_run browser-cdp-snapshot-build -t "$IMAGE_NAME" -f "$build_dir/Dockerfile" "$build_dir" fi +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 browser-cdp-snapshot empty)" echo "Starting browser CDP snapshot container..." docker_cmd docker run -d \ @@ -53,13 +54,23 @@ docker_cmd docker run -d \ -e OPENCLAW_SKIP_GMAIL_WATCHER=1 \ -e OPENCLAW_SKIP_CRON=1 \ -e OPENCLAW_SKIP_CANVAS_HOST=1 \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail +eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" +{ + printf 'export HOME=%q\n' \"\$HOME\" + printf 'export OPENCLAW_HOME=%q\n' \"\$OPENCLAW_HOME\" + printf 'export OPENCLAW_STATE_DIR=%q\n' \"\$OPENCLAW_STATE_DIR\" + printf 'export OPENCLAW_CONFIG_PATH=%q\n' \"\$OPENCLAW_CONFIG_PATH\" + printf 'export OPENCLAW_AGENT_DIR=%q\n' \"\${OPENCLAW_AGENT_DIR-}\" + printf 'export PI_CODING_AGENT_DIR=%q\n' \"\${PI_CODING_AGENT_DIR-}\" +} >/tmp/openclaw-test-state-env entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js -mkdir -p \"\$HOME/.openclaw\" /tmp/openclaw-browser-cdp/chrome +mkdir -p /tmp/openclaw-browser-cdp/chrome find dist -maxdepth 1 -type f -name 'pw-ai-*.js' ! -name 'pw-ai-state-*' -exec mv {} /tmp/openclaw-browser-cdp/ \; -cat > \"\$HOME/.openclaw/openclaw.json\" <<'JSON' +cat > \"\$OPENCLAW_CONFIG_PATH\" <<'JSON' { \"gateway\": { \"port\": $PORT, @@ -143,6 +154,7 @@ fi echo "Running browser CDP snapshot smoke..." docker_cmd docker exec "$CONTAINER_NAME" bash -lc " set -euo pipefail +source /tmp/openclaw-test-state-env entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js base_args=(--url ws://127.0.0.1:$PORT --token '$TOKEN') diff --git a/scripts/e2e/config-reload-source-docker.sh b/scripts/e2e/config-reload-source-docker.sh index 9421c72edd3..e12ff8afdc8 100755 --- a/scripts/e2e/config-reload-source-docker.sh +++ b/scripts/e2e/config-reload-source-docker.sh @@ -16,6 +16,7 @@ cleanup() { trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" config-reload "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$SKIP_BUILD" +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 config-reload empty)" echo "Starting gateway container..." docker run -d \ @@ -27,12 +28,21 @@ docker run -d \ -e OPENCLAW_SKIP_GMAIL_WATCHER=1 \ -e OPENCLAW_SKIP_CRON=1 \ -e OPENCLAW_SKIP_CANVAS_HOST=1 \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail +eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" +{ + printf 'export HOME=%q\n' \"\$HOME\" + printf 'export OPENCLAW_HOME=%q\n' \"\$OPENCLAW_HOME\" + printf 'export OPENCLAW_STATE_DIR=%q\n' \"\$OPENCLAW_STATE_DIR\" + printf 'export OPENCLAW_CONFIG_PATH=%q\n' \"\$OPENCLAW_CONFIG_PATH\" + printf 'export OPENCLAW_AGENT_DIR=%q\n' \"\${OPENCLAW_AGENT_DIR-}\" + printf 'export PI_CODING_AGENT_DIR=%q\n' \"\${PI_CODING_AGENT_DIR-}\" +} >/tmp/openclaw-test-state-env entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js -mkdir -p \"\$HOME/.openclaw\" -cat > \"\$HOME/.openclaw/openclaw.json\" <<'JSON' +cat > \"\$OPENCLAW_CONFIG_PATH\" <<'JSON' { \"gateway\": { \"port\": $PORT, @@ -95,18 +105,18 @@ fi echo "Checking initial RPC status..." docker exec "$CONTAINER_NAME" bash -lc " +source /tmp/openclaw-test-state-env entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js node \"\$entry\" gateway status --url ws://127.0.0.1:$PORT --token '$TOKEN' --require-rpc --timeout 30000 >/tmp/config-reload-status-before.log " echo "Mutating hot-reload gateway metadata..." -docker exec "$CONTAINER_NAME" bash -lc "node --input-type=module - <<'NODE' +docker exec "$CONTAINER_NAME" bash -lc "source /tmp/openclaw-test-state-env +node --input-type=module - <<'NODE' import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; -const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json'); +const configPath = process.env.OPENCLAW_CONFIG_PATH; const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); config.gateway.channelHealthCheckMinutes = 2; fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8'); @@ -122,6 +132,7 @@ fi echo "Checking post-write RPC status..." docker exec "$CONTAINER_NAME" bash -lc " +source /tmp/openclaw-test-state-env entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js node \"\$entry\" gateway status --url ws://127.0.0.1:$PORT --token '$TOKEN' --require-rpc --timeout 30000 >/tmp/config-reload-status-after.log diff --git a/scripts/e2e/crestodian-first-run-docker.sh b/scripts/e2e/crestodian-first-run-docker.sh index 4614a680ddb..20fc158ebe9 100644 --- a/scripts/e2e/crestodian-first-run-docker.sh +++ b/scripts/e2e/crestodian-first-run-docker.sh @@ -17,17 +17,18 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-first-run docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-first-run empty)" echo "Running in-container Crestodian first-run smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker run --rm \ --name "$CONTAINER_NAME" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ - -e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" tsx scripts/e2e/crestodian-first-run-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} diff --git a/scripts/e2e/crestodian-planner-docker.sh b/scripts/e2e/crestodian-planner-docker.sh index 559ee5161ae..c518e24299f 100755 --- a/scripts/e2e/crestodian-planner-docker.sh +++ b/scripts/e2e/crestodian-planner-docker.sh @@ -17,17 +17,18 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-planner docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-planner empty)" echo "Running in-container Crestodian planner fallback smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker run --rm \ --name "$CONTAINER_NAME" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ - -e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" tsx scripts/e2e/crestodian-planner-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} diff --git a/scripts/e2e/crestodian-rescue-docker.sh b/scripts/e2e/crestodian-rescue-docker.sh index efaa230d52e..d4de3b687ce 100755 --- a/scripts/e2e/crestodian-rescue-docker.sh +++ b/scripts/e2e/crestodian-rescue-docker.sh @@ -17,17 +17,18 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" crestodian-rescue docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 crestodian-rescue empty)" echo "Running in-container Crestodian rescue smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker run --rm \ --name "$CONTAINER_NAME" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ - -e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" tsx scripts/e2e/crestodian-rescue-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} diff --git a/scripts/e2e/cron-mcp-cleanup-docker.sh b/scripts/e2e/cron-mcp-cleanup-docker.sh index 7b579e74df7..43cba93d8e7 100644 --- a/scripts/e2e/cron-mcp-cleanup-docker.sh +++ b/scripts/e2e/cron-mcp-cleanup-docker.sh @@ -19,6 +19,7 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" cron-mcp-cleanup docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 cron-mcp-cleanup empty)" echo "Running in-container cron/subagent MCP cleanup smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. @@ -32,14 +33,14 @@ docker run --rm \ -e "OPENCLAW_SKIP_CANVAS_HOST=1" \ -e "OPENCLAW_SKIP_ACPX_RUNTIME=1" \ -e "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE=1" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ - -e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ -e "GW_URL=ws://127.0.0.1:$PORT" \ -e "GW_TOKEN=$TOKEN" \ -e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js export MOCK_PORT=44081 diff --git a/scripts/e2e/mcp-channels-docker.sh b/scripts/e2e/mcp-channels-docker.sh index 0553f8b1225..0663efade95 100644 --- a/scripts/e2e/mcp-channels-docker.sh +++ b/scripts/e2e/mcp-channels-docker.sh @@ -19,6 +19,7 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" mcp-channels docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 mcp-channels empty)" echo "Running in-container gateway + MCP smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. @@ -32,14 +33,14 @@ docker run --rm \ -e "OPENCLAW_SKIP_CANVAS_HOST=1" \ -e "OPENCLAW_SKIP_ACPX_RUNTIME=1" \ -e "OPENCLAW_SKIP_ACPX_RUNTIME_PROBE=1" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ - -e "OPENCLAW_CONFIG_PATH=/tmp/openclaw-state/openclaw.json" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ -e "GW_URL=ws://127.0.0.1:$PORT" \ -e "GW_TOKEN=$TOKEN" \ -e "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" entry=dist/index.mjs [ -f \"\$entry\" ] || entry=dist/index.js mock_port=44081 diff --git a/scripts/e2e/pi-bundle-mcp-tools-docker.sh b/scripts/e2e/pi-bundle-mcp-tools-docker.sh index a303a3f7dc5..f5bcb45b43e 100755 --- a/scripts/e2e/pi-bundle-mcp-tools-docker.sh +++ b/scripts/e2e/pi-bundle-mcp-tools-docker.sh @@ -17,16 +17,18 @@ trap cleanup EXIT docker_e2e_build_or_reuse "$IMAGE_NAME" pi-bundle-mcp-tools docker_e2e_harness_mount_args +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 pi-bundle-mcp-tools empty)" echo "Running in-container Pi bundle MCP tool availability smoke..." # Harness files are mounted read-only; the app under test comes from /app/dist. set +e docker run --rm \ --name "$CONTAINER_NAME" \ - -e "OPENCLAW_STATE_DIR=/tmp/openclaw-state" \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "${DOCKER_E2E_HARNESS_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail + eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" tsx scripts/e2e/pi-bundle-mcp-tools-docker-client.ts " >"$RUN_LOG" 2>&1 status=${PIPESTATUS[0]} diff --git a/scripts/e2e/plugin-update-unchanged-docker.sh b/scripts/e2e/plugin-update-unchanged-docker.sh index 41918a65896..803867b0286 100755 --- a/scripts/e2e/plugin-update-unchanged-docker.sh +++ b/scripts/e2e/plugin-update-unchanged-docker.sh @@ -14,15 +14,18 @@ PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz plugin-update "${OPENCLAW_CURRENT_ docker_e2e_package_mount_args "$PACKAGE_TGZ" docker_e2e_build_or_reuse "$IMAGE_NAME" plugin-update "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "bare" "$SKIP_BUILD" +OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 plugin-update empty)" echo "Running unchanged plugin update smoke..." docker run --rm \ -e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ -e OPENCLAW_SKIP_CHANNELS=1 \ -e OPENCLAW_SKIP_PROVIDERS=1 \ + -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ "$IMAGE_NAME" \ bash -lc "set -euo pipefail +eval \"\$(printf '%s' \"\${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}\" | base64 -d)\" package_tgz=\"\${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}\" npm install -g --prefix /tmp/npm-prefix \"\$package_tgz\" --no-fund --no-audit >/tmp/openclaw-install.log 2>&1 entry=\"/tmp/npm-prefix/lib/node_modules/openclaw/dist/index.mjs\" diff --git a/scripts/lib/docker-e2e-scenarios.mjs b/scripts/lib/docker-e2e-scenarios.mjs index 2831b529816..14e840bf600 100644 --- a/scripts/lib/docker-e2e-scenarios.mjs +++ b/scripts/lib/docker-e2e-scenarios.mjs @@ -248,15 +248,22 @@ export const mainLanes = [ ), serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", { resources: ["npm"], + stateScenario: "empty", weight: 3, }), - lane("pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools"), - lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue"), - lane("crestodian-planner", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-planner"), + lane("pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools", { + stateScenario: "empty", + }), + lane("crestodian-rescue", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-rescue", { + stateScenario: "empty", + }), + lane("crestodian-planner", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-planner", { + stateScenario: "empty", + }), serviceLane( "cron-mcp-cleanup", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup", - { resources: ["npm"], weight: 3 }, + { resources: ["npm"], stateScenario: "empty", weight: 3 }, ), npmLane("doctor-switch", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:doctor-switch", { stateScenario: "empty", @@ -291,8 +298,12 @@ export const mainLanes = [ "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:bundled-channel-deps:fast", { resources: ["service"], stateScenario: "empty", weight: 3 }, ), - npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update"), - serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload"), + npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", { + stateScenario: "empty", + }), + serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", { + stateScenario: "empty", + }), ...bundledScenarioLanes, lane("openai-image-auth", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-image-auth", { stateScenario: "empty", @@ -300,6 +311,7 @@ export const mainLanes = [ lane( "crestodian-first-run", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:crestodian-first-run", + { stateScenario: "empty" }, ), lane( "session-runtime-context", @@ -424,6 +436,7 @@ const releasePathPluginRuntimeLanes = [ "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup", { resources: ["npm"], + stateScenario: "empty", weight: 3, }, ), @@ -448,6 +461,7 @@ const releasePathPluginRuntimeServiceLanes = [ "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cron-mcp-cleanup", { resources: ["npm"], + stateScenario: "empty", weight: 3, }, ), @@ -464,7 +478,9 @@ const releasePathPluginRuntimeCoreLanes = [ ]; const releasePathBundledChannelLanes = [ - npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update"), + npmLane("plugin-update", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugin-update", { + stateScenario: "empty", + }), ...bundledScenarioLanes, ]; @@ -519,7 +535,9 @@ const primaryReleasePathChunks = { weight: 2, }), serviceLane("gateway-network", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:gateway-network"), - serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload"), + serviceLane("config-reload", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:config-reload", { + stateScenario: "empty", + }), lane( "session-runtime-context", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:session-runtime-context", @@ -527,9 +545,11 @@ const primaryReleasePathChunks = { lane( "pi-bundle-mcp-tools", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:pi-bundle-mcp-tools", + { stateScenario: "empty" }, ), serviceLane("mcp-channels", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels", { resources: ["npm"], + stateScenario: "empty", weight: 3, }), ], diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index fe89167b684..daa1e74bd86 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -182,8 +182,10 @@ describe("docker build helper", () => { it("keeps OpenAI web search smoke on one gateway agent connection", () => { const runner = readFileSync(OPENAI_WEB_SEARCH_MINIMAL_E2E_PATH, "utf8"); - expect(runner).toContain('"--expect-final"'); - expect(runner).toContain('[...gatewayArgs, "agent", "--params"'); + expect(runner).toContain("const callGateway = await loadCallGateway();"); + expect(runner).toContain('method: "agent"'); + expect(runner).toContain("expectFinal: true"); + expect(runner).toContain('scopes: ["operator.write"]'); expect(runner).not.toContain('"agent.wait"'); }); diff --git a/test/scripts/docker-e2e-plan.test.ts b/test/scripts/docker-e2e-plan.test.ts index a47a3881700..2e79136d16a 100644 --- a/test/scripts/docker-e2e-plan.test.ts +++ b/test/scripts/docker-e2e-plan.test.ts @@ -176,6 +176,14 @@ describe("scripts/lib/docker-e2e-plan", () => { "openai-web-search-minimal", "openwebui", ]); + expect(pluginsRuntimeServices.lanes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: "cron-mcp-cleanup", + stateScenario: "empty", + }), + ]), + ); expect(pluginsRuntimePlugins.lanes.map((lane) => lane.name)).not.toContain( "bundled-plugin-install-uninstall-0", ); @@ -203,6 +211,10 @@ describe("scripts/lib/docker-e2e-plan", () => { "bundled-channel-feishu", "bundled-channel-memory-lancedb", ]); + expect(bundledChannelsCore.lanes[0]).toMatchObject({ + name: "plugin-update", + stateScenario: "empty", + }); expect(bundledChannelsUpdateA.lanes.map((lane) => lane.name)).toEqual([ "bundled-channel-update-telegram", "bundled-channel-update-memory-lancedb", @@ -323,6 +335,14 @@ describe("scripts/lib/docker-e2e-plan", () => { "doctor-switch", "openai-image-auth", "openai-web-search-minimal", + "mcp-channels", + "cron-mcp-cleanup", + "pi-bundle-mcp-tools", + "crestodian-first-run", + "crestodian-planner", + "crestodian-rescue", + "config-reload", + "plugin-update", "plugins", "bundled-channel-deps-compat", "bundled-channel-setup-entry", @@ -352,6 +372,38 @@ describe("scripts/lib/docker-e2e-plan", () => { name: "openai-web-search-minimal", stateScenario: "empty", }), + expect.objectContaining({ + name: "mcp-channels", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "cron-mcp-cleanup", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "pi-bundle-mcp-tools", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "crestodian-first-run", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "crestodian-planner", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "crestodian-rescue", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "config-reload", + stateScenario: "empty", + }), + expect.objectContaining({ + name: "plugin-update", + stateScenario: "empty", + }), expect.objectContaining({ name: "plugins", stateScenario: "empty",