refactor(e2e): remove stale proof scripts (#112776)

This commit is contained in:
Peter Steinberger
2026-07-22 15:31:36 -07:00
committed by GitHub
parent c4bd3f5b2e
commit 62c148d7d0
14 changed files with 1 additions and 2530 deletions
-2
View File
@@ -44,8 +44,6 @@ const repositoryScriptEntries = [
"scripts/e2e/lib/openai-chat-tools/client.mjs!",
"scripts/e2e/lib/openai-chat-tools/write-config.mjs!",
"scripts/e2e/lib/package-git-fixture.mjs!",
"scripts/e2e/lib/parallels-package/build-info-commit.mjs!",
"scripts/e2e/lib/parallels-package/log-progress-extract.mjs!",
"scripts/e2e/lib/plugin-lifecycle-matrix/measure.mjs!",
"scripts/e2e/lib/plugin-update/registry-server.mjs!",
"scripts/e2e/lib/plugins/npm-registry-server.mjs!",
-324
View File
@@ -1,324 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Definition:
# Docker/package E2E proof for local channel plugin trust gating. The host
# mode builds or reuses the functional Docker image, then runs the container
# mode against the installed OpenClaw package.
#
# Parameters:
# --container: run the in-container scenario. Host mode is the default.
# OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_IMAGE: override the Docker image name.
# OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_SKIP_BUILD=1: reuse/pull the image.
#
# Outputs:
# stdout logs each case and prints "Channel plugin trust Docker E2E passed."
# Exit 0 means both representative package-environment cases passed.
# Exit non-zero means the package build, Docker run, or trust assertion failed.
usage() {
cat <<'EOF'
Usage:
bash scripts/e2e/channel-plugin-trust-docker.sh [--container]
Description:
Proves the packaged OpenClaw CLI enforces local channel plugin trust for
plugins.load.paths entries in a clean Docker/package environment.
Options:
--container Run the in-container scenario. Used by the host wrapper.
-h, --help Show this help.
Environment:
OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_IMAGE Override Docker image name.
OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_SKIP_BUILD Reuse/pull image instead of building.
OPENCLAW_TEST_STATE_SCRIPT_B64 Required in --container mode.
Outputs:
Prints case progress and PASS lines to stdout. Exits non-zero on assertion
failure and leaves the failing command output in the container log.
Examples:
bash scripts/e2e/channel-plugin-trust-docker.sh
OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_SKIP_BUILD=1 bash scripts/e2e/channel-plugin-trust-docker.sh
EOF
}
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
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
}
write_load_paths_fixture() {
local plugin_dir="${1:?missing plugin dir}"
local origin="${2:?missing origin}"
local plugin_id="e2e-load-paths-shadow"
local channel_id="e2e-load-paths"
mkdir -p "$plugin_dir"
cat >"$plugin_dir/package.json" <<EOF
{
"name": "@openclaw-e2e/$plugin_id",
"version": "0.0.0-e2e",
"private": true,
"openclaw": {
"extensions": ["./index.cjs"],
"setupEntry": "./setup-entry.cjs",
"channel": {
"id": "$channel_id",
"label": "E2E Load Paths",
"selectionLabel": "E2E Load Paths",
"docsPath": "/channels/$channel_id",
"blurb": "Docker E2E local trust fixture."
}
}
}
EOF
cat >"$plugin_dir/openclaw.plugin.json" <<EOF
{
"id": "$plugin_id",
"name": "E2E load-paths Shadow",
"description": "Docker E2E local trust fixture.",
"activation": { "onStartup": false },
"channels": ["$channel_id"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
EOF
cat >"$plugin_dir/index.cjs" <<EOF
const fs = require("node:fs");
const path = require("node:path");
const importMarker = process.env.PLUGINTRUST_IMPORT_MARKER;
const registerMarker = process.env.PLUGINTRUST_REGISTER_MARKER;
const canary = process.env.PLUGINTRUST_CANARY ?? "<no-canary>";
function writeMarker(target, payload) {
if (!target) return;
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, payload, "utf8");
}
writeMarker(importMarker, "imported|origin=$origin|canary=" + canary + "\\n");
module.exports = {
id: "$plugin_id",
register(api) {
writeMarker(registerMarker, "registered|origin=$origin|canary=" + canary + "\\n");
api.registerChannel({
plugin: {
id: "$channel_id",
meta: {
id: "$channel_id",
label: "E2E Load Paths",
selectionLabel: "E2E Load Paths",
docsPath: "/channels/$channel_id",
blurb: "Docker E2E local trust fixture.",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
},
});
},
};
EOF
cat >"$plugin_dir/setup-entry.cjs" <<EOF
const fs = require("node:fs");
const path = require("node:path");
const importMarker = process.env.PLUGINTRUST_SETUP_IMPORT_MARKER;
const registerMarker = process.env.PLUGINTRUST_SETUP_REGISTER_MARKER;
const canary = process.env.PLUGINTRUST_CANARY ?? "<no-canary>";
function writeMarker(target, payload) {
if (!target) return;
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.writeFileSync(target, payload, "utf8");
}
writeMarker(importMarker, "setup-imported|origin=$origin|canary=" + canary + "\\n");
module.exports = {
plugin: {
id: "$channel_id",
meta: {
id: "$channel_id",
label: "E2E Load Paths setup",
selectionLabel: "E2E Load Paths setup",
docsPath: "/channels/$channel_id",
blurb: "Docker E2E local trust setup fixture.",
},
capabilities: { chatTypes: ["direct"] },
config: {
listAccountIds: () => [],
resolveAccount: () => ({ accountId: "default" }),
},
outbound: { deliveryMode: "direct" },
setup: {
validateInput: ({ input }) => {
writeMarker(
registerMarker,
"setup-registered|origin=$origin|canary=" + canary + "|token=" + (input?.token ?? "<no-token>") + "\\n",
);
return null;
},
applyAccountConfig: ({ cfg }) => cfg,
},
},
};
EOF
}
write_case_config() {
local plugin_dir="${1:?missing plugin dir}"
local trusted="${2:?missing trusted flag}"
local plugin_id="e2e-load-paths-shadow"
mkdir -p "$(dirname "$OPENCLAW_CONFIG_PATH")"
if [ "$trusted" = "1" ]; then
cat >"$OPENCLAW_CONFIG_PATH" <<EOF
{
"plugins": {
"enabled": true,
"allow": ["$plugin_id"],
"load": {
"paths": ["$plugin_dir"]
}
}
}
EOF
else
cat >"$OPENCLAW_CONFIG_PATH" <<EOF
{
"plugins": {
"enabled": true,
"load": {
"paths": ["$plugin_dir"]
}
}
}
EOF
fi
}
run_case() {
local case_id="${1:?missing case id}"
local trusted="${2:?missing trusted flag}"
local scratch
scratch="$(mktemp -d "/tmp/openclaw-channel-plugin-trust-$case_id.XXXXXX")"
local plugin_dir="$scratch/e2e-load-paths-shadow"
local marker_dir="$scratch/markers"
local stdout_file="$scratch/stdout.log"
local stderr_file="$scratch/stderr.log"
local canary="$case_id-canary"
mkdir -p "$marker_dir"
write_load_paths_fixture "$plugin_dir" "config"
write_case_config "$plugin_dir" "$trusted"
echo "[CASE $case_id] plugins.load.paths trusted=$trusted"
set +e
PLUGINTRUST_IMPORT_MARKER="$marker_dir/import.marker" \
PLUGINTRUST_REGISTER_MARKER="$marker_dir/register.marker" \
PLUGINTRUST_SETUP_IMPORT_MARKER="$marker_dir/setup-import.marker" \
PLUGINTRUST_SETUP_REGISTER_MARKER="$marker_dir/setup-register.marker" \
PLUGINTRUST_CANARY="$canary" \
run_openclaw channels add --channel e2e-load-paths --token "$canary" \
>"$stdout_file" 2>"$stderr_file"
local status=$?
set -e
if [ "$trusted" = "1" ] && [ "$status" -ne 0 ]; then
echo "Expected trusted case to succeed; exit=$status" >&2
cat "$stderr_file" >&2 || true
exit 1
fi
if [ "$trusted" = "1" ]; then
for marker in setup-import setup-register; do
local marker_path="$marker_dir/$marker.marker"
if [ ! -f "$marker_path" ]; then
echo "Expected $marker marker for trusted case" >&2
cat "$stderr_file" >&2 || true
exit 1
fi
if ! grep -qF "canary=$canary" "$marker_path"; then
echo "$marker marker did not include canary $canary" >&2
cat "$marker_path" >&2 || true
exit 1
fi
done
echo "PASS: $case_id trusted load-paths setup entry executed"
else
for marker in setup-import setup-register import register; do
if [ -e "$marker_dir/$marker.marker" ]; then
echo "Expected $marker marker to be absent for untrusted case" >&2
cat "$marker_dir/$marker.marker" >&2 || true
exit 1
fi
done
echo "PASS: $case_id untrusted load-paths setup entry blocked"
fi
}
run_container() {
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 OPENCLAW_WORKSPACE_DIR="$HOME/.openclaw/workspace"
run_openclaw --version
run_case untrusted-load-paths 0
run_case trusted-load-paths 1
echo "Channel plugin trust Docker E2E passed."
}
run_host() {
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
local image_name
image_name="$(
docker_e2e_resolve_image \
"openclaw-channel-plugin-trust-e2e:local" \
OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_IMAGE
)"
local skip_build="${OPENCLAW_CHANNEL_PLUGIN_TRUST_E2E_SKIP_BUILD:-0}"
docker_e2e_build_or_reuse "$image_name" channel-plugin-trust "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" "" "$skip_build"
local state_script_b64
state_script_b64="$(docker_e2e_test_state_shell_b64 channel-plugin-trust minimal)"
echo "Running channel plugin trust Docker E2E..."
docker_e2e_run_logged_print_with_harness \
channel-plugin-trust \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$state_script_b64" \
"$image_name" \
bash scripts/e2e/channel-plugin-trust-docker.sh --container
}
case "${1:-}" in
-h | --help)
usage
;;
--container)
run_container
;;
"")
run_host
;;
*)
echo "Unknown argument: $1" >&2
echo >&2
usage >&2
exit 1
;;
esac
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
parallels_macos_resolve_desktop_user() {
local vm_name="$1"
local user
user="$(prlctl exec "$vm_name" /usr/bin/stat -f '%Su' /dev/console 2>/dev/null | tr -d '\r' | tail -n 1 || true)"
if [[ "$user" =~ ^[A-Za-z0-9._-]+$ && "$user" != "root" && "$user" != "loginwindow" ]]; then
printf '%s\n' "$user"
return 0
fi
prlctl exec "$vm_name" /usr/bin/dscl . -list /Users NFSHomeDirectory 2>/dev/null \
| tr -d '\r' \
| awk '$2 ~ /^\/Users\// && $1 !~ /^_/ && $1 != "Shared" && $1 != ".localized" { print $1; exit }'
}
parallels_macos_resolve_desktop_home() {
local vm_name="$1"
local user="$2"
local home
home="$(
prlctl exec "$vm_name" /usr/bin/dscl . -read "/Users/$user" NFSHomeDirectory 2>/dev/null \
| tr -d '\r' \
| awk '/NFSHomeDirectory:/ { print $2; exit }'
)"
if [[ -n "$home" ]]; then
printf '%s\n' "$home"
else
printf '/Users/%s\n' "$user"
fi
}
@@ -1,25 +0,0 @@
#!/usr/bin/env bash
parallels_package_acquire_build_lock() {
local lock_dir="$1"
local owner_pid=""
while ! mkdir "$lock_dir" 2>/dev/null; do
if [[ -f "$lock_dir/pid" ]]; then
owner_pid="$(cat "$lock_dir/pid" 2>/dev/null || true)"
if [[ -n "$owner_pid" ]] && ! kill -0 "$owner_pid" >/dev/null 2>&1; then
printf 'warn: Removing stale Parallels build lock\n' >&2
rm -rf "$lock_dir"
continue
fi
fi
sleep 1
done
printf '%s\n' "$$" >"$lock_dir/pid"
}
parallels_package_release_build_lock() {
local lock_dir="$1"
if [[ -d "$lock_dir" ]]; then
rm -rf "$lock_dir"
fi
}
@@ -1,10 +0,0 @@
// Validates build-info commit metadata for Parallels package E2E scenarios.
import fs from "node:fs";
const path = "dist/build-info.json";
if (!fs.existsSync(path)) {
console.log("");
} else {
const buildInfo = JSON.parse(fs.readFileSync(path, "utf8"));
console.log(buildInfo.commit ?? "");
}
@@ -1,22 +0,0 @@
// Extracts progress markers from Parallels package E2E logs.
import fs from "node:fs";
import { readTextFileTail } from "../text-file-utils.mjs";
const LOG_PROGRESS_TAIL_BYTES = 256 * 1024;
const [logPath] = process.argv.slice(2);
if (!logPath || !fs.existsSync(logPath)) {
console.log("");
process.exit(0);
}
const text = readTextFileTail(logPath, LOG_PROGRESS_TAIL_BYTES);
const lines = text
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);
const reversed = lines.toReversed();
const progress = reversed.find((line) => line.startsWith("==> "));
const warning = reversed.find((line) => line.startsWith("warn:") || line.startsWith("error:"));
console.log(progress?.slice(4).trim() ?? warning ?? lines.at(-1)?.slice(0, 240) ?? "");
-23
View File
@@ -1,23 +0,0 @@
export type PondGatewayRpcOptions = {
url: string;
token: string;
scopes: string[];
openTimeoutMs?: number;
webSocketFactory?: (target: string) => unknown;
};
export type PondGatewayRpcRequestOptions = {
expectFinal?: boolean;
timeoutMs?: number;
};
export declare class PondGatewayRpc {
constructor(options: PondGatewayRpcOptions);
connect(): Promise<void>;
request(
method: string,
params?: unknown,
options?: PondGatewayRpcRequestOptions,
): Promise<unknown>;
close(): void;
}
-158
View File
@@ -1,158 +0,0 @@
// Gateway RPC client used by the Pond E2E verifier.
import { WebSocket } from "ws";
import { waitForWebSocketOpen } from "./websocket-open.mjs";
function asError(value) {
return value instanceof Error ? value : new Error(String(value));
}
function formatCloseReason(code, reason) {
const text = reason instanceof Uint8Array ? Buffer.from(reason).toString("utf8") : String(reason);
return text ? ` (${code}): ${text}` : ` (${code})`;
}
export class PondGatewayRpc {
constructor({
url,
token,
scopes,
openTimeoutMs = 15_000,
webSocketFactory = (target) => new WebSocket(target),
}) {
this.url = url;
this.token = token;
this.scopes = scopes;
this.openTimeoutMs = openTimeoutMs;
this.webSocketFactory = webSocketFactory;
this.pending = new Map();
this.nextId = 1;
}
async connect() {
this.ws = this.webSocketFactory(this.url);
this.ws.on("message", (data) => this.onMessage(data));
// These listeners outlive the open wait so post-handshake failures reject RPCs
// instead of surfacing as uncaught EventEmitter errors.
this.ws.on("error", (error) => this.rejectPending(asError(error)));
this.ws.on("close", (code, reason) => {
this.rejectPending(new Error(`Gateway socket closed${formatCloseReason(code, reason)}`));
});
try {
await waitForWebSocketOpen(
this.ws,
this.openTimeoutMs,
`Gateway connect timeout: ${this.url}`,
);
await this.request("connect", {
minProtocol: 1,
maxProtocol: 99,
client: {
id: "gateway-client",
displayName: "Pond proof verifier",
version: "0.0.0",
platform: process.platform,
mode: "backend",
},
auth: { token: this.token },
role: "operator",
scopes: this.scopes,
});
} catch (error) {
// Callers only receive the client after connect succeeds, so failed setup
// must close its socket here or the E2E process can remain alive.
this.close();
throw error;
}
}
rejectPending(error) {
this.terminalError ??= error;
for (const pending of this.pending.values()) {
clearTimeout(pending.timer);
pending.reject(this.terminalError);
}
this.pending.clear();
}
onMessage(data) {
let frame;
try {
frame = JSON.parse(String(data));
} catch {
return;
}
if (frame?.type !== "res" || typeof frame.id !== "string") {
return;
}
const pending = this.pending.get(frame.id);
if (!pending) {
return;
}
if (pending.expectFinal && frame.payload?.status === "accepted") {
return;
}
this.pending.delete(frame.id);
clearTimeout(pending.timer);
if (frame.ok) {
pending.resolve(frame.payload);
return;
}
pending.reject(new Error(frame.error?.message ?? `Gateway RPC failed: ${pending.method}`));
}
request(method, params = {}, options = {}) {
if (this.terminalError) {
return Promise.reject(asError(this.terminalError));
}
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
return Promise.reject(new Error(`Gateway socket is not open for RPC: ${method}`));
}
const id = `pond-proof-${this.nextId}`;
this.nextId += 1;
const timeoutMs = options.timeoutMs ?? 30_000;
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
this.pending.delete(id);
reject(new Error(`Gateway RPC timeout: ${method}`));
}, timeoutMs);
this.pending.set(id, {
method,
expectFinal: options.expectFinal === true,
resolve,
reject,
timer,
});
try {
this.ws.send(JSON.stringify({ type: "req", id, method, params }), (error) => {
if (!error) {
return;
}
const pending = this.pending.get(id);
if (!pending) {
return;
}
this.pending.delete(id);
clearTimeout(pending.timer);
pending.reject(asError(error));
});
} catch (error) {
this.pending.delete(id);
clearTimeout(timer);
reject(asError(error));
}
});
}
close() {
this.rejectPending(new Error("Gateway RPC client closed"));
if (this.ws?.readyState === WebSocket.CONNECTING) {
this.ws.terminate();
return;
}
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.close();
}
}
}
File diff suppressed because it is too large Load Diff
+1 -13
View File
@@ -1737,10 +1737,6 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
"test/scripts/e2e-helper-env-limits.test.ts",
],
],
[
"scripts/e2e/channel-plugin-trust-docker.sh",
["test/scripts/docker-build-helper.test.ts", "test/scripts/test-projects.test.ts"],
],
[
"scripts/e2e/config-reload-source-docker.sh",
[
@@ -3671,18 +3667,10 @@ function resolveK8sManifestTargets(changedPath) {
function resolveParallelsToolingTestTargets(changedPath) {
if (
!/^scripts\/e2e\/parallels\/[^/]+\.ts$/u.test(changedPath) &&
!/^scripts\/e2e\/parallels-(?:linux|macos|npm-update|windows)-smoke\.sh$/u.test(changedPath) &&
!/^scripts\/e2e\/lib\/parallels-package\/build-info-commit\.mjs$/u.test(changedPath) &&
!/^scripts\/e2e\/lib\/parallels-(?:macos|package)-common\.sh$/u.test(changedPath)
!/^scripts\/e2e\/parallels-(?:linux|macos|npm-update|windows)-smoke\.sh$/u.test(changedPath)
) {
return null;
}
if (
/^scripts\/e2e\/lib\/parallels-package\/build-info-commit\.mjs$/u.test(changedPath) ||
/^scripts\/e2e\/lib\/parallels-(?:macos|package)-common\.sh$/u.test(changedPath)
) {
return ["test/scripts/parallels-lib-helpers.test.ts"];
}
const targets = ["test/scripts/parallels-smoke-model.test.ts"];
if (
[
-116
View File
@@ -1,116 +0,0 @@
import { spawnSync } from "node:child_process";
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
const BUILD_INFO_COMMIT_SCRIPT = path.resolve(
"scripts/e2e/lib/parallels-package/build-info-commit.mjs",
);
const tempDirs: string[] = [];
afterEach(() => {
cleanupTempDirs(tempDirs);
});
function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`;
}
function runBash(script: string, env: NodeJS.ProcessEnv = {}) {
return spawnSync("/bin/bash", ["-c", script], {
cwd: process.cwd(),
encoding: "utf8",
env: { ...process.env, ...env },
});
}
describe("Parallels lib helpers", () => {
it("reads build-info commit metadata from the current package cwd", () => {
const root = makeTempDir(tempDirs, "openclaw-parallels-build-info-");
const missingResult = spawnSync(process.execPath, [BUILD_INFO_COMMIT_SCRIPT], {
cwd: root,
encoding: "utf8",
env: { ...process.env },
});
expect(missingResult.status).toBe(0);
expect(missingResult.stdout).toBe("\n");
mkdirSync(path.join(root, "dist"));
writeFileSync(
path.join(root, "dist", "build-info.json"),
`${JSON.stringify({ commit: "abc123" })}\n`,
);
const result = spawnSync(process.execPath, [BUILD_INFO_COMMIT_SCRIPT], {
cwd: root,
encoding: "utf8",
env: { ...process.env },
});
expect(result.status).toBe(0);
expect(result.stdout).toBe("abc123\n");
});
it("reclaims stale package shell locks and releases current locks", () => {
const root = makeTempDir(tempDirs, "openclaw-parallels-package-lock-");
const lockDir = path.join(root, "build.lock");
const result = runBash(`
set -euo pipefail
source scripts/e2e/lib/parallels-package-common.sh
lock_dir=${shellQuote(lockDir)}
mkdir -p "$lock_dir"
printf '%s\\n' 999999999 >"$lock_dir/pid"
parallels_package_acquire_build_lock "$lock_dir"
owner="$(cat "$lock_dir/pid")"
parallels_package_release_build_lock "$lock_dir"
printf 'owner=%s exists=%s\\n' "$owner" "$([[ -e "$lock_dir" ]] && echo yes || echo no)"
`);
expect(result.status).toBe(0);
expect(result.stderr).toContain("warn: Removing stale Parallels build lock");
expect(result.stdout).toMatch(/^owner=\d+ exists=no\n$/u);
expect(result.stdout).not.toContain("owner=999999999");
});
it("resolves macOS desktop users through prlctl fallbacks", () => {
const root = makeTempDir(tempDirs, "openclaw-parallels-macos-common-");
const binDir = path.join(root, "bin");
const macHome = `${"/"}Users/alice`;
mkdirSync(binDir);
const prlctlShim = path.join(binDir, "prlctl");
writeFileSync(
prlctlShim,
`#!/usr/bin/env bash
args="$*"
if [[ "$args" == *"/usr/bin/stat -f %Su /dev/console"* ]]; then
printf 'loginwindow\\r\\n'
exit 0
fi
if [[ "$args" == *"/usr/bin/dscl . -list /Users NFSHomeDirectory"* ]]; then
printf '_daemon /var/root\\r\\nShared %s\\r\\nalice %s\\r\\n' "${`${"/"}Users/Shared`}" "${macHome}"
exit 0
fi
if [[ "$args" == *"-read ${macHome} NFSHomeDirectory"* ]]; then
printf 'NFSHomeDirectory: %s\\r\\n' "${macHome}"
exit 0
fi
exit 1
`,
);
chmodSync(prlctlShim, 0o755);
const result = runBash(
`
set -euo pipefail
source scripts/e2e/lib/parallels-macos-common.sh
printf 'user=%s\\n' "$(parallels_macos_resolve_desktop_user macos-vm)"
printf 'home=%s\\n' "$(parallels_macos_resolve_desktop_home macos-vm alice)"
`,
{ PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}` },
);
expect(result.status).toBe(0);
expect(result.stdout).toBe(`user=alice\nhome=${macHome}\n`);
});
});
@@ -1,51 +0,0 @@
// Parallels Package Log Progress Extract tests cover parallels package log progress extract script behavior.
import { spawnSync } from "node:child_process";
import { writeFileSync } from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
const SCRIPT_PATH = "scripts/e2e/lib/parallels-package/log-progress-extract.mjs";
const tempRoots = useAutoCleanupTempDirTracker(afterEach);
function makeTempRoot(): string {
return tempRoots.make("openclaw-parallels-progress-");
}
function runExtract(logPath?: string) {
return spawnSync(process.execPath, [SCRIPT_PATH, ...(logPath ? [logPath] : [])], {
encoding: "utf8",
});
}
describe("parallels package log progress extractor", () => {
it("prints a blank status when the log is absent", () => {
const result = runExtract(path.join(makeTempRoot(), "missing.log"));
expect(result.status).toBe(0);
expect(result.stdout).toBe("\n");
});
it("extracts the latest progress line from recent log output", () => {
const logPath = path.join(makeTempRoot(), "phase.log");
writeFileSync(logPath, "==> Build package\nwarn: transient\n==> Copy artifact\n");
const result = runExtract(logPath);
expect(result.status).toBe(0);
expect(result.stdout).toBe("Copy artifact\n");
});
it("does not let stale progress hide recent warnings in long logs", () => {
const logPath = path.join(makeTempRoot(), "phase.log");
writeFileSync(
logPath,
`==> Stale build step\n${"ordinary output\n".repeat(24 * 1024)}warn: recent package issue\n`,
);
const result = runExtract(logPath);
expect(result.status).toBe(0);
expect(result.stdout).toBe("warn: recent package issue\n");
});
});
-125
View File
@@ -1,125 +0,0 @@
import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";
import { PondGatewayRpc } from "../../scripts/e2e/lib/pond-gateway-rpc.mjs";
const CONNECTING = 0;
const OPEN = 1;
const CLOSED = 3;
class FakeWebSocket extends EventEmitter {
readyState = CONNECTING;
sent: Array<{ id: string; method: string }> = [];
terminated = false;
sendError: Error | undefined;
respondToConnect = true;
open(): void {
this.readyState = OPEN;
this.emit("open");
}
send(payload: string, callback: (error?: Error) => void): void {
const frame = JSON.parse(payload) as { id: string; method: string };
this.sent.push(frame);
callback(this.sendError);
if (frame.method === "connect" && !this.sendError && this.respondToConnect) {
queueMicrotask(() => {
this.emit("message", JSON.stringify({ type: "res", id: frame.id, ok: true }));
});
}
}
close(): void {
this.readyState = CLOSED;
this.emit("close", 1000, Buffer.alloc(0));
}
terminate(): void {
this.terminated = true;
this.readyState = CLOSED;
this.emit("close", 1006, Buffer.alloc(0));
}
}
function createRpc(socket: FakeWebSocket, openTimeoutMs = 100) {
return new PondGatewayRpc({
url: "ws://127.0.0.1:18789",
token: String(),
scopes: ["operator.read"],
openTimeoutMs,
webSocketFactory: () => socket,
});
}
async function connect(rpc: PondGatewayRpc, socket: FakeWebSocket): Promise<void> {
const connecting = rpc.connect();
socket.open();
await connecting;
}
describe("Pond gateway RPC", () => {
it("terminates a stalled websocket handshake at the connection deadline", async () => {
const socket = new FakeWebSocket();
const rpc = createRpc(socket, 1);
const keepAlive = setTimeout(() => {}, 100);
try {
await expect(rpc.connect()).rejects.toThrow("Gateway connect timeout: ws://127.0.0.1:18789");
} finally {
clearTimeout(keepAlive);
}
expect(socket.terminated).toBe(true);
});
it("closes the websocket when the gateway connect RPC stalls", async () => {
vi.useFakeTimers();
try {
const socket = new FakeWebSocket();
socket.respondToConnect = false;
const rpc = createRpc(socket);
const connecting = rpc.connect();
const rejected = expect(connecting).rejects.toThrow("Gateway RPC timeout: connect");
socket.open();
await vi.advanceTimersByTimeAsync(30_000);
await rejected;
expect(socket.readyState).toBe(CLOSED);
} finally {
vi.useRealTimers();
}
});
it("rejects pending RPCs when an open websocket emits an error", async () => {
const socket = new FakeWebSocket();
const rpc = createRpc(socket);
await connect(rpc, socket);
const request = rpc.request("node.list");
socket.emit("error", new Error("invalid websocket frame"));
await expect(request).rejects.toThrow("invalid websocket frame");
await expect(rpc.request("node.list")).rejects.toThrow("invalid websocket frame");
});
it("rejects pending RPCs when an open websocket closes", async () => {
const socket = new FakeWebSocket();
const rpc = createRpc(socket);
await connect(rpc, socket);
const request = rpc.request("node.list");
socket.readyState = CLOSED;
socket.emit("close", 1006, Buffer.from("gateway stopped"));
await expect(request).rejects.toThrow("Gateway socket closed (1006): gateway stopped");
});
it("rejects an RPC when the websocket send callback reports failure", async () => {
const socket = new FakeWebSocket();
const rpc = createRpc(socket);
await connect(rpc, socket);
socket.sendError = new Error("send failed");
await expect(rpc.request("node.list")).rejects.toThrow("send failed");
});
});
-20
View File
@@ -818,10 +818,6 @@ describe("scripts/test-projects changed-target routing", () => {
"test/scripts/e2e-helper-env-limits.test.ts",
],
],
[
"scripts/e2e/channel-plugin-trust-docker.sh",
["test/scripts/docker-build-helper.test.ts", "test/scripts/test-projects.test.ts"],
],
[
"scripts/e2e/config-reload-source-docker.sh",
[
@@ -2678,9 +2674,6 @@ describe("scripts/test-projects changed-target routing", () => {
"scripts/e2e/parallels/update-job-timeout.ts",
"scripts/e2e/parallels/windows-smoke.ts",
"scripts/e2e/parallels-windows-smoke.sh",
"scripts/e2e/lib/parallels-package/build-info-commit.mjs",
"scripts/e2e/lib/parallels-macos-common.sh",
"scripts/e2e/lib/parallels-package-common.sh",
]),
).toEqual([
{
@@ -2690,7 +2683,6 @@ describe("scripts/test-projects changed-target routing", () => {
"test/scripts/parallels-smoke-model.test.ts",
"test/scripts/parallels-npm-update-smoke.test.ts",
"test/scripts/parallels-update-job-timeout.test.ts",
"test/scripts/parallels-lib-helpers.test.ts",
],
watchMode: false,
},
@@ -2704,18 +2696,6 @@ describe("scripts/test-projects changed-target routing", () => {
});
});
it("routes Parallels common shell helpers through lib helper owner tests", () => {
for (const changedPath of [
"scripts/e2e/lib/parallels-macos-common.sh",
"scripts/e2e/lib/parallels-package-common.sh",
]) {
expect(resolveChangedTestTargetPlan([changedPath]), changedPath).toEqual({
mode: "targets",
targets: ["test/scripts/parallels-lib-helpers.test.ts"],
});
}
});
it("routes MCP and cron Docker E2E script targets instead of skipping changed tests", () => {
const targets = [
"scripts/e2e/mcp-channels-docker.sh",