mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
refactor: extract e2e scenario fixtures
This commit is contained in:
@@ -24,34 +24,7 @@ trap cleanup EXIT
|
||||
run_with_timeout() {
|
||||
local timeout_ms="$1"
|
||||
shift
|
||||
node - "$timeout_ms" "$@" <<'NODE'
|
||||
const { spawnSync } = require("node:child_process");
|
||||
|
||||
const timeout = Number(process.argv[2]);
|
||||
const command = process.argv[3];
|
||||
const args = process.argv.slice(4);
|
||||
const result = spawnSync(command, args, {
|
||||
encoding: "utf8",
|
||||
env: process.env,
|
||||
timeout,
|
||||
});
|
||||
|
||||
if (result.stdout) {
|
||||
process.stdout.write(result.stdout);
|
||||
}
|
||||
if (result.stderr) {
|
||||
process.stderr.write(result.stderr);
|
||||
}
|
||||
if (result.error) {
|
||||
console.error(`command failed: ${command}: ${result.error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (result.signal) {
|
||||
console.error(`command terminated: ${command}: ${result.signal}`);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(result.status ?? 0);
|
||||
NODE
|
||||
node scripts/e2e/lib/bun-global-install/assertions.mjs run-with-timeout "$timeout_ms" "$@"
|
||||
}
|
||||
|
||||
restore_dist_from_image() {
|
||||
@@ -163,29 +136,7 @@ main() {
|
||||
echo "==> OpenClaw image providers through Bun global install"
|
||||
local providers_json
|
||||
providers_json="$(run_with_timeout "$COMMAND_TIMEOUT_MS" "$openclaw_bin" infer image providers --json)"
|
||||
OPENCLAW_IMAGE_PROVIDERS_JSON="$providers_json" node - <<'NODE'
|
||||
const raw = process.env.OPENCLAW_IMAGE_PROVIDERS_JSON ?? "";
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch (error) {
|
||||
console.error(raw);
|
||||
throw new Error(`image providers output is not JSON: ${error.message}`);
|
||||
}
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error("image providers output must be a JSON array");
|
||||
}
|
||||
if (parsed.length === 0) {
|
||||
throw new Error("image providers output is empty");
|
||||
}
|
||||
const ids = new Set(parsed.map((entry) => entry && typeof entry.id === "string" ? entry.id : ""));
|
||||
for (const expected of ["google", "openai", "xai"]) {
|
||||
if (!ids.has(expected)) {
|
||||
throw new Error(`image providers output is missing bundled provider '${expected}'`);
|
||||
}
|
||||
}
|
||||
console.log(`bun-global-install-smoke: image providers OK (${parsed.length} providers)`);
|
||||
NODE
|
||||
OPENCLAW_IMAGE_PROVIDERS_JSON="$providers_json" node scripts/e2e/lib/bun-global-install/assertions.mjs assert-image-providers
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
57
scripts/e2e/lib/bun-global-install/assertions.mjs
Normal file
57
scripts/e2e/lib/bun-global-install/assertions.mjs
Normal file
@@ -0,0 +1,57 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const usage = () => {
|
||||
console.error("Usage: assertions.mjs <run-with-timeout|assert-image-providers> [...]");
|
||||
process.exit(2);
|
||||
};
|
||||
|
||||
const [mode, ...args] = process.argv.slice(2);
|
||||
|
||||
if (mode === "run-with-timeout") {
|
||||
const [timeoutMs, command, ...commandArgs] = args;
|
||||
const timeout = Number(timeoutMs);
|
||||
if (!Number.isFinite(timeout) || timeout <= 0 || !command) {
|
||||
usage();
|
||||
}
|
||||
|
||||
const result = spawnSync(command, commandArgs, { encoding: "utf8", env: process.env, timeout });
|
||||
process.stdout.write(result.stdout ?? "");
|
||||
process.stderr.write(result.stderr ?? "");
|
||||
if (result.error) {
|
||||
console.error(`command failed: ${command}: ${result.error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (result.signal) {
|
||||
console.error(`command terminated: ${command}: ${result.signal}`);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(result.status ?? 0);
|
||||
}
|
||||
|
||||
if (mode === "assert-image-providers") {
|
||||
const raw = process.env.OPENCLAW_IMAGE_PROVIDERS_JSON ?? "";
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch (error) {
|
||||
console.error(raw);
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`image providers output is not JSON: ${message}`, { cause: error });
|
||||
}
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error("image providers output must be a JSON array");
|
||||
}
|
||||
if (parsed.length === 0) {
|
||||
throw new Error("image providers output is empty");
|
||||
}
|
||||
const ids = new Set(parsed.map((entry) => (typeof entry?.id === "string" ? entry.id : "")));
|
||||
for (const expected of ["google", "openai", "xai"]) {
|
||||
if (!ids.has(expected)) {
|
||||
throw new Error(`image providers output is missing bundled provider '${expected}'`);
|
||||
}
|
||||
}
|
||||
console.log(`bun-global-install-smoke: image providers OK (${parsed.length} providers)`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
usage();
|
||||
@@ -24,60 +24,7 @@ bundled_channel_install_package /tmp/openclaw-load-failure-install.log
|
||||
|
||||
root="$(bundled_channel_package_root)"
|
||||
plugin_dir="$root/dist/extensions/load-failure-alpha"
|
||||
mkdir -p "$plugin_dir"
|
||||
cat >"$plugin_dir/package.json" <<'JSON'
|
||||
{
|
||||
"name": "@openclaw/load-failure-alpha",
|
||||
"version": "2026.4.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.js"],
|
||||
"setupEntry": "./setup-entry.js"
|
||||
}
|
||||
}
|
||||
JSON
|
||||
cat >"$plugin_dir/openclaw.plugin.json" <<'JSON'
|
||||
{
|
||||
"id": "load-failure-alpha",
|
||||
"channels": ["load-failure-alpha"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
cat >"$plugin_dir/index.js" <<'JS'
|
||||
export default {
|
||||
kind: "bundled-channel-entry",
|
||||
id: "load-failure-alpha",
|
||||
name: "Load Failure Alpha",
|
||||
description: "Load Failure Alpha",
|
||||
register() {},
|
||||
loadChannelSecrets() {
|
||||
globalThis.__loadFailureSecrets = (globalThis.__loadFailureSecrets ?? 0) + 1;
|
||||
throw new Error("synthetic channel secrets failure");
|
||||
},
|
||||
loadChannelPlugin() {
|
||||
globalThis.__loadFailurePlugin = (globalThis.__loadFailurePlugin ?? 0) + 1;
|
||||
throw new Error("synthetic channel plugin failure");
|
||||
}
|
||||
};
|
||||
JS
|
||||
cat >"$plugin_dir/setup-entry.js" <<'JS'
|
||||
export default {
|
||||
kind: "bundled-channel-setup-entry",
|
||||
loadSetupSecrets() {
|
||||
globalThis.__loadFailureSetupSecrets = (globalThis.__loadFailureSetupSecrets ?? 0) + 1;
|
||||
throw new Error("synthetic setup secrets failure");
|
||||
},
|
||||
loadSetupPlugin() {
|
||||
globalThis.__loadFailureSetup = (globalThis.__loadFailureSetup ?? 0) + 1;
|
||||
throw new Error("synthetic setup plugin failure");
|
||||
}
|
||||
};
|
||||
JS
|
||||
node scripts/e2e/lib/bundled-channel/write-load-failure-fixture.mjs "$plugin_dir"
|
||||
|
||||
echo "Loading synthetic failing bundled channel through packaged loader..."
|
||||
node scripts/e2e/lib/bundled-channel/loader-probe.mjs load-failure "$root" load-failure-alpha
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const [pluginDir] = process.argv.slice(2);
|
||||
if (!pluginDir) {
|
||||
throw new Error("usage: write-load-failure-fixture.mjs <plugin-dir>");
|
||||
}
|
||||
|
||||
const writeJson = (filename, contents) =>
|
||||
fs.writeFileSync(path.join(pluginDir, filename), `${JSON.stringify(contents, null, 2)}\n`);
|
||||
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
writeJson("package.json", {
|
||||
name: "@openclaw/load-failure-alpha",
|
||||
version: "2026.4.21",
|
||||
private: true,
|
||||
type: "module",
|
||||
openclaw: { extensions: ["./index.js"], setupEntry: "./setup-entry.js" },
|
||||
});
|
||||
writeJson("openclaw.plugin.json", {
|
||||
id: "load-failure-alpha",
|
||||
channels: ["load-failure-alpha"],
|
||||
configSchema: { type: "object", additionalProperties: false, properties: {} },
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "index.js"),
|
||||
`export default {
|
||||
kind: "bundled-channel-entry", id: "load-failure-alpha", name: "Load Failure Alpha", description: "Load Failure Alpha", register() {},
|
||||
loadChannelSecrets() { globalThis.__loadFailureSecrets = (globalThis.__loadFailureSecrets ?? 0) + 1; throw new Error("synthetic channel secrets failure"); },
|
||||
loadChannelPlugin() { globalThis.__loadFailurePlugin = (globalThis.__loadFailurePlugin ?? 0) + 1; throw new Error("synthetic channel plugin failure"); }
|
||||
};
|
||||
`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "setup-entry.js"),
|
||||
`export default {
|
||||
kind: "bundled-channel-setup-entry",
|
||||
loadSetupSecrets() { globalThis.__loadFailureSetupSecrets = (globalThis.__loadFailureSetupSecrets ?? 0) + 1; throw new Error("synthetic setup secrets failure"); },
|
||||
loadSetupPlugin() { globalThis.__loadFailureSetup = (globalThis.__loadFailureSetup ?? 0) + 1; throw new Error("synthetic setup plugin failure"); }
|
||||
};
|
||||
`,
|
||||
);
|
||||
@@ -161,6 +161,13 @@ run_wizard() {
|
||||
run_wizard_cmd "$case_name" "$state_ref" "node \"$OPENCLAW_ENTRY\" onboard $ONBOARD_FLAGS" "$send_fn" true "$validate_fn"
|
||||
}
|
||||
|
||||
assert_onboard_config() {
|
||||
local scenario="$1"
|
||||
shift
|
||||
openclaw_e2e_assert_file "$OPENCLAW_CONFIG_PATH"
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs "$scenario" "$OPENCLAW_CONFIG_PATH" "$@"
|
||||
}
|
||||
|
||||
set_isolated_openclaw_env() {
|
||||
local state_ref="$1"
|
||||
openclaw_test_state_create "$state_ref" empty
|
||||
@@ -230,16 +237,14 @@ run_case_local_basic() {
|
||||
|
||||
# Assert config + workspace scaffolding.
|
||||
workspace_dir="$OPENCLAW_STATE_DIR/workspace"
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
sessions_dir="$OPENCLAW_STATE_DIR/agents/main/sessions"
|
||||
|
||||
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
|
||||
openclaw_e2e_assert_file "$workspace_dir/$file"
|
||||
done
|
||||
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs local-basic "$config_path" "$workspace_dir"
|
||||
assert_onboard_config local-basic "$workspace_dir"
|
||||
|
||||
}
|
||||
|
||||
@@ -253,25 +258,12 @@ run_case_remote_non_interactive() {
|
||||
--skip-skills \
|
||||
--skip-health
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs remote-non-interactive "$config_path"
|
||||
assert_onboard_config remote-non-interactive
|
||||
}
|
||||
|
||||
run_case_reset() {
|
||||
set_isolated_openclaw_env reset-config
|
||||
# Seed a remote config to exercise reset path.
|
||||
cat >"$OPENCLAW_CONFIG_PATH" <<'JSON'
|
||||
{
|
||||
"meta": {},
|
||||
"agents": { "defaults": { "workspace": "/root/old" } },
|
||||
"gateway": {
|
||||
"mode": "remote",
|
||||
"remote": { "url": "ws://old.example:18789", "token": "old-token" }
|
||||
}
|
||||
}
|
||||
JSON
|
||||
node scripts/e2e/lib/onboard/write-config.mjs reset "$OPENCLAW_CONFIG_PATH"
|
||||
|
||||
openclaw_e2e_run_logged reset-config node "$OPENCLAW_ENTRY" onboard \
|
||||
--non-interactive \
|
||||
@@ -285,43 +277,25 @@ JSON
|
||||
--skip-ui \
|
||||
--skip-health
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs reset "$config_path"
|
||||
assert_onboard_config reset
|
||||
}
|
||||
|
||||
run_case_channels() {
|
||||
# Channels-only configure flow.
|
||||
run_wizard_cmd channels channels "node \"$OPENCLAW_ENTRY\" configure --section channels" send_channels_flow
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs channels "$config_path"
|
||||
assert_onboard_config channels
|
||||
}
|
||||
|
||||
run_case_skills() {
|
||||
local home_dir
|
||||
set_isolated_openclaw_env skills
|
||||
home_dir="$HOME"
|
||||
# Seed skills config to ensure it survives the wizard.
|
||||
cat >"$OPENCLAW_CONFIG_PATH" <<'JSON'
|
||||
{
|
||||
"meta": {},
|
||||
"skills": {
|
||||
"allowBundled": ["__none__"],
|
||||
"install": { "nodeManager": "bun" }
|
||||
}
|
||||
}
|
||||
JSON
|
||||
node scripts/e2e/lib/onboard/write-config.mjs skills "$OPENCLAW_CONFIG_PATH"
|
||||
|
||||
run_wizard_cmd skills "$home_dir" "node \"$OPENCLAW_ENTRY\" configure --section skills" send_skills_flow
|
||||
|
||||
config_path="$OPENCLAW_CONFIG_PATH"
|
||||
openclaw_e2e_assert_file "$config_path"
|
||||
|
||||
node scripts/e2e/lib/onboard/assert-config.mjs skills "$config_path"
|
||||
assert_onboard_config skills
|
||||
}
|
||||
|
||||
validate_local_basic_log() {
|
||||
|
||||
20
scripts/e2e/lib/onboard/write-config.mjs
Normal file
20
scripts/e2e/lib/onboard/write-config.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
import fs from "node:fs";
|
||||
|
||||
const [scenario, configPath] = process.argv.slice(2);
|
||||
if (!scenario || !configPath) {
|
||||
throw new Error("usage: write-config.mjs <reset|skills> <config-path>");
|
||||
}
|
||||
|
||||
const config = {
|
||||
reset: {
|
||||
meta: {},
|
||||
agents: { defaults: { workspace: "/root/old" } },
|
||||
gateway: { mode: "remote", remote: { url: "ws://old.example:18789", token: "old-token" } },
|
||||
},
|
||||
skills: { meta: {}, skills: { allowBundled: ["__none__"], install: { nodeManager: "bun" } } },
|
||||
}[scenario];
|
||||
if (!config) {
|
||||
throw new Error(`unknown config scenario: ${scenario}`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
||||
Reference in New Issue
Block a user