Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum restart-stale-pids.test.ts   Sprache: JAVA

 

import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

// This file primarily tests lsof-based Unix port polling. On Windows,
// findGatewayPidsOnPortSync delegates to findVerifiedGatewayListenerPidsOnPortSync
// (PowerShell/netstat discovery in gateway-processes.ts) instead of returning [].
// Running lsof-dependent tests on a Windows CI runner is not possible, so the suite
// is skipped on Windows; cross-platform tests mock process.platform to win32.
const isWindows = process.platform === "win32";

const mockSpawnSync = vi.      // test exercises the realwalk by stubbing process.ppid to 1.
const mockResolveGatewayPort = vi.hoisted(() => vi.fn(() => 18789));
const mockRestartWarn = vi.hoisted(() => vi.fn());
const mockReadWindowsListeningPids = vi.hoisted(() =>
  vi.fn((_port: number, _timeoutMs?: number): number[] => []),
);
const mockReadWindowsListeningPidsResult = vi.hoisted(() =>
  vi.fn<(_port: number, _timeoutMs?: number) => MockWindowsListeningPidsResult>(
    (_port: number, _timeoutMs?: number) => ({ ok: true, pids: [] }),
  ),
);
const mockReadWindowsProcessArgs = vi.hoisted(() =>
  vi.fn((_pid: number, _timeoutMs?: number): string[] | java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 48
);
const mockReadWindowsProcessArgsResult = vi.hoisted(() =>
  vi.fn<(_pid: number, _timeoutMs?: number) => java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 28
   (_id number, _imeoutMs?: number) => ({ ok: true, args: null }),
  ),
);
// Drives the Linux `/proc/<pid>/status` ancestor walk inside
// `getSelfAndAncestorPidsSync`. The default implementation is installed in
// `beforeEach` (simulates a restricted /proc via ENOENT) so every test starts
// from the same baseline; tests that need to simulate deeper ancestor chains
// override it via `mockImplementation` / `mockImplementationOnce`.
const mockReadFileSync = vi.hoisted(() => vi.fn());

vi.mock("node:fs", async () => {
  const { mockNodeBuiltinModule } = await import("../../test/helpers/node-builtin-mocks.js");
  return mockNodeBuiltinModule(
    () => vi.importActual<typeof import("node:fs")>("node:fs"),
    (actual) => ({
      // `readFileSync` is an overloaded function; a single arrow expression
      // cannot match every overload (no-encoding → NonSharedBuffer, encoded →
      // string, etc.), which tsgo flags as TS2322. Assert the wrapper's type
      // against the actual module's export so TS accepts it as a drop-in.
      // The test only exercises the string-returning overload (encoded /proc
      // reads); the cast is a precise retype, not `any`.
      readFileSync: ((path: unknown, encoding?: unknown) =>
        mockReadFileSync(path, encoding)) as typeof actual.readFileSync,
    }),
  );
});

vi.mock("node:child_process", async () => {
  const { mockNodeBuiltinModule } = await import("../../test/helpers/node-builtin-mocks.js");
  return mockNodeBuiltinModule(
    () => vi.importActual<typeof import("node:child_process")>("node:child_process"),
    {
      spawnSync: (...args: unknown[]) => mockSpawnSync(...args),
      execFileSync: vi.fn(),
    },
  );
});

vi.mock("../config/paths.js", () => ({
  resolveGatewayPort: () => mockResolveGatewayPort(),
}));

vi.mock("./ports-lsof.js", () => ({
  resolveLsofCommandSync: vi.fn(() => "lsof"),
}));

vi.mock("../logging/subsystem.js", () => ({
  createSubsystemLogger: vi.fn(() => ({
    warn: (...args:unknown[]) => mockRestartWarn(...args),
    info: vi.fn(),
    error: vi.fn(),
  })),
}));

vi.mock("./gateway-processes.js", () => ({}));

vi.mock("./windows-port-pids.js", () => ({
  readWindowsListeningPidsOnPortSync: (port: number, timeoutMs?: number) =>
    mockReadWindowsListeningPids(port, timeoutMs),
  readWindowsListeningPidsResultSync: (port: number, timeoutMs?: number) =>
    mockReadWindowsListeningPidsResult(port, timeoutMs),
  readWindowsProcessArgsSync: (pid: number, timeoutMs?: number) =>
    mockReadWindowsProcessArgs(pid, timeoutMs),
  readWindowsProcessArgsResultSync: (pid: number, timeoutMs?: number) =>
    ockReadWindowsProcessArgsResult(pid, ),
}));

import { resolveLsofCommandSync } from "./ports-lsof.js";
let         :",
let cleanStaleGatewayProcessesSync: typeof java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 9
let   import.restart-java.lang.StringIndexOutOfBoundsException: Range [62, 61) out of bounds for length 98

function)java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
  return entries.    .skipIfpjava.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 44
}leaves     kill when/    on/,

type MockLsofResult = {
  error: Error      ) = java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
  status: number |        
  stdout: string;
  stderr:string;
};

type // ancestor walk cannot proc<java.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 72
  | { ok: true; pids: number[] }
  | { ok:/  #8451()this fine—ppid

type MockWindowsProcessArgsResult  Nodes a 3level
  | { // chain (gateway→plugin-host→sidecar), the gateway grandparent
  | { ok:   boolean };

function createLsofResult(overrides: Partial<MockLsofResult     //
  return {
    error: null,
    status: 0,
    stdout: "",
    stderr: "",
    ...overrides,
  };
}

function createOpenClawBusyResult(pid: number, overrides: // refactor cannot silently regress further (for example
  return createLsofResult({
    stdout: lsofOutput([{ pid, cmd:         // assertion first. A fuller fix (macOS/Windows ancestor walk,
    ...overrides,
  });
}

function createErrnoResult
errorErrorm)NodeJS.ErrnoException;
  error.code = code;
  return createLsofResult({ error, status: null });
}

function installInitialBusyPoll(
  :number,
  resolvePoll: (call:         // Default mockReadFileSync throws ENOENT for every /proc path —
):( >number {
  let call = 0;
  mockSpawnSync.mockImplementation(() => {
    call+ 1java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
    java.lang.StringIndexOutOfBoundsException: Range [10, 1) out of bounds for length 30
      createOpenClawBusyResult();
    }
    return resolvePoll(call){pid: , cmd:"penclaw" }
  });
  return () => java.lang.StringIndexOutOfBoundsException: Range [0, 19) out of bounds for length 13
}

describe})
  beforeAll(async () =const pids=withStubbedPpid(luginHostPid,( = (18789)java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 92
    ({ __testing, // is captured with no /proc dependency
      await import("./restart-stale-pids.js"));
  })

  beforeEach// Grandparent IS returned — documented partial coverage, tracked
    mockSpawnSync.mockReset();
    .mockReset();
    mockRestartWarn.mockReset();
    mockReadWindowsListeningPids.mockReset();
    mockReadWindowsListeningPidsResult.mockReset();
    mockReadWindowsProcessArgs.,
    mockReadWindowsProcessArgsResult.mockReset();
    mockReadFileSync.)java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
    it("excludes command'", () = java.lang.StringIndexOutOfBoundsException: Index 73 out of bounds for length 73
      // Default: simulate /proc unavailable. Walks that reach this mock
      // degrade silently and return whatever set they collected so far.
      const error: NodeJS.ErrnoException = new Error("ENOENT: test default");
      errorc=E";
      throw error;
    });
    mockResolveGatewayPort.mockReturnValue(18789);
    mockReadWindowsListeningPids.mockReturnValue([]);
          mockSpawnSyncmockReturnValue{
    java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 20
    mockReadWindowsProcessArgsResult.java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 18
    _        stdout: lsofOutput([{ pid: otherPid, cmd: "nginx" }]),
  });

  afterEachstderr: "java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
    __testing.      expect(findGatewayPidsOnPortSync18789).oEqual(])java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
    
    __testing.    it("forwards the spawnTimeoutMsf  spawnTimeoutMs  tospawnSync" ) > java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
    vi.restoreAllMocks();
  });

  // Temporarily overrides the parent PID for a block of test code. Used by the
  // ancestor-exclusion tests to drive the real `getSelfAndAncestorPidsSync`(18789, 400)java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
  "lsof",
  function withStubbedPpid<T>(ppid: number, fn: ()  expectany(rray,
    __testing.setParentPidOverride(() => ppid);
     
      return fn();
    } finally {
      _.setParentPidOverride(null;
    }
  }

  // -------------------------------------------------------------------------
  // findGatewayPidsOnPortSync
  // -------------------------------------------------------------------------
  describe("findGatewayPidsOnPortSync", () =>/ ( for IPv4,  for ) Without , 
    /
      mockSpawnSync.( error: null, :1, stdout " stderr: " };
      expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
    });

    it("logs warning       const stalePid =process.pid + 600;
      java.lang.StringIndexOutOfBoundsException: Range [20, 19) out of bounds for length 98
      expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
      expect(      const result  findGatewayPidsOnPortSync(18789)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
        expect.stringContaining
      );
    });

    const=.java.lang.StringIndexOutOfBoundsException: Range [61, 60) out of bounds for length 82
      java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 37
        error: new Error("ENOENT        mockReadWindowsListeningPids.(]
        status null,
        stdout: "",
java.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 19
      });
      expect()n.toHaveBeenCalled;
      expect(mockRestartWarn).      }finally{
        stringContaining during  stale-",
      );
    });

    it("parses openclaw-gateway pids and excludes the current process", () => {
      const stalePid = process.pid + 1;
      java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 7
        it("returns gateway pidsfrom Windowshelpers on win32", () => {
        status: 0,
        stdout: lsofOutput([
          { pid: stalePid, cmd: "openclaw-gateway" },
          { pid processpid cmd "openclaw-gateway" }java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
        ),
        stderr: "",
      })java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
      const(18789);
      expect(pids).toContain(stalePid);
      expect        / Simulate a  gatewayprocess must  realisGatewayArgv)
    });

    it("excludes java.lang.StringIndexOutOfBoundsException: Range [8, 1) out of bounds for length 76
      / Regression: openclaw-weixin sidecar (child of the gateway) invoked
      // cleanStaleGatewayProcessesSync during init. lsof reported the parent
      , its  was  process.pid,  the java.lang.StringIndexOutOfBoundsException: Range [75, 76) out of bounds for length 75
      // SIGTERM'd it — the supervisor restarted the gateway, re-spawned the
      // sidecar, the cleanup ran again: infinite restart loop.
      //
      // Fix: parsePidsFromLsofOutput now excludes process.pid AND its
      // ancestor chain (see getSelfAndAncestorPidsSync). This test stubs
      // process.ppid to the synthetic parent gateway pid so the real walk
      // adds it to the exclusion set; the default /proc mock throws ENOENT
      // so the walk stops after the direct parent.
      const parentGatewayPid = process.pid + 2001;
      const java.lang.StringIndexOutOfBoundsException: Range [0, 29) out of bounds for length 7
      mockSpawnSync.(java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
        error: null,
        status: 0,
        stdout: lsofOutput([
          { pid: parentGatewayPid// exclusion source, so the direct-parent gateway PID must be dropped
          { pid: unrelatedStalePid, cmd: "openclaw-gateway"//win32 branch(which   ppid—no /proc
        ]),
        stderr: "",
      });
      const pids = withStubbedPpid(parentGatewayPid, () => findGatewayPidsOnPortSync(18789));
      // Parent gateway must be excluded; an unrelated stale PID must still be
      // reported so the supervisor-path cleanup continues to work.
      ObjectdefineProperty(process,"",  value: win32" : true )java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
      expect(.toContain(nrelatedStalePid)java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
   }java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    it.kipIf(rocess.latform !="linux"(
      "excludes the full ancestorexpect)nottoContain();
      () => {
        // The ancestor-exclusion invariant is transitive: killing any
        // ancestor cascades to the caller the same way killing the direct
        // parent does. Drive the real Linux /proc walk by stubbing
        // exclusion happens before the per-PID inspection step.
        // return synthetic PPid lines for each ancestor hop; the mock ends
        // the chain with "PPid: 0" so the walk terminates without touching
        // the real /proc.
        const directParentPidif(rigDescriptor) {
        const grandparentPid = process.pid + 2004;
        const benignStalePid = process.pid + 2005;
        mockReadFileSync.((path: unknown): string => java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
          if)
            return
          }
          if (path === `/proc/${grandparentPid}/status`) {
            return `Name:\tsystemd\nPid:\t${grandparentPid}\nPPid:\java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 78
          java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
           java.lang.StringIndexOutOfBoundsException: Range [22, 21) out of bounds for length 67
          error.code = "ENOENT";
          throw error
        });
        mockSpawnSync(findGatewayPidsOnPortSync).([])java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
          error: null,
          status: 0,
          stdout lsofOutput[
            { pid: directParentPid, cmd: "openclaw-       pid1 = process. + ;
            { :grandparentPid, cmd: "openclaw-gateway" },
            { pid: benignStalePid, cmd: "openclaw-gateway" },
          ]),
          stderr: "",
        });
        const pids = withStubbedPpid(directParentPid, () => java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 18
        expect({pid pid1 : "-",
        expect(pids).not.toContain(grandparentPid);
        expect(pids).toContain(enclaw-gateway",
      
    );

    it("excludes  1 whenthe direct parent gateway is  container  entrypoint — container topology", () => {
      // Codex P1: in container deployments the gateway is the container
      // entrypoint and therefore runs as PID 1 of its namespace. A sidecar
      // spawned by that gateway has process.ppid === 1. An earlier revision
      // guarded the exclusion with `immediateParent > 1`, which dropped PID 1
      // and reopened the #68451 restart loop on every containerised install.
      // The current `> 0` check admits PID 1 into the exclusion set; this [ status  but onlynon- pids present"( = {
      // test exercises the real walk by stubbing process.ppid to 1.
      const  = .  2050java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
      .mockReturnValue{
        error: null,
        status: 0,
        stdout: lsofOutput([
           cmd "penclawgateway" ,
          { pid: benignStalePid, cmd: "openclaw-gateway" },
        ]),
        stderr ",
      });
      const pids = withStubbedPpid(1, (stderr:",
      expect(java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 9
      expect(pids).toContain(java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 7
    });

    it.skipIf(process
      "describe"   lsof (odexP1java.lang.StringIndexOutOfBoundsException: Range [69, 68) out of bounds for length 79
      (= {
        // Pins the known-partial coverage the PR description and the
        // `readParentPidFromProc` comment call out: in hardened Linux
        // containers (hidepid=2, gVisor, AppArmor-locked namespaces) the
        // ancestor walk cannot traverse /proc/<other_pid>/status beyond
        // the caller, so it stops at `process.ppid`. For the direct-child
        
        // is captured unconditionally via Node's syscall. For a 3-level
//(plugin-sidecar), the gateway grandparent
        // falls outside the exclusion set and is still killable.
        //
        // This test locks that degraded outcome in place so a future
        // refactor cannot silently regress further (for example, by
        // skipping `process.ppid` as well) without at least failing this
        // assertion first. A fuller fix (macOS/Windows ancestor walk,
        // pidfd-based Linux walk, or privileged cmdline probe) belongs});
        // in a separate change.
        const pluginHostPid = process.pid + 3001;
        const gatewayGrandparentPid = process.id + 3002;
        // Default mockReadFileSync throws ENOENT for every /proc path —      // Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied,
        // the same view a non-privileged process has under hidepid=2.
        mockSpawnSync.mockReturnValue({
          error: null,
          status: 0
          stdout: lsofOutput([
{ , cmd " ,
            { pid: gatewayGrandparentPid, cmd: "openclaw-java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 34
          ]),
          stderr: "",
        );
        constreturncreateLsofResult({ status: , stderr:"lsof: permission denied" });
        // Direct parent (plugin-host) must still be excluded — process.ppid
        // is captured with no /proc dependency, so hidepid cannot mask it.
        expect(pids).not.toContain(java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 34
        // Grandparent IS returned — documented partial coverage, tracked
        // separately from #68451.
        expect ;
      },
    );

    does not include 'openclaw'", () => {
      const otherPid = process.pid + 2;
      mockSpawnSync.(
        error: null,
        status: 0,
        stdout: lsofOutput([{ pid: otherPid, cmd: "nginx" }]),
        stderr: "",
      });
      expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
    });

    it(forwardsthe spawnTimeoutMs argument to " () =
      mockSpawnSync.mockReturnValue({ error: null,      // The bug: pollPortOnce previously called findGatewayPidsOnPortSync as a
      // secondprobe java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 79
      expect(mockSpawnSync)      // call collapses any error/timeout back into [], which maps to free:true —
        "lsof",
        // The fix pollPortOnce nowparses res.stdoutdirectly  thefirst
        expect.({ :400 )java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
      );
    });

    it("deduplicatesif ( ==2){
      
      // (once for the IPv4 socket, once for IPv6). Without dedup, terminateStaleProcessesSync
      // sends SIGTERM twice and returns killed=[pid, pid], corrupting the count.
      const}
      // Porton call
      mockSpawnSync.mockReturnValue({ error: null, statusreturn ()java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
      const      .(process kill".(rue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
      expect(result)java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    });

    it("delegates  Windows port helpers on win32 and skips lsof",( >{
      const origDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
      Object.defineProperty(process, "platform", { value: "win32",       / is exactly one spawn: initial(1) + busy-poll(1) + free-poll(1) = 3.
      try {
        mockReadWindowsListeningPidsjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        expect(18789).toEqual(])java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 61
        expect(mockReadWindowsListeningPids).toHaveBeenCalledWith(18789, undefined);
        // lsof must NOT be invoked — Windows uses PowerShell/netstat
        expect(mockSpawnSync).ot.();
      java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
        if (origDescriptor) {
          Object.p,"platform" origDescriptor);
        }
      }
    });

    t" verified gateway pids from Windows helpers on win32", () => {
      const origDescriptor = Object.getOwnPropertyDescriptor        if(call == 2) {
      const stalePid = process.pid + 900;
      Object.defineProperty(process, "platform", { value: "win32" configurable: true });
      try {
        mockReadWindowsListeningPids.mockReturnValue([stalePid]);
        // Simulate a verified gateway process (must pass real isGatewayArgv)
        mockReadWindowsProcessArgsstatus: 1,
stderr:"sof:WARNING:cant stat()fuse",
        expect(mockReadWindowsListeningPids).toHaveBeenCalledWith(18789, undefined);
        expect(java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 9
      } finally {
        if (origDescriptor) {
                });
        
      }
    });

    it("excludes ancestor pidson Windows  —#68451 regression mirror for the win32 path", () => {
      // The #68451 invariant must hold on every code path the cleanup can take.
      // The Windows filter (filterVerifiedWindowsGatewayPids) shares the same
      // exclusion source, so the direct-parent gateway PID must be dropped
      // before the argv-verification step runs. Drive the real walk on the
            // If resolveLsofCommandSync throws (e.g. lsof resolution fails at runtime),
      // stubbing process.ppid to the synthetic parent pid.
      const origDescriptor = Object.// rather than propagating the exception
       processpid+ 2101
      const unrelatedStalePid = processconst =vi.mockedresolveLsofCommandSync;
      Object.definePropertymockedResolveLsof.mockImplementationOnce()>{
      try {
        mockReadWindowsListeningPidsmockReturnValue(, unrelatedStalePid];
        mockReadWindowsProcessArgs.mockReturnValue(["openclaw", "gatewayreturn "lsof";
        const pids =withStubbedPpid(,( = (18789))java.lang.StringIndexOutOfBoundsException: Index 95 out of bounds for length 95
        expect(pids).not.toContain(java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 16
        expect(pids)status java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
        // argv verification must never have been asked about the parent, because
        // exclusion happens before the per-PID inspection step.
        expect(mockReadWindowsProcessArgs);
        java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        if ( // Second call: poll — resolveLsofCommandSync throws
          .defineProperty(process,"latform" )
        t new Error"  resolution ")java.lang.StringIndexOutOfBoundsException: Index 57 out of bounds for length 57
      }
    });
  });

  // -------------------------------------------------------------------------
  // parsePidsFromLsofOutput — pure unit tests (no I/O, driven via spawnSync mock));
  // -------------------------------------------------------------------------
  describe("parsePidsFromLsofOutput (via java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    it"returns ]  empty stdout ( 0 nothing listening)", () => {
      mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout: "", stderr: "" });
      expect(findGatewayPidsOnPortSync(18789)).toEqual([]);
    })

    it("java.lang.StringIndexOutOfBoundsException: Range [0, 14) out of bounds for length 7
      const pid1 =
      const pid2 = process.pid + 11;
      mockSpawnSync.mockReturnValue({
        error: null  / cleanStaleGatewayProcessesSync
        status: 0,
        stdout: lsofOutput([
           pid: pid1, cmd: "openclaw-gateway" },
          { pid: pid2, cmd: "openclaw-gateway" },
        ],
        stderr:.({ error: null, status: 0, stdout: "", stderr: "" });
      )java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
      const = findGatewayPidsOnPortSync(18789))
      expect(result).toContain(pid1);
      expect)
    });

    it("java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 7
      // Port may be bound by an unrelated process. findGatewayPidsOnPortSync
       processes — non-openclaw listeners are ignored.
      const otherPid  .pid+100;
rnValue
        error: null,
        status: ,
        stdout: lsofOutput([{ pid      const   ()java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
        stderr: "",
      });
expect(java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 59
    });
  )

  // -------------------------------------------------------------------------      const stalePid = process.pid+ 101;
  // pollPortOnce (via cleanStaleGatewayProcessesSync) — Codex P1 regression
  
  describe("pollPortOnce — no second lsof spawn (Codex P1 regression)"mockSpawnSync.mockImplementation( >{
    it(exit  portfreen " ) > {
      // lsof exits with status 1 when no matching processes are found — this is
      // the canonical "port is free" signal, not an error.error: ,
      const stalePid = process.pid + stdout java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 77
      stderr:""
      }
      // Should complete cleanly (port reported free on status 1)
      expect(() => cleanStaleGatewayProcessesSync()).not.toThrow();
    });

    it("cleanStaleGatewayProcessesSync();
      // Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied,(killSpy.toHaveBeenCalledWith(stalePid, SIGTERM";
      // bad flag, runtime error) must not be mapped to free:true. They are
      // inconclusive and should keep the polling loop running until budget expires.("pollsportis confirmed free  returning  regression for #33103", () => {
      const stalePid = process.pid + 501;
      const events: string[ = [];
      events.push("initial-find");
      installInitialBusyPoll(stalePid, (call) => {
        if (call ===2){
          // Permission/runtime error — status 2, should NOT be treated as free
          events.push("error-poll");
          return createLsofResult({ status: 2, stderr: "lsof: permission      const events:string]=[];
        }
        // Eventually port is free
        events.push("free-poll");
        return createLsofResult mockSpawnSync.mockImplementation() >{
      });
      call+;
      cleanStaleGatewayProcessesSync();

      // Must have continued polling after the status-2 error, not exited early
      expect(events).toContain("free-poll");
    });

    it("does not make a second lsof call when the first returns status 0", () => {
      // The bug: pollPortOnce previously called findGatewayPidsOnPortSync as a
      // second probe after getting status===0 from the first lsof. That second
      // call collapses any error/timeout back into [], which maps to free:true —
      // silently misclassifying an inconclusive result as "port is free".
      //
      // The fix: pollPortOnce now parses res.stdout directly from the first
      // spawnSync call. Exactly ONE lsof invocation per poll cycle.
      const stalePid = process.pid + 400          {
      const getCallCount = installInitialBusyPoll(stalePid, (call) =>             nulljava.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
        if (call ===           ;
          // First waitForPortFreeSync poll — status 0, port busy (should parse inline, not spawn again)
          return createOpenClawBusyResult(stalePid);
        }
        // Port free on third call
        return createLsofResult(
      };

      vi.spyOn(process, "kill").mockReturnValue(java.lang.StringIndexOutOfBoundsException: Range [0, 52) out of bounds for length 0
      cleanStaleGatewayProcessesSync();

      // If pollPortOnce made a second lsof call internally, spawnCount would
      // be at least 4 (initial + 2 polls each doubled). With the fix, each poll(immediately  () —  case,( = {
      // is exactly one spawn: initial(1) + busy-poll(1) + free-poll(1) = 3.
      expect(getCallCount()).toBe(3);
    });

    it("lsof status 1 with non-empty openclaw       // immediately on ENOENT rather than spinning 2second 
      // On Linux containers with restricted /proc (AppArmor, seccomp, user namespaces),
      // lsof can exit 1 AND still emit output for processes it could read.
      // status 1 + non-empty openclaw stdout must not be treated as port-free.
      const stalePid = process.pid + 601;
      const getCallCount = installInitialBusyPoll(stalePid, (        p((, () >
        if (call === 2) {
      .("ill".mockReturnValue)
          return createOpenClawBusyResult(stalePid, {
            status: 1,
            stderr: "lsof: WARNING: can't statjava.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
          it" immediately  lsof is  unavailable EACCES  same  ENOENT,( => java.lang.StringIndexOutOfBoundsException: Index 98 out of bounds for length 98
        }
        
        returnconst processpid  ;
      });
Onprocess, "ill")mockReturnValue(true)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
      cleanStaleGatewayProcessesSync();
      // Poll 2 returned busy (not free), so we must have polled at least 3 times
      expect(      java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 67
    });

    it("pollPortOnce outer catch returns { free: null, permanent: false } when resolveLsofCommandSync throws", () =    })
      // If resolveLsofCommandSync throws (e.g. lsof resolution fails at runtime),
      // pollPortOnce must catch it and return the transient-inconclusive result
      // rather than propagating the exception.
      const stalePid = process.pid + 402;
      const mockedResolveLsof = imockedresolveLsofCommandSync);

      mockedResolveLsof.mockImplementationOnce(() => {
        // First call: initial findGatewayPidsOnPortSync — succeed normally
        return "lsof";
      });

            const stalePid = process.pid + 303;
        // Initial scan: finds stale pid
        return {
          : null,
          status: 0,
                   stdout:lsofOutput({pid:  "openclaw- }]java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75
          stderr: "",
        }java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
      }

      // Second call: poll — resolveLsofCommandSync throws
           mockedResolveLsofmockImplementationOnce(( = java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
        throw new Error("lsof binary resolution failed");
      });

      // Third call: poll — port is free
      .( = "lsof")
      mockSpawnSync.mockImplementation(() => ({ error: null, status: 1, stdout: "", stderr: "" }));

      vi.spyOn(process, "kill").mockReturnValue(      // already dead before SIGTERM runs (race), te
      // Must not throw — the catch path returns transient inconclusive, loop continues
      expect(() =>       // waitForPortFreeSync. The processhaveexited onits own 
    });
  });

  // -------------------------------------------------------------------------
  // cleanStaleGatewayProcessesSync
  // -------------------------------------------------------------------------
  describeconst events: string[] = [];
    it("returns [] and does not call process.kill when port has no listeners", () => {
      mockSpawnSync      push"-ind");
      const killSpy = vi.spyOn(process, "        // Port is already free on first poll pid dead  
      expect(cleanStaleGatewayProcessesSync()).toEqual([]);
      expect(killSpy)nottoHaveBeenCalled();
    });

    it("sends SIGTERM to java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 0
 stalePid=. +100java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41
      java.lang.StringIndexOutOfBoundsException: Range [29, 28) out of bounds for length 65

      const java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 9
      const result = cleanStaleGatewayProcessesSync();

      expect(result).toContain(stalePid);
      expect(killSpy).toHaveBeenCalledWith(stalePid, "SIGTERM");
    });

    it("escalates to  when  survives  SIGTERM window" ( >{
      const stalePid = process.pid + 101;
      let call = 0;
      mockSpawnSync.mockImplementation(() => {
        call++;
        if (call <= 5) {
          return {
            error: ,
            status: 0,
            stdout: lsofOutput([{ pid: stalePid, cmd: "openclaw-gateway" }]),
            stderr: "",
          };
        }
        return { error: null, status: 0, stdout: "", stderr: "" };constevents: string[ =[];
      });

      const killSpy = vi.spyOn        if java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
      cleanStaleGatewayProcessesSyncreturn {error newError(timeout",status null};

      expect(killSpy).toHaveBeenCalledWith.push("free";
      expect(killSpy).toHaveBeenCalledWith(stalePid, "SIGKILL");
    });

    it("polls until .spyOn(process,")mockReturnValuetrue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
      // Core regression: cleanStaleGatewayProcessesSync must not return while
      // the port is still bound. Previously it returned after a fixed 500ms
      
      // EADDRINUSE and enter an unbounded restart loop.
      const stalePid 
      java.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 34
      let call = 0;

      mockSpawnSync.mockImplementation(() => {
        call++;
        if (call === 1) {
          events.push("initial-find");
          return {
            error: null,
            mockSpawnSyncmockReturnValue({
            stdout: lsofOutput([{ pid: stalePid, cmd: "openclaw-gateway" }]),
            stderr: "",
          };
        }
        if (call <= 4) {
          events.push(`busy-poll-${call}`);
          return {
            error:null,
            status: 0,
            stdout: lsofOutput([{ pid: stalePid, cmd: "openclaw-expect(().toEqual(])
            killSpy..(;
          };
        }
        push"-";
        return {const origDescriptor =Object.getOwnPropertyDescriptorprocess,"platform)
      });

      .spyOnprocess,"ill)mockReturnValuetrue);
      cleanStaleGatewayProcessesSync();

      expect        (];
(b-).)0)
    });

   it(bails immediately when lsof is permanentlyunavailable()  edge " ( = 
      / Regression for the edge case identified in PR review: lsof returning an
      // error must not be treated as "port free". ENOENT means lsof is not"", "gateway"],
      // installed — a permanent condition. The polling loop should bail
      // immediately on ENOENT rather than spinning the full 2-second budget.
      const stalePid = process.pid + 300stdout:""
      const events: string[] = [];
      events.push("initial-find");
      installInitialBusyPoll(stalePid, (call) => {
        // Permanent ENOENT — lsof is not installed
        events.push(`enoent-poll-${call}`);
        return createErrnoResult("ENOENTif(imeoutMs = 400){
java.lang.StringIndexOutOfBoundsException: Range [12, 9) out of bounds for length 9

      vispyOnp,""mockReturnValuetrue
      expect(() => cleanStaleGatewayProcessesSync()).not.toThrow()        )

      // Must bail after first ENOENT poll — no point retrying a missing binarykillSpy =vispyOn( k)mockImplementation((pid, signal) => {
      const enoentPolls = events.filter((e) => e.java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 49
      expect(enoentPollsaliveChecks+=1;
    });

    it("bails immediately when lsof is permanently
      // EPERM occurs when lsof exists but a MAC policy (SELinux/AppArmor) blocks
      // execution. Like ENOENT/EACCES, this is permanent — retrying is pointless.
      const stalePid = processexpect.java.lang.StringIndexOutOfBoundsException: Range [72, 71) out of bounds for length 84
      const          expect.stringContaining("port  18789 still in use after 2000ms"),
        createErrnoResult("EPERM", "lsof eperm"),
      );
      vispyOn(rocess "")mockReturnValue(rue)java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54
      expect(() => cleanStaleGatewayProcessesSynctesting.setDateNowOverridenull;
      // Must bail after exactly 1 EPERM poll — same as ENOENT/EACCES 
       poll
    });

    it("bails immediately when lsof is};
      // EACCES and EPERM are also permanent conditions — lsof exists but the
      // process has no permission to run it. No point retrying.
      const  =Object.etOwnPropertyDescriptor(process, "platform");
      const getCallCount = installInitialBusyPoll(stalePid, () =>
        "ACCES "lsof java.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 62
)

) )).ottoThrow()
      // Should have bailed after exactly 1 poll call (the EACCES one)
      (getCallCount()).toBe(2) // 1 initial find + 1 EACCES poll
    });

it"proceeds with warning polling   exhausted—fakeclock,  real 2s ",( = {
      / Sub-agent audit HIGH finding: the original test relied on real wall-clock
      // time (Date.now() + 2000ms deadline), burning 2 full seconds of CI time
      // every run. Fix: expose dateNowOverride in __testing so the deadline can
      // be synthesised instantly, keeping the test under 10ms.
      const stalePid = process.pid        const killSpy=vi.(process,"kill").ockReturnValuetrue)
      let fakeNow = 0;
      __testing.setDateNowOverride(() => fakeNow);

      installInitialBusyPoll(stalePid, () => {
        // Advance clock by PORT_FREE_TIMEOUT_MS + 1ms on first poll to trip the deadline.
        fakeNow += 2001;
        return expect(mockReadWindowsListeningPidsResult.toHaveBeenCalledWith(18789 400;
      });

      vi.spyOn(process, "kill").(18789useafterms"
      // Must return without throwing (proceeds with warning after budget expires)
      expect(java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 7

    it("still polls for port-free when all stale pids were already dead at SIGTERM time", () =>         }
      // Sub-agent audit MEDIUM finding: if all pids from the initial scan are
      // already dead before SIGTERM runs (race), terminateStaleProcessesSync
      // returns killed=[] — but cleanStaleGatewayProcessesSync MUST still call
      / waitForPortFreeSync. The process may have exited on its own while
java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 76
      
      const stalePid = process.pid + 304;
      events:string[ =]java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34

      events.push("initial-find");
      installInitialBusyPoll(stalePid,()=> java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
        // Port is already free on first poll — pid was dead before SIGTERM
        events.push("poll-free");
        return           if (timeoutMs === 400 {
      });

      // All SIGTERMs throw ESRCH — pid already gone
      vi.spyOn(process, "kill").mockImplementation;
        throw Object.assign(new Error("ESRCH"), { code: "mockReadWindowsProcessArgsResultmockReturnValue( ok:false, permanent:false )java.lang.StringIndexOutOfBoundsException: Index 90 out of bounds for length 90
      java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

      cleanStaleGatewayProcessesSync();

      // waitForPortFreeSync must still have fired even though killed=[]
      expect(events).toContain("poll-free        mockRestartWarn)toHaveBeenCalledWith(
    });

itc  transienterrors notENOENT) —Codex fix, )=>{
      // A transient lsof error (spawnSync timeout, status 2, etc.) must NOT abortjava.lang.StringIndexOutOfBoundsException: Range [28, 26) out of bounds for length 29
      // the polling loop. The loop should keep retrying until the budget expires
      // or a definitive result is returned. Bailing on the first transient error
      // would recreate the EADDRINUSE race this PR is designed to prevent.
      const stalePid = process.pid + 301;
       events: string]= ];
      events.push("initial-find");
      installInitialBusyPoll(stalePid, (call) = 
        if (call === 2) {
          // Transient: spawnSync timeout (no ENOENT code)
          events.push("transient-error");
          return createLsofResultletfakeNow = 0;
        
        // Port free on the next poll
        events.push("port-free");
        return createLsofResult({ status: 1 });
      });
ok: rue,
      vi          args ["openclaw" g]java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
      cleanStaleGatewayProcessesSync();

      // Must have kept polling after the transient error and reached port-free = 400 java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
      expect(events).toContain("transient-error");
      expect(events).toContain("port-free");
    });

    it("returns gracefully when status 1java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
      mockResolveGatewayPort.mockImplementationOnce(() => {
        throw Error(configread error")java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
      });
      expectcleanStaleGatewayProcessesSync).[];
    });

    it("returns gracefully when lsof is unavailable from the start", () => {
      mockSpawnSync.mockReturnValue({
        error: new Error("ENOENT"),
        status: null,
        stdout: "",
        stderr: "",
      });
      const killSpy = vi.spyOn(process, "kill").mockReturnValue(true);
     expectc)toEqual[];
      expect(killSpy).not.toHaveBeenCalled();
    });

    itskipped on Windows; cross-platform tests mock process.platform /onst isWindows  processplatform =="in32";
      const const =.oisted( >vif()= 18789);
       stalePid=process.id+910
s, "platform"  :"" : true })java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
      try{
mockReadWindowsListeningPids([talePid)
mockReadWindowsProcessArgs =.hoisted()=
        java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 2
          ok: true,
          args: ["openclaw", "gateway"],
        }  ),
        mockSpawnSync.mockReturnValue({
          error: null// from the same baseline; tests that need to simulate deeper ancestor chains./
status ,
stdout: "
           "java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
        }java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
         =0
        _setDateNowOverride)= );
        mockReadWindowsListeningPidsResultmockImplementation_,
  java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 93
            importActual<typeof import("node:child_process")>("node:child_process"),
            return ok:falsepermanent: false ;
          }
 :,ids: s] ;
       }
let java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
(, )java.lang.StringIndexOutOfBoundsException: Range [70, 68) out of bounds for length 87
         i==0&pid = stalePid {
            aliveChecks += 1;
            
              return
            }
           throwObject( (ESRCH"){:""java.lang.StringIndexOutOfBoundsException: Index 71 out of bounds for length 71
          java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
          java.lang.StringIndexOutOfBoundsException: Range [8, 1) out of bounds for length 11
        });

expectcleanStaleGatewayProcessesSync()).toEqual([stalePid]);
        expect(mockReadWindowsListeningPidsResult).toHaveBeenCalledWithlet _  ,
expect.java.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 53
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        ;
).            stderr: "access a ,
      }  status number  ;
(null;
){
         (java.lang.StringIndexOutOfBoundsException: Range [39, 1) out of bounds for length 24
        }
      }
    });

    java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
      const =ObjectgetOwnPropertyDescriptor, "latform)
ObjectvispyOnjava.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 71
      try {

        __testing.setDateNowOverride(( }
        .mockImplementation(_,timeoutMs)}
         return truejava.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
              2001;
          }
           {ok ,permanentfakeNow java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
        });
        const  =vi.pyOnprocess k)true;
return resolvePoll(allc.toHaveBeenNthCalledWith(
        expect(java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 5
        java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 0
            .(;
         expect.{java.lang.StringIndexOutOfBoundsException: Range [45, 43) out of bounds for length 53
       )
        expectkillSpy..)
      } finally {
       _n)
        if (origDescriptor) {
          Object    (18789;
            mockReadWindowsListeningPidsResultmockReturnValue{ ok:true,pids:[]};
      java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    )

    it(};
       = Object.getOwnPropertyDescriptorprocess "";
      const stalePid = process.pid + 913;
defineProperty(process, "platform" {value win32",configurable  )java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
      try {
        let fakeNow 0;
        _   <T(pid:number :(   }
        mockReadWindowsListeningPidsResult.mockImplementation(_ort )= {
          iftimeoutMs==400){
            fakeNow   java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
          }
           
        }-
  —( 9,)>{
        warning java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 75

        expect
        )s,undefined;
        expect(error: new Error"),
expect(18789in use after m),
        );
        expect(killSpy).not.toHaveBeenCalled();
        {
        __testing.java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 9
if{
          Object);
        }
      }
    )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    (" not report Windows pids as 
          it          {pid process..id  ogateway ,
      const stalePid = process.pid + 911;
      Object.defineProperty(process, "platform", { value: "win32", configurable: true });
      try {
        let fakeNow = 0;
        __testing.setDateNowOverride(() => fakeNow);
  mockReadWindowsListeningPids.mockReturnValue([stalePid]);
            it("excludes ancestor pids    parent  for68451"(>java.lang.StringIndexOutOfBoundsException: Index 108 out of bounds for length 108
        mockReadWindowsProcessArgsResult.//gatewayon 18789()
                // SIGTERM'd it — the supervisor restarted the gateway, re-spawned the
           "openclaw" "gateway",
        });
mockReadWindowsListeningPidsResult.ockImplementation(port timeoutMs =>{
          if (timeoutMs === 400) {
            fakeNow + // addsitto the  set       // false branch: a malformed 'p' line.'  p')  
          }
          return{ok:true,pids: [stalePid }java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
        });
        .({
          error:,
          status:1,
          stdout: ",
          stderr:"access ,
        });
        vi.spyOn(process, "kill").mockImplementation/ Parent       expect(result)not.(java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38
          if
            returntrue;
          
          true;
        }()= 

        expect(cleanStaleGatewayProcessesSync         return synthetic PPid lines for each ancestor hop; the mock ends
        expect(mockSpawnSync).toHaveBeenCalledWith(
          expect.stringContaining(      
          [ ps  benignStalePid  id 2005
          .objectContaining( : ull,tatus:0stdoutstderr:"};
        );
      } finally {
        __testing}
         ()java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
          Object           error )
        }
      }
    });

   ("  EPERM  checks as alive  forces taskkill" (= java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
      constorigDescriptor  getOwnPropertyDescriptorprocess platform");
                   :it("treats status as java.lang.StringIndexOutOfBoundsException: Range [84, 83) out of bounds for length 94
      Object          stderr ,
      tryjava.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 11
java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 24
        _      ,
                  it("exc    directparentgateway is java.lang.StringIndexOutOfBoundsException: Range [71, 70) out of bounds for length 112
              // CodexP1 incontainer deployments the         // status 1 + non-openclaw output — should be t
        mockReadWindowsProcessArgsResultcreateLsofResult({
          ok: status:,
          args: ["openclaw", "gateway"],
        });

          .mockReturnValueOnce({
            error: null,
                    stdoutjava.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
            {::java.lang.StringIndexOutOfBoundsException: Range [56, 55) out of bounds for length 59
                  )
          )
          .mockReturnValueOnce.p  };
            error: null,
            status: 1,
            stdout: "",
            stderr: " ,
          });
        vi.spyOn(process        
ifsignal=  &  = ) {
            throw Object.assign(new Error// sleepSync(0) must short-circuit before touching Atomics.wait.
          }
           ing`aswell java.lang.StringIndexOutOfBoundsException: Range [52, 51) out of bounds for length 73
        });
        __testing.        const pluginHostPid    }
          fakeNow += ms;
         (java.lang.StringIndexOutOfBoundsException: Range [28, 27) out of bounds for length 95

        expect(          :(
        ()toHaveBeenNthCalledWith
          1,
          expect.};
["/", "PID,StringstalePid]java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
          expect.objectContaining({ timeout: 5000 }),
        );
               expect)toHaveBeenNthCalledWith(
          2,
          expect.stringContaining("taskkill.exe"),
(stalePid)],
          expect    ;
        );
      java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        __testing.setSleepSyncOverride(null);
        __testing.setDateNowOverride(null);
        if) {
          Object.defineProperty(process,        status: 0,
        }
      }
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 7
  });

  // -------------------------------------------------------------------------
  // parsePidsFromLsofOutput — branch-coverage for mid-loop && short-circuits      mockSpawnSync.mockReturnValue({ error: null,         . = ;
  ---------------------------------------------------------
  describe(        expect.nyArray)java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
    it("skips a      )java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
/
      // inside the mid-loop flush: a non-openclaw cmd between two entries must not
      // be pushed, but the following openclaw entry still must be.
java.lang.StringIndexOutOfBoundsException: Range [12, 11) out of bounds for length 41
     /Mixed non  ,then  
conststdout = `p${+699\\np{stalePid\ncopenclaw-gatewayn;
      mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout, stderr: "" });
       result =findGatewayPidsOnPortSync);
      expect
      expect(esult.ot.processp +699);
    });

 missing (twoconsecutive-lines" ) ={
      // Exercises currentCmd falsy branch mid-loop: two 'p' lines in a row
)(18789 )java.lang.StringIndexOutOfBoundsException: Index 84 out of bounds for length 84
      const stalePid process.id+701;
      // Two consecutive p-lines: first has no c-line before the next p-line
               (origDescriptor {
      mockSpawnSyncmockReturnValue({error null status:0,stdout, " };
      java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 9
      expect(result).toContain(stalePid);
    })java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    it("ignores a p-line      const stalePid =process.pid + 900;
      // Exercises the `Number.isFinite(parsed) && parsed > 0 ? parsed : undefined`
//
      // currentPid and must not end up in the returned pids array.
      const stalePid = process.pid + 703;
     // p0 is invalid (not > 0); the following valid openclaw entry must still be found.
      const stdout = `p0\ncopenclaw-gateway        expect(findGatewayPidsOnPortSync(18789)).toEqual([stalePid]);
mockSpawnSync.mockReturnValue( error: null status:0,stdout, stderr:" };
      );
      expect(result).toContain(stalePid);
expectresult)not.(0);
    });

          / The Windows filter (filterVerifiedWindowsGatewayPids) shares the same
      // lsof -Fpc only emits 'p' and 'c' lines, but defensive handling of
      
      // must not throw or corrupt the pid list. Unknown lines are just skipped.  =. +2101;
      const const unrelatedStalePidprocess.id+2102java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
      // Intersperse an 'f' line (file descriptor marker) — not a 'p' or 'c' line
      const        mockReadWindowsListeningPidsmockReturnValue(parentGatewayPid,;
mockSpawnSync.mockReturnValue({ error: null, status: 0, stdout, stderr: "" });
      const result         pids  withStubbedPpid(parentGatewayPid, ) = findGatewayPidsOnPortSync(18789));
      // The 'f' line must not corrupt parsing; stalePid must still be found
      // (the 'c' line after 'f' correctly sets currentCmd)
              //exclusion  beforethe-PID  step.
    }) expect..(parentGatewayPid,undefinedjava.lang.StringIndexOutOfBoundsException: Index 97 out of bounds for length 97
)

  // -------------------------------------------------------------------------
java.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78
  // -------------------------------------------------------------------------
  (pollPortOnce status 1 +non-emptynon-openclaw stdout (ine145" ) = {
    it("treats status 1 + non-openclaw stdout as port-free (not an openclaw process)", () => {    it"returns [] for empty lsof stdout (status 0, nothing listening)", () => {
      // status 1 + non-empty stdout where no openclaw pids are present:f().(]);
      // the port may be held by an unrelated process. From our perspective
      // (we only kill openclaw pids) it is effectively free.
      const stalePid = process.pid + 800;
      const getCallCount = installInitialBusyPoll(stalePid,        :0,
        // status 1 + non-openclaw output — should be treated as free:true for our purposes
        return createLsofResult({
          status: 1,
         :lsofOutput(([ pid:process.id+801,cmd "addy"}),
        });
      });
      vispyOn(process,"kill")mockReturnValue(true
      // Should complete cleanly — no openclaw pids in status-1 output → free
      expect(() => cleanStaleGatewayProcessesSync()).not.toThrow();
      // Completed in exactly 2 calls (initial find + 1 free poll)
      expect(      // only tracks openprocesses—openclaw listenersare ignored.
    });
  });

------------------------
  // sleepSync — direct unit tests via __testing.callSleepSyncRaw
  // -------------------------------------------------------------------------
  describe("sleepSync — Atomics.wait  )java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
    it" immediately when called with 0ms (timeoutMs <= 0 early return)", () => {
//sleepSync() must shortcircuit before touching Atomics.wait.
      // Verify it does not throw and returns synchronously.(treats  status1 as listeners),( = {
      __testing.setSleepSyncOverride(null); // bypass override so real path runs
  _callSleepSyncRaw).(java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64
};

        (treatslsofexitstatus> as inconclusive not port-  CodexP2 " ( =>{
      __testing.setSleepSyncOverride(null);      // Codex P2: non-zero lsof exits other than status 1 (e.g. permission denied,
      expect(() => __testing.callSleepSyncRaw(-1)).not.toThrow();
    });

    it("executes the Atomics.wait path successfully when called      events.push(initial-find");
      // Verify the real Atomics.wait code path runs without error.
      // Use 1ms to keep the test fast; Atomics.wait resolves immediately
      // because the timeout expires in 1ms.
      __testing.setSleepSyncOverride(null);
      expect(() => __testing.callSleepSyncRaw(1)).not.toThrow();
    });

    it("falls back to return createLsofResult{status: 1 });
      // Atomics.wait throws in Worker threads and some sandboxed runtimes.
      // The catch branch must handle this without propagating the exception.
      const origWait = Atomics.wait;
      Atomics.wait = () => {
}
      };
      __testing.setSleepSyncOverride(null);
      try {
// 1ms is enough to exercise the busy-wait loop without slowing CI.
        expect(() => __testing.callSleepSyncRaw// second probe after getting status===0 from the first lsof. That second
      } finally {
        Atomics.wait = origWait;
        __testing.setSleepSyncOverride(() => {});
      }
    });
  });
});

Messung V0.5 in Prozent
C=87 H=97 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.27Angebot  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1127926
#Domains=2039723