mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-27 23:12:52 +00:00
fix(e2e): harden Windows kitchen sink assertions
This commit is contained in:
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Tests: keep kitchen-sink plugin assertion fixtures on a configurable temp root so native Windows runs no longer skip full-surface diagnostic coverage.
|
||||
- Config/secrets: allow exec SecretRef ids to include `#` selectors so AWS-style `secret#json_key` ids validate consistently. (#80731) Thanks @TurboTheTurtle.
|
||||
- Tests: keep the Telegram user credential helper on platform temp and path APIs so native Windows credential export and restore commands do not write through POSIX-only paths.
|
||||
- Installer: include the optional verify phase in the progress counter so `--verify` shows `[4/4] Verifying installation` instead of `[4/3]`.
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const command = process.argv[2];
|
||||
const scratchRoot = process.env.KITCHEN_SINK_TMP_DIR || os.tmpdir();
|
||||
|
||||
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
const scratchFile = (name) => path.join(scratchRoot, name);
|
||||
const normalizedPath = (filePath) => filePath.replaceAll("\\", "/");
|
||||
|
||||
function resolveHomePath(value) {
|
||||
if (value === "~") {
|
||||
return process.env.HOME;
|
||||
}
|
||||
if (value?.startsWith("~/") || value?.startsWith("~\\")) {
|
||||
return path.join(process.env.HOME, value.slice(2));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function expectFailure() {
|
||||
const outputFile = process.argv[3];
|
||||
@@ -24,7 +38,7 @@ function expectFailure() {
|
||||
}
|
||||
|
||||
function scanLogs() {
|
||||
const roots = ["/tmp", path.join(process.env.HOME, ".openclaw")];
|
||||
const roots = [scratchRoot, path.join(process.env.HOME, ".openclaw")];
|
||||
const files = [];
|
||||
const visit = (entry) => {
|
||||
if (!fs.existsSync(entry)) {
|
||||
@@ -38,7 +52,7 @@ function scanLogs() {
|
||||
return;
|
||||
}
|
||||
if (/\.(?:log|jsonl)$/u.test(entry) || /openclaw-kitchen-sink-/u.test(path.basename(entry))) {
|
||||
if (entry.includes("/.npm/_logs/")) {
|
||||
if (normalizedPath(entry).includes("/.npm/_logs/")) {
|
||||
return;
|
||||
}
|
||||
files.push(entry);
|
||||
@@ -287,9 +301,9 @@ function assertInstalled() {
|
||||
const source = process.env.KITCHEN_SINK_SOURCE;
|
||||
const surfaceMode = process.env.KITCHEN_SINK_SURFACE_MODE;
|
||||
const label = process.env.KITCHEN_SINK_LABEL;
|
||||
const list = readJson(`/tmp/kitchen-sink-${label}-plugins.json`);
|
||||
const inspect = readJson(`/tmp/kitchen-sink-${label}-inspect.json`);
|
||||
const allInspect = readJson(`/tmp/kitchen-sink-${label}-inspect-all.json`);
|
||||
const list = readJson(scratchFile(`kitchen-sink-${label}-plugins.json`));
|
||||
const inspect = readJson(scratchFile(`kitchen-sink-${label}-inspect.json`));
|
||||
const allInspect = readJson(scratchFile(`kitchen-sink-${label}-inspect-all.json`));
|
||||
const plugin = (list.plugins || []).find((entry) => entry.id === pluginId);
|
||||
if (!plugin) {
|
||||
throw new Error(`kitchen-sink plugin not found after install: ${pluginId}`);
|
||||
@@ -429,20 +443,20 @@ function assertInstalled() {
|
||||
if (typeof record.installPath !== "string" || record.installPath.length === 0) {
|
||||
throw new Error("missing kitchen-sink install path");
|
||||
}
|
||||
const installPath = record.installPath.replace(/^~(?=$|\/)/u, process.env.HOME);
|
||||
const installPath = resolveHomePath(record.installPath);
|
||||
if (!fs.existsSync(installPath)) {
|
||||
throw new Error(`kitchen-sink install path missing: ${record.installPath}`);
|
||||
}
|
||||
if (source === "clawhub" && record.artifactKind === "npm-pack") {
|
||||
assertClawHubExternalInstallContract(installPath);
|
||||
}
|
||||
fs.writeFileSync(`/tmp/kitchen-sink-${label}-install-path.txt`, installPath, "utf8");
|
||||
fs.writeFileSync(scratchFile(`kitchen-sink-${label}-install-path.txt`), installPath, "utf8");
|
||||
}
|
||||
|
||||
function assertRemoved() {
|
||||
const pluginId = process.env.KITCHEN_SINK_ID;
|
||||
const label = process.env.KITCHEN_SINK_LABEL;
|
||||
const list = readJson(`/tmp/kitchen-sink-${label}-uninstalled.json`);
|
||||
const list = readJson(scratchFile(`kitchen-sink-${label}-uninstalled.json`));
|
||||
if ((list.plugins || []).some((entry) => entry.id === pluginId)) {
|
||||
throw new Error(`kitchen-sink plugin still listed after uninstall: ${pluginId}`);
|
||||
}
|
||||
@@ -467,7 +481,7 @@ function assertRemoved() {
|
||||
if (config.channels?.["kitchen-sink-channel"]) {
|
||||
throw new Error("kitchen-sink channel config still present after uninstall");
|
||||
}
|
||||
const installPathFile = `/tmp/kitchen-sink-${label}-install-path.txt`;
|
||||
const installPathFile = scratchFile(`kitchen-sink-${label}-install-path.txt`);
|
||||
if (fs.existsSync(installPathFile)) {
|
||||
const installPath = fs.readFileSync(installPathFile, "utf8").trim();
|
||||
if (installPath && fs.existsSync(installPath)) {
|
||||
|
||||
@@ -6,13 +6,14 @@ source scripts/lib/docker-e2e-logs.sh
|
||||
|
||||
OPENCLAW_ENTRY="$(openclaw_e2e_resolve_entrypoint)"
|
||||
export OPENCLAW_ENTRY
|
||||
export KITCHEN_SINK_TMP_DIR="${KITCHEN_SINK_TMP_DIR:-/tmp}"
|
||||
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
|
||||
run_expect_failure() {
|
||||
local label="$1"
|
||||
shift
|
||||
local output_file="/tmp/kitchen-sink-expected-failure-${label}.txt"
|
||||
local output_file="${KITCHEN_SINK_TMP_DIR}/kitchen-sink-expected-failure-${label}.txt"
|
||||
set +e
|
||||
"$@" >"$output_file" 2>&1
|
||||
local status="$?"
|
||||
@@ -90,9 +91,9 @@ run_success_scenario() {
|
||||
run_logged_print "kitchen-sink-install-${KITCHEN_SINK_LABEL}" node "$OPENCLAW_ENTRY" plugins install "${install_args[@]}"
|
||||
configure_kitchen_sink_runtime
|
||||
run_logged_print "kitchen-sink-enable-${KITCHEN_SINK_LABEL}" node "$OPENCLAW_ENTRY" plugins enable "$KITCHEN_SINK_ID"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"/tmp/kitchen-sink-${KITCHEN_SINK_LABEL}-plugins.json"
|
||||
node "$OPENCLAW_ENTRY" plugins inspect "$KITCHEN_SINK_ID" --runtime --json >"/tmp/kitchen-sink-${KITCHEN_SINK_LABEL}-inspect.json"
|
||||
node "$OPENCLAW_ENTRY" plugins inspect --all --runtime --json >"/tmp/kitchen-sink-${KITCHEN_SINK_LABEL}-inspect-all.json"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"${KITCHEN_SINK_TMP_DIR}/kitchen-sink-${KITCHEN_SINK_LABEL}-plugins.json"
|
||||
node "$OPENCLAW_ENTRY" plugins inspect "$KITCHEN_SINK_ID" --runtime --json >"${KITCHEN_SINK_TMP_DIR}/kitchen-sink-${KITCHEN_SINK_LABEL}-inspect.json"
|
||||
node "$OPENCLAW_ENTRY" plugins inspect --all --runtime --json >"${KITCHEN_SINK_TMP_DIR}/kitchen-sink-${KITCHEN_SINK_LABEL}-inspect-all.json"
|
||||
assert_kitchen_sink_installed
|
||||
if [ "$KITCHEN_SINK_SOURCE" = "clawhub" ]; then
|
||||
run_logged_print "kitchen-sink-uninstall-${KITCHEN_SINK_LABEL}" node "$OPENCLAW_ENTRY" plugins uninstall "$KITCHEN_SINK_SPEC" --force
|
||||
@@ -100,7 +101,7 @@ run_success_scenario() {
|
||||
run_logged_print "kitchen-sink-uninstall-${KITCHEN_SINK_LABEL}" node "$OPENCLAW_ENTRY" plugins uninstall "$KITCHEN_SINK_ID" --force
|
||||
fi
|
||||
remove_kitchen_sink_channel_config
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"/tmp/kitchen-sink-${KITCHEN_SINK_LABEL}-uninstalled.json"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"${KITCHEN_SINK_TMP_DIR}/kitchen-sink-${KITCHEN_SINK_LABEL}-uninstalled.json"
|
||||
assert_kitchen_sink_removed
|
||||
}
|
||||
|
||||
@@ -108,7 +109,7 @@ run_failure_scenario() {
|
||||
echo "Testing expected ${KITCHEN_SINK_LABEL} install failure from ${KITCHEN_SINK_SPEC}..."
|
||||
run_expect_failure "install-${KITCHEN_SINK_LABEL}" node "$OPENCLAW_ENTRY" plugins install "$KITCHEN_SINK_SPEC"
|
||||
remove_kitchen_sink_channel_config
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"/tmp/kitchen-sink-${KITCHEN_SINK_LABEL}-uninstalled.json"
|
||||
node "$OPENCLAW_ENTRY" plugins list --json >"${KITCHEN_SINK_TMP_DIR}/kitchen-sink-${KITCHEN_SINK_LABEL}-uninstalled.json"
|
||||
assert_kitchen_sink_removed
|
||||
}
|
||||
|
||||
|
||||
@@ -62,10 +62,11 @@ function runAssertInstalled({
|
||||
const pluginId = "openclaw-kitchen-sink-fixture";
|
||||
const home = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-home-"));
|
||||
const installPath = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-install-"));
|
||||
const pluginsJsonPath = path.join("/tmp", `kitchen-sink-${label}-plugins.json`);
|
||||
const inspectJsonPath = path.join("/tmp", `kitchen-sink-${label}-inspect.json`);
|
||||
const inspectAllJsonPath = path.join("/tmp", `kitchen-sink-${label}-inspect-all.json`);
|
||||
const installPathMarker = path.join("/tmp", `kitchen-sink-${label}-install-path.txt`);
|
||||
const scratchRoot = tmpdir();
|
||||
const pluginsJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-plugins.json`);
|
||||
const inspectJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-inspect.json`);
|
||||
const inspectAllJsonPath = path.join(scratchRoot, `kitchen-sink-${label}-inspect-all.json`);
|
||||
const installPathMarker = path.join(scratchRoot, `kitchen-sink-${label}-install-path.txt`);
|
||||
const installsPath = path.join(home, ".openclaw", "plugins", "installs.json");
|
||||
const spawnEnv = { ...process.env };
|
||||
delete spawnEnv.KITCHEN_SINK_REQUIRE_ALL_DIAGNOSTICS;
|
||||
@@ -100,6 +101,7 @@ function runAssertInstalled({
|
||||
KITCHEN_SINK_SOURCE: "npm",
|
||||
KITCHEN_SINK_SPEC: "npm:@openclaw/kitchen-sink@latest",
|
||||
KITCHEN_SINK_SURFACE_MODE: "full",
|
||||
KITCHEN_SINK_TMP_DIR: scratchRoot,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
@@ -113,41 +115,32 @@ function runAssertInstalled({
|
||||
}
|
||||
|
||||
describe("kitchen-sink plugin assertions", () => {
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"fails full-surface installs when stable diagnostic canaries disappear",
|
||||
() => {
|
||||
const result = runAssertInstalled();
|
||||
it("fails full-surface installs when stable diagnostic canaries disappear", () => {
|
||||
const result = runAssertInstalled();
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
||||
"missing expected kitchen-sink diagnostic error",
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
||||
"missing expected kitchen-sink diagnostic error",
|
||||
);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"accepts published full-surface installs with stable diagnostic canaries",
|
||||
() => {
|
||||
const result = runAssertInstalled({
|
||||
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
||||
});
|
||||
it("accepts published full-surface installs with stable diagnostic canaries", () => {
|
||||
const result = runAssertInstalled({
|
||||
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
},
|
||||
);
|
||||
expect(result.status).toBe(0);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"keeps exhaustive diagnostic matching available for synchronized fixtures",
|
||||
() => {
|
||||
const result = runAssertInstalled({
|
||||
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
||||
env: { KITCHEN_SINK_REQUIRE_ALL_DIAGNOSTICS: "1" },
|
||||
});
|
||||
it("keeps exhaustive diagnostic matching available for synchronized fixtures", () => {
|
||||
const result = runAssertInstalled({
|
||||
diagnostics: diagnosticErrors(REQUIRED_FULL_DIAGNOSTIC_CANARIES),
|
||||
env: { KITCHEN_SINK_REQUIRE_ALL_DIAGNOSTICS: "1" },
|
||||
});
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
||||
"cli registration missing explicit commands metadata",
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
||||
"cli registration missing explicit commands metadata",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user