






















@@ -108,6 +108,58 @@ async function writeNodeScript(env: Record<string, string>, port = "18789") {
108108);
109109}
110110111+const NODE_PROCESS_QUERY =
112+"Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress";
113+114+function makeNodeServiceEnv(env: Record<string, string>): Record<string, string> {
115+return {
116+ ...env,
117+OPENCLAW_SERVICE_KIND: "node",
118+OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
119+};
120+}
121+122+function makeSpawnSyncResult(overrides: Partial<SpawnSyncResult> = {}): SpawnSyncResult {
123+return {
124+pid: 0,
125+output: [null, "", ""],
126+stdout: "",
127+stderr: "",
128+status: 0,
129+signal: null,
130+ ...overrides,
131+};
132+}
133+134+function mockWindowsNodeHostProcess(processId = 5151): void {
135+vi.spyOn(process, "platform", "get").mockReturnValue("win32");
136+spawnSync.mockImplementation((command, args) => {
137+if (command === "powershell" && Array.isArray(args) && args.includes(NODE_PROCESS_QUERY)) {
138+return makeSpawnSyncResult({
139+stdout: JSON.stringify([
140+{
141+ProcessId: processId,
142+CommandLine: "C:\\bin\\openclaw.cmd node run --host 127.0.0.1 --port 18789",
143+},
144+]),
145+});
146+}
147+return makeSpawnSyncResult();
148+});
149+}
150+151+function expectTaskkillPid(pid: number): void {
152+expect(
153+spawnSync.mock.calls.some(
154+([command, args]) =>
155+command.endsWith("taskkill.exe") &&
156+Array.isArray(args) &&
157+args.includes("/PID") &&
158+args.includes(String(pid)),
159+),
160+).toBe(true);
161+}
162+111163function expectStartupFallbackSpawn() {
112164expect(spawn).toHaveBeenCalled();
113165const calls = spawn.mock.calls as unknown as Array<
@@ -634,11 +686,7 @@ describe("Windows startup fallback", () => {
634686635687it("does not report a node Startup fallback as running from the gateway listener", async () => {
636688await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
637-const nodeEnv = {
638- ...env,
639-OPENCLAW_SERVICE_KIND: "node",
640-OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
641-};
689+const nodeEnv = makeNodeServiceEnv(env);
642690addStartupFallbackMissingResponses();
643691await writeStartupFallbackEntry(nodeEnv);
644692inspectPortUsage.mockResolvedValue({
@@ -657,11 +705,7 @@ describe("Windows startup fallback", () => {
657705658706it("does not kill the gateway listener when stopping a node Startup fallback", async () => {
659707await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
660-const nodeEnv = {
661- ...env,
662-OPENCLAW_SERVICE_KIND: "node",
663-OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
664-};
708+const nodeEnv = makeNodeServiceEnv(env);
665709addStartupFallbackMissingResponses();
666710await writeStartupFallbackEntry(nodeEnv);
667711inspectPortUsage.mockResolvedValue({
@@ -680,182 +724,53 @@ describe("Windows startup fallback", () => {
680724681725it("stops a node Startup fallback by terminating the matching node host process", async () => {
682726await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
683-vi.spyOn(process, "platform", "get").mockReturnValue("win32");
684-const nodeEnv = {
685- ...env,
686-OPENCLAW_SERVICE_KIND: "node",
687-OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
688-};
727+const nodeEnv = makeNodeServiceEnv(env);
689728addStartupFallbackMissingResponses();
690729await writeStartupFallbackEntry(nodeEnv);
691730await writeNodeScript(nodeEnv);
692-spawnSync.mockImplementation((command, args) => {
693-if (
694-command === "powershell" &&
695-Array.isArray(args) &&
696-args.includes(
697-"Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress",
698-)
699-) {
700-return {
701-pid: 0,
702-output: [null, "", ""],
703-stdout: JSON.stringify([
704-{
705-ProcessId: 5151,
706-CommandLine: "C:\\bin\\openclaw.cmd node run --host 127.0.0.1 --port 18789",
707-},
708-]),
709-stderr: "",
710-status: 0,
711-signal: null,
712-};
713-}
714-return {
715-pid: 0,
716-output: [null, "", ""],
717-stdout: "",
718-stderr: "",
719-status: 0,
720-signal: null,
721-};
722-});
731+mockWindowsNodeHostProcess();
723732724733await stopScheduledTask({ env: nodeEnv, stdout: new PassThrough() });
725734726735expect(inspectPortUsage).not.toHaveBeenCalled();
727-expect(
728-spawnSync.mock.calls.some(
729-([command, args]) =>
730-command.endsWith("taskkill.exe") &&
731-Array.isArray(args) &&
732-args.includes("/PID") &&
733-args.includes("5151"),
734-),
735-).toBe(true);
736+expectTaskkillPid(5151);
736737});
737738});
738739739740it("cleans up a stale node Startup fallback when a node Scheduled Task is registered", async () => {
740741await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
741-vi.spyOn(process, "platform", "get").mockReturnValue("win32");
742-const nodeEnv = {
743- ...env,
744-OPENCLAW_SERVICE_KIND: "node",
745-OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
746-};
742+const nodeEnv = makeNodeServiceEnv(env);
747743schtasksResponses.push(
748744{ code: 0, stdout: "", stderr: "" },
749745{ code: 0, stdout: "", stderr: "" },
750746{ code: 0, stdout: "", stderr: "" },
751747);
752748await writeStartupFallbackEntry(nodeEnv);
753749await writeNodeScript(nodeEnv);
754-spawnSync.mockImplementation((command, args) => {
755-if (
756-command === "powershell" &&
757-Array.isArray(args) &&
758-args.includes(
759-"Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress",
760-)
761-) {
762-return {
763-pid: 0,
764-output: [null, "", ""],
765-stdout: JSON.stringify([
766-{
767-ProcessId: 5151,
768-CommandLine: "C:\\bin\\openclaw.cmd node run --host 127.0.0.1 --port 18789",
769-},
770-]),
771-stderr: "",
772-status: 0,
773-signal: null,
774-};
775-}
776-return {
777-pid: 0,
778-output: [null, "", ""],
779-stdout: "",
780-stderr: "",
781-status: 0,
782-signal: null,
783-};
784-});
750+mockWindowsNodeHostProcess();
785751786752await stopScheduledTask({ env: nodeEnv, stdout: new PassThrough() });
787753788754expect(inspectPortUsage).not.toHaveBeenCalled();
789-expect(
790-spawnSync.mock.calls.some(
791-([command, args]) =>
792-command.endsWith("taskkill.exe") &&
793-Array.isArray(args) &&
794-args.includes("/PID") &&
795-args.includes("5151"),
796-),
797-).toBe(true);
755+expectTaskkillPid(5151);
798756});
799757});
800758801759it("stops a registered node Scheduled Task by terminating the matching node host process", async () => {
802760await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
803-vi.spyOn(process, "platform", "get").mockReturnValue("win32");
804-const nodeEnv = {
805- ...env,
806-OPENCLAW_SERVICE_KIND: "node",
807-OPENCLAW_WINDOWS_TASK_NAME: "OpenClaw Node",
808-};
761+const nodeEnv = makeNodeServiceEnv(env);
809762schtasksResponses.push(
810763{ code: 0, stdout: "", stderr: "" },
811764{ code: 0, stdout: "", stderr: "" },
812765{ code: 0, stdout: "", stderr: "" },
813766);
814767await writeNodeScript(nodeEnv);
815-spawnSync.mockImplementation((command, args) => {
816-if (
817-command === "powershell" &&
818-Array.isArray(args) &&
819-args.includes(
820-"Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress",
821-)
822-) {
823-return {
824-pid: 0,
825-output: [null, "", ""],
826-stdout: JSON.stringify([
827-{
828-ProcessId: 5151,
829-CommandLine: "C:\\bin\\openclaw.cmd node run --host 127.0.0.1 --port 18789",
830-},
831-]),
832-stderr: "",
833-status: 0,
834-signal: null,
835-};
836-}
837-return {
838-pid: 0,
839-output: [null, "", ""],
840-stdout: "",
841-stderr: "",
842-status: 0,
843-signal: null,
844-};
845-});
768+mockWindowsNodeHostProcess();
846769847770await stopScheduledTask({ env: nodeEnv, stdout: new PassThrough() });
848771849772expect(inspectPortUsage).not.toHaveBeenCalled();
850-expect(
851-spawnSync.mock.calls.some(
852-([command, args]) =>
853-command.endsWith("taskkill.exe") &&
854-Array.isArray(args) &&
855-args.includes("/PID") &&
856-args.includes("5151"),
857-),
858-).toBe(true);
773+expectTaskkillPid(5151);
859774});
860775});
861776此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。