




















@@ -9,7 +9,7 @@ import {
99rmSync,
1010writeFileSync,
1111} from "node:fs";
12-import { createServer } from "node:http";
12+import { createServer, request as httpRequest } from "node:http";
1313import { tmpdir } from "node:os";
1414import path from "node:path";
1515import { describe, expect, it } from "vitest";
@@ -143,6 +143,45 @@ function runPluginsSweepShell(script: string, env: NodeJS.ProcessEnv = {}) {
143143});
144144}
145145146+async function waitForPortFile(portFile: string): Promise<number> {
147+for (let attempt = 0; attempt < 50; attempt += 1) {
148+if (existsSync(portFile)) {
149+const port = Number(readFileSync(portFile, "utf8"));
150+if (Number.isInteger(port) && port > 0) {
151+return port;
152+}
153+}
154+await new Promise((resolve) => setTimeout(resolve, 20));
155+}
156+throw new Error(`timed out waiting for ${portFile}`);
157+}
158+159+function requestFixtureRegistry(
160+port: number,
161+requestPath: string,
162+): Promise<{ body: string; statusCode: number | undefined }> {
163+return new Promise((resolve, reject) => {
164+const request = httpRequest(
165+{ host: "127.0.0.1", method: "GET", path: requestPath, port },
166+(response) => {
167+let body = "";
168+response.setEncoding("utf8");
169+response.on("data", (chunk: string) => {
170+body += chunk;
171+});
172+response.on("end", () => {
173+resolve({ body, statusCode: response.statusCode });
174+});
175+},
176+);
177+request.setTimeout(2_000, () => {
178+request.destroy(new Error(`timed out requesting ${requestPath}`));
179+});
180+request.on("error", reject);
181+request.end();
182+});
183+}
184+146185describe("plugins Docker assertions", () => {
147186it("rejects loose ClawHub preflight limits instead of parsing prefixes", () => {
148187const timeoutResult = spawnSync(process.execPath, [ASSERTIONS_SCRIPT, "clawhub-preflight"], {
@@ -510,6 +549,59 @@ test -d "$OPENCLAW_PLUGINS_TMP_DIR"
510549}
511550});
512551552+it("keeps npm fixture registry alive after malformed package paths", async () => {
553+const tempDirs: string[] = [];
554+const root = makeTempDir(tempDirs, "openclaw-plugin-npm-fixture-request-");
555+const portFile = path.join(root, "port");
556+const tarballPath = path.join(root, "demo-plugin.tgz");
557+writeFileSync(tarballPath, "fixture package archive", "utf8");
558+559+const child = spawn(
560+process.execPath,
561+[
562+"scripts/e2e/lib/plugins/npm-registry-server.mjs",
563+portFile,
564+"@openclaw/demo-plugin-npm",
565+"1.0.0",
566+tarballPath,
567+],
568+{
569+cwd: process.cwd(),
570+stdio: ["ignore", "pipe", "pipe"],
571+},
572+);
573+const stderr = createBoundedChildOutput();
574+child.stderr.setEncoding("utf8");
575+child.stderr.on("data", (chunk) => {
576+stderr.append(chunk);
577+});
578+579+try {
580+const port = await waitForPortFile(portFile);
581+const malformed = await requestFixtureRegistry(port, "/%");
582+583+expect(malformed.statusCode).toBe(404);
584+expect(malformed.body).toContain("not found");
585+expect(child.exitCode, stderr.text()).toBeNull();
586+587+const valid = await requestFixtureRegistry(port, "/@openclaw%2Fdemo-plugin-npm");
588+589+expect(valid.statusCode, stderr.text()).toBe(200);
590+expect(JSON.parse(valid.body)).toMatchObject({
591+name: "@openclaw/demo-plugin-npm",
592+"dist-tags": { latest: "1.0.0" },
593+});
594+} finally {
595+if (child.exitCode === null) {
596+child.kill();
597+await new Promise((resolve) => {
598+child.once("close", resolve);
599+});
600+}
601+cleanupTempDirs(tempDirs);
602+}
603+});
604+513605it("rejects invalid plugin fixture log byte limits before npm fixture setup", () => {
514606const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-npm-fixture-log-invalid-"));
515607try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。