diff --git a/scripts/e2e/agents-delete-shared-workspace-docker.sh b/scripts/e2e/agents-delete-shared-workspace-docker.sh index 23af71022313..06b63eaec181 100644 --- a/scripts/e2e/agents-delete-shared-workspace-docker.sh +++ b/scripts/e2e/agents-delete-shared-workspace-docker.sh @@ -30,28 +30,32 @@ run_logged agents-delete-shared-workspace docker_e2e_docker_cmd run --rm \ set -euo pipefail source scripts/lib/openclaw-e2e-instance.sh -run_openclaw() { - if command -v openclaw >/dev/null 2>&1; then - openclaw "$@" - return - fi - if [ -f /app/openclaw.mjs ]; then - node /app/openclaw.mjs "$@" - return - fi - echo "openclaw CLI not found in Docker image" >&2 - exit 1 -} - openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}" export SHARED_WORKSPACE="$HOME/workspace-shared" output_file="$HOME/delete.json" -trap '\''rm -rf "$HOME"'\'' EXIT +gateway_log="$HOME/gateway.log" +gateway_pid="" + +cleanup() { + openclaw_e2e_terminate_gateways "${gateway_pid:-}" + rm -rf "$HOME" +} +dump_logs_on_error() { + local status=$? + openclaw_e2e_print_log "$gateway_log" >&2 + exit "$status" +} +trap cleanup EXIT +trap dump_logs_on_error ERR mkdir -p "$OPENCLAW_STATE_DIR" "$SHARED_WORKSPACE" node scripts/e2e/lib/fixture.mjs agents-delete-config -run_openclaw agents delete ops --force --json > "$output_file" +entry="$(openclaw_e2e_resolve_entrypoint)" +gateway_pid="$(openclaw_e2e_start_gateway "$entry" 18789 "$gateway_log")" +openclaw_e2e_wait_gateway_ready "$gateway_pid" "$gateway_log" 300 18789 + +node "$entry" agents delete ops --force --json > "$output_file" node scripts/e2e/lib/fixture.mjs agents-delete-assert "$output_file" ' diff --git a/scripts/e2e/lib/fixtures/workspace.mjs b/scripts/e2e/lib/fixtures/workspace.mjs index 7636921aaddf..2632e2434400 100644 --- a/scripts/e2e/lib/fixtures/workspace.mjs +++ b/scripts/e2e/lib/fixtures/workspace.mjs @@ -65,6 +65,7 @@ function assertAgentsDeleteResult([outputPath]) { [parsed.workspace, process.env.SHARED_WORKSPACE, "workspace"], [parsed.workspaceRetained, true, "workspaceRetained"], [parsed.workspaceRetainedReason, "shared", "workspaceRetainedReason"], + [parsed.transport, "gateway", "transport"], ]; for (const [actual, expected, label] of comparisons) { assert(actual === expected, `${label} mismatch: ${JSON.stringify(actual)}`); diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index bc19403a361f..3eec63604fbe 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -28,6 +28,8 @@ const OPENAI_WEB_SEARCH_MINIMAL_SCENARIO_PATH = "scripts/e2e/lib/openai-web-search-minimal/scenario.sh"; const OPENAI_WEB_SEARCH_MINIMAL_CLIENT_PATH = "scripts/e2e/lib/openai-web-search-minimal/client.mjs"; +const AGENTS_DELETE_SHARED_WORKSPACE_DOCKER_E2E_PATH = + "scripts/e2e/agents-delete-shared-workspace-docker.sh"; const OPENWEBUI_DOCKER_E2E_PATH = "scripts/e2e/openwebui-docker.sh"; const ONBOARD_DOCKER_E2E_PATH = "scripts/e2e/onboard-docker.sh"; const KITCHEN_SINK_PLUGIN_DOCKER_E2E_PATH = "scripts/e2e/kitchen-sink-plugin-docker.sh"; @@ -4981,6 +4983,25 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-logs.sh" expect(scenario).not.toContain('node "$entry" gateway --port "$PORT"'); }); + it("runs agents delete shared workspace smoke through one managed gateway", () => { + const runner = readFileSync(AGENTS_DELETE_SHARED_WORKSPACE_DOCKER_E2E_PATH, "utf8"); + expectTextToIncludeAll(runner, [ + 'entry="$(openclaw_e2e_resolve_entrypoint)"', + 'gateway_pid="$(openclaw_e2e_start_gateway "$entry" 18789 "$gateway_log")"', + 'openclaw_e2e_wait_gateway_ready "$gateway_pid" "$gateway_log" 300 18789', + 'node "$entry" agents delete ops --force --json > "$output_file"', + 'openclaw_e2e_terminate_gateways "${gateway_pid:-}"', + 'openclaw_e2e_print_log "$gateway_log" >&2', + "trap cleanup EXIT", + "trap dump_logs_on_error ERR", + ]); + + expect(runner.match(/openclaw_e2e_start_gateway/gu)).toHaveLength(1); + expect(runner.match(/openclaw_e2e_wait_gateway_ready/gu)).toHaveLength(1); + expect(runner).not.toContain("run_openclaw()"); + expect(runner).not.toContain("for _ in"); + }); + it("keeps OpenAI web search smoke logs isolated per run", () => { const scenario = readFileSync(OPENAI_WEB_SEARCH_MINIMAL_SCENARIO_PATH, "utf8"); expectTextToIncludeAll(scenario, [ diff --git a/test/scripts/fixtures-workspace.test.ts b/test/scripts/fixtures-workspace.test.ts index deee482c4ae8..dbc6865f4983 100644 --- a/test/scripts/fixtures-workspace.test.ts +++ b/test/scripts/fixtures-workspace.test.ts @@ -3,9 +3,11 @@ import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; +import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js"; const FIXTURE_SCRIPT = "scripts/e2e/lib/fixture.mjs"; +const tempDirs = useAutoCleanupTempDirTracker(afterEach); function runAgentsDeleteAssert(root: string, outputPath: string, env: Record = {}) { return spawnSync(process.execPath, [FIXTURE_SCRIPT, "agents-delete-assert", outputPath], { @@ -99,4 +101,40 @@ describe("workspace fixture assertions", () => { rmSync(root, { force: true, recursive: true }); } }); + + it.each([undefined, "local"])( + "rejects agents delete output without gateway transport (%s)", + (transport) => { + const root = tempDirs.make("openclaw-fixture-workspace-"); + const stateDir = path.join(root, "state"); + const workspace = path.join(root, "workspace"); + const outputPath = path.join(root, "agents-delete.json"); + try { + mkdirSync(stateDir, { recursive: true }); + mkdirSync(workspace, { recursive: true }); + writeFileSync( + path.join(stateDir, "openclaw.json"), + `${JSON.stringify({ agents: { entries: { main: { workspace } } } })}\n`, + ); + writeFileSync( + outputPath, + `${JSON.stringify({ + agentId: "ops", + workspace, + workspaceRetained: true, + workspaceRetainedReason: "shared", + workspaceSharedWith: ["main"], + ...(transport ? { transport } : {}), + })}\n`, + ); + + const result = runAgentsDeleteAssert(root, outputPath); + + expect(result.status).not.toBe(0); + expect(result.stderr).toContain("transport mismatch"); + } finally { + rmSync(root, { force: true, recursive: true }); + } + }, + ); });