|
| 1 | +import { spawn } from "node:child_process"; |
1 | 2 | // Qa Lab tests cover gateway child plugin behavior. |
2 | | -import { EventEmitter } from "node:events"; |
| 3 | +import { EventEmitter, once } from "node:events"; |
3 | 4 | import { lstat, mkdir, mkdtemp, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises"; |
4 | 5 | import os from "node:os"; |
5 | 6 | import path from "node:path"; |
@@ -142,6 +143,51 @@ describe("runQaGatewayCliCommand", () => { |
142 | 143 | }), |
143 | 144 | ).rejects.toThrow("OpenClaw CLI exited 7: fixture failure"); |
144 | 145 | }); |
| 146 | + |
| 147 | + it.each(["stdout", "stderr"] as const)( |
| 148 | + "rejects and stops the CLI child when its %s pipe fails", |
| 149 | + async (streamName) => { |
| 150 | + const child = spawn(process.execPath, ["--eval", "setInterval(() => {}, 1000)"], { |
| 151 | + stdio: ["ignore", "pipe", "pipe"], |
| 152 | + }); |
| 153 | + const close = once(child, "close"); |
| 154 | + const result = testing.readQaGatewayCliCommand(child); |
| 155 | + const message = `synthetic ${streamName} read failure`; |
| 156 | + |
| 157 | + child[streamName]?.destroy(new Error(message)); |
| 158 | + |
| 159 | + await expect(result).rejects.toThrow( |
| 160 | + `qa gateway cli ${streamName} stream failed: ${message}`, |
| 161 | + ); |
| 162 | + await close; |
| 163 | + }, |
| 164 | + ); |
| 165 | +}); |
| 166 | + |
| 167 | +describe("monitorQaGatewayChildFailure", () => { |
| 168 | + it("records the first pipe failure and stops the detached Gateway child", async () => { |
| 169 | + const child = spawn(process.execPath, ["--eval", "setInterval(() => {}, 1000)"], { |
| 170 | + detached: process.platform !== "win32", |
| 171 | + stdio: ["ignore", "pipe", "pipe"], |
| 172 | + }); |
| 173 | + const close = once(child, "close"); |
| 174 | + const output = testing.createQaGatewayChildLogCollector(); |
| 175 | + const getFailure = testing.monitorQaGatewayChildFailure(child, output); |
| 176 | + const error = new Error("synthetic gateway stdout read failure"); |
| 177 | + |
| 178 | + child.stdout?.destroy(error); |
| 179 | + child.stderr?.destroy(new Error("later stderr read failure")); |
| 180 | + |
| 181 | + await vi.waitFor(() => expect(getFailure()).toEqual({ source: "stdout", error })); |
| 182 | + await close; |
| 183 | + expect(output.text()).toContain( |
| 184 | + "gateway child stdout stream failed: synthetic gateway stdout read failure", |
| 185 | + ); |
| 186 | + expect(output.text()).not.toContain("later stderr read failure"); |
| 187 | + expect(() => testing.throwQaGatewayChildFailure(getFailure, () => output.text())).toThrow( |
| 188 | + "gateway child stdout stream failed: synthetic gateway stdout read failure", |
| 189 | + ); |
| 190 | + }); |
145 | 191 | }); |
146 | 192 |
|
147 | 193 | describe("Gateway child fixture helpers", () => { |
|
0 commit comments