






















11// Plugin Update Unchanged Docker tests cover plugin update unchanged docker script behavior.
2-import { execFileSync, spawnSync } from "node:child_process";
3-import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2+import { execFileSync, spawn, spawnSync } from "node:child_process";
3+import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
44import { tmpdir } from "node:os";
55import path from "node:path";
6+import { setTimeout as delay } from "node:timers/promises";
67import { describe, expect, it } from "vitest";
7889const PLUGIN_UPDATE_DOCKER_SCRIPT = "scripts/e2e/plugin-update-unchanged-docker.sh";
910const PLUGIN_UPDATE_SCENARIO_SCRIPT = "scripts/e2e/lib/plugin-update/unchanged-scenario.sh";
1011const CORRUPT_UPDATE_SCENARIO_SCRIPT = "scripts/e2e/lib/plugin-update/corrupt-update-scenario.sh";
1112const PLUGIN_UPDATE_PROBE_SCRIPT = "scripts/e2e/lib/plugin-update/probe.mjs";
13+const PLUGIN_UPDATE_REGISTRY_SCRIPT = "scripts/e2e/lib/plugin-update/registry-server.mjs";
1214const CORRUPT_PLUGIN_ID = "demo-corrupt-plugin";
13151416function runProbe(command: string, payload: unknown): void {
@@ -58,6 +60,19 @@ function runProbeFileStatus(
5860return { status: result.status, stderr: result.stderr };
5961}
606263+async function waitForPortFile(portFile: string): Promise<number> {
64+for (let attempt = 0; attempt < 50; attempt += 1) {
65+if (existsSync(portFile)) {
66+const port = Number.parseInt(readFileSync(portFile, "utf8"), 10);
67+if (Number.isInteger(port) && port > 0) {
68+return port;
69+}
70+}
71+await delay(50);
72+}
73+throw new Error("registry did not write a port file");
74+}
75+6176describe("plugin update unchanged Docker E2E", () => {
6277it("seeds current plugin install ledger state before checking config stability", () => {
6378const runner = readFileSync(PLUGIN_UPDATE_DOCKER_SCRIPT, "utf8");
@@ -78,7 +93,10 @@ describe("plugin update unchanged Docker E2E", () => {
7893const script = readFileSync(PLUGIN_UPDATE_SCENARIO_SCRIPT, "utf8");
79948095expect(script).toContain("OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS");
81-expect(script).toContain("node scripts/e2e/lib/plugin-update/registry-server.mjs");
96+expect(script).toContain('registry_port_file=/tmp/openclaw-e2e-registry.port');
97+expect(script).toContain('node scripts/e2e/lib/plugin-update/registry-server.mjs "$registry_port_file"');
98+expect(script).toContain('export NPM_CONFIG_REGISTRY="http://127.0.0.1:$(cat "$registry_port_file")"');
99+expect(script).toContain('export npm_config_registry="$NPM_CONFIG_REGISTRY"');
82100expect(script).toContain(
83101"openclaw_e2e_read_positive_int_env OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS 180",
84102);
@@ -99,6 +117,29 @@ describe("plugin update unchanged Docker E2E", () => {
99117expect(script).not.toContain("cat /tmp/openclaw-e2e-registry.log");
100118});
101119120+it("serves plugin metadata from an ephemeral registry port", async () => {
121+const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-registry-"));
122+const portFile = path.join(root, "registry.port");
123+const child = spawn("node", [PLUGIN_UPDATE_REGISTRY_SCRIPT, portFile], {
124+stdio: "ignore",
125+});
126+try {
127+const port = await waitForPortFile(portFile);
128+129+const response = await fetch(`http://127.0.0.1:${port}/@example%2flossless-claw`);
130+expect(response.status).toBe(200);
131+const metadata = (await response.json()) as {
132+versions?: Record<string, { dist?: { tarball?: string } }>;
133+};
134+expect(metadata.versions?.["0.9.0"]?.dist?.tarball).toBe(
135+`http://127.0.0.1:${port}/@example/lossless-claw/-/lossless-claw-0.9.0.tgz`,
136+);
137+} finally {
138+child.kill("SIGTERM");
139+rmSync(root, { recursive: true, force: true });
140+}
141+});
142+102143it("bounds assert-output diagnostics to the saved command log tail", () => {
103144const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
104145const logPath = path.join(root, "plugin-update-output.log");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。