


























11import { execFile } from "node:child_process";
22import fs from "node:fs/promises";
3+import { createServer, type Server } from "node:http";
34import os from "node:os";
45import path from "node:path";
56import { fileURLToPath } from "node:url";
@@ -33,6 +34,39 @@ afterEach(async () => {
3334await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
3435});
353637+async function listenOnLoopback(server: Server) {
38+await new Promise<void>((resolve, reject) => {
39+server.once("error", reject);
40+server.listen(0, "127.0.0.1", () => {
41+server.off("error", reject);
42+resolve();
43+});
44+});
45+const address = server.address();
46+if (!address || typeof address === "string") {
47+throw new Error("Expected TCP server address.");
48+}
49+return address;
50+}
51+52+function closeServer(server: Server) {
53+return new Promise<void>((resolve, reject) => {
54+server.close((error) => (error ? reject(error) : resolve()));
55+});
56+}
57+58+function credentialBrokerEnv(port: number) {
59+return {
60+ ...process.env,
61+OPENCLAW_QA_ALLOW_INSECURE_HTTP: "1",
62+OPENCLAW_QA_CONVEX_SECRET_MAINTAINER: "test-secret",
63+OPENCLAW_QA_CONVEX_SITE_URL: `http://127.0.0.1:${port}`,
64+OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS: "1000",
65+OPENCLAW_QA_CREDENTIAL_OWNER_ID: "test-owner",
66+OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE: "maintainer",
67+};
68+}
69+3670describe("RTT harness", () => {
3771it("validates OpenClaw package specs", () => {
3872expect(validateOpenClawPackageSpec("openclaw@main")).toBe("openclaw@main");
@@ -131,6 +165,10 @@ describe("RTT harness", () => {
131165"OPENCLAW_QA_CONVEX_SECRET_CI",
132166installEnvSnapshotIndex,
133167);
168+const bodyLimitForwardIndex = script.indexOf(
169+"OPENCLAW_QA_CREDENTIAL_HTTP_MAX_BODY_BYTES",
170+installEnvSnapshotIndex,
171+);
134172const packageInstallIndex = script.indexOf("npm install -g");
135173const credentialAcquireIndex = script.indexOf(
136174"node /app/scripts/e2e/npm-telegram-rtt-credentials.mjs acquire",
@@ -142,23 +180,26 @@ describe("RTT harness", () => {
142180expect(tokenExportIndex).toBeGreaterThan(sourceIndex);
143181expect(installEnvSnapshotIndex).toBeGreaterThanOrEqual(0);
144182expect(convexSecretForwardIndex).toBeGreaterThan(installEnvSnapshotIndex);
183+expect(bodyLimitForwardIndex).toBeGreaterThan(installEnvSnapshotIndex);
145184expect(packageInstallIndex).toBeLessThan(credentialAcquireIndex);
146185expect(script).toContain(
147186'-e OPENCLAW_E2E_NPM_INSTALL_TIMEOUT="${OPENCLAW_E2E_NPM_INSTALL_TIMEOUT:-600s}"',
148187);
149188expect(script).toContain(
150189'"$timeout_bin" --kill-after=30s "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
151190);
152-expect(script).toContain('elif command -v gtimeout >/dev/null 2>&1; then');
153-expect(script).toContain("timeout_bin=\"gtimeout\"");
191+expect(script).toContain("elif command -v gtimeout >/dev/null 2>&1; then");
192+expect(script).toContain('timeout_bin="gtimeout"');
154193expect(script).toContain(
155194'echo "timeout or gtimeout is required for OPENCLAW_E2E_NPM_INSTALL_TIMEOUT=$npm_install_timeout" >&2',
156195);
157196expect(script).toContain('"$timeout_bin" --kill-after=1s 1s true >/dev/null 2>&1');
158197expect(script).toContain(
159198'"$timeout_bin" "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
160199);
161-expect(script).not.toContain("running package install without OPENCLAW_E2E_NPM_INSTALL_TIMEOUT");
200+expect(script).not.toContain(
201+"running package install without OPENCLAW_E2E_NPM_INSTALL_TIMEOUT",
202+);
162203expect(script).toContain("run_logged docker_e2e_docker_run_cmd run --rm");
163204expect(script).not.toContain("run_logged docker run --rm");
164205expect(heartbeatStartIndex).toBeGreaterThan(sourceIndex);
@@ -169,15 +210,87 @@ describe("RTT harness", () => {
169210expect(script).not.toContain('export TELEGRAM_BOT_TOKEN="$OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN"');
170211});
171212172-it("keeps broker helper heartbeat and empty-response handling aligned with QA leases", async () => {
213+it("keeps broker helper heartbeat handling aligned with QA leases", async () => {
173214const script = await fs.readFile(CREDENTIAL_SCRIPT_PATH, "utf8");
174215175-expect(script).toContain("await response.text()");
176-expect(script).toContain('response.ok\n ? { status: "ok" }');
177216expect(script).toContain("leaseTtlMs: acquired.leaseTtlMs ?? config.leaseTtlMs");
178217expect(script).toContain("leaseTtlMs: leaseTtlMsFromLease(config, lease)");
179218});
180219220+it("bounds Convex credential broker response bodies", async () => {
221+const server = createServer((_request, response) => {
222+response.writeHead(500, { "content-type": "application/json" });
223+response.end(JSON.stringify({ status: "error", message: "x".repeat(128) }));
224+});
225+const { port } = await listenOnLoopback(server);
226+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rtt-credentials-"));
227+tempDirs.push(tempDir);
228+229+try {
230+await execFileAsync(
231+process.execPath,
232+[
233+CREDENTIAL_SCRIPT_PATH,
234+"acquire",
235+"--lease-file",
236+path.join(tempDir, "lease.json"),
237+"--credential-env-file",
238+path.join(tempDir, "credentials.env"),
239+],
240+{
241+env: {
242+ ...credentialBrokerEnv(port),
243+OPENCLAW_QA_CREDENTIAL_HTTP_MAX_BODY_BYTES: "16",
244+},
245+maxBuffer: 128 * 1024,
246+},
247+);
248+throw new Error("Expected credential acquire to fail.");
249+} catch (error) {
250+const execError = error as Error & { stderr?: string };
251+expect(execError.stderr).toContain(
252+"credential broker acquire response body exceeded 16 bytes",
253+);
254+expect(execError.stderr).not.toContain("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
255+} finally {
256+await closeServer(server);
257+}
258+});
259+260+it("preserves empty broker responses for successful lease release", async () => {
261+const server = createServer((_request, response) => {
262+response.writeHead(204);
263+response.end();
264+});
265+const { port } = await listenOnLoopback(server);
266+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rtt-credentials-"));
267+tempDirs.push(tempDir);
268+const leaseFile = path.join(tempDir, "lease.json");
269+await fs.writeFile(
270+leaseFile,
271+`${JSON.stringify({
272+ kind: "telegram",
273+ ownerId: "test-owner",
274+ actorRole: "maintainer",
275+ credentialId: "credential",
276+ leaseToken: "lease",
277+ })}\n`,
278+);
279+280+try {
281+await execFileAsync(
282+process.execPath,
283+[CREDENTIAL_SCRIPT_PATH, "release", "--lease-file", leaseFile],
284+{
285+env: credentialBrokerEnv(port),
286+},
287+);
288+await expect(fs.stat(leaseFile)).rejects.toMatchObject({ code: "ENOENT" });
289+} finally {
290+await closeServer(server);
291+}
292+});
293+181294it("generates final-only Telegram RTT delivery config for release packages", async () => {
182295const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rtt-config-test-"));
183296tempDirs.push(tempDir);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。