test: tighten system event and lsof helper coverage

This commit is contained in:
Peter Steinberger 2026-03-13 19:35:27 +00:00
parent bb84e5e82e
commit a9b5fe4099
2 changed files with 120 additions and 1 deletions

View File

@ -0,0 +1,67 @@
import fs from "node:fs";
import fsPromises from "node:fs/promises";
import { afterEach, describe, expect, it, vi } from "vitest";
import { resolveLsofCommand, resolveLsofCommandSync } from "./ports-lsof.js";
const LSOF_CANDIDATES =
process.platform === "darwin"
? ["/usr/sbin/lsof", "/usr/bin/lsof"]
: ["/usr/bin/lsof", "/usr/sbin/lsof"];
describe("lsof command resolution", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("prefers the first executable async candidate", async () => {
const accessSpy = vi.spyOn(fsPromises, "access").mockImplementation(async (target) => {
if (target === LSOF_CANDIDATES[0]) {
return;
}
throw new Error("unexpected");
});
await expect(resolveLsofCommand()).resolves.toBe(LSOF_CANDIDATES[0]);
expect(accessSpy).toHaveBeenCalledTimes(1);
});
it("falls through async candidates before using the shell fallback", async () => {
const accessSpy = vi.spyOn(fsPromises, "access").mockImplementation(async (target) => {
if (target === LSOF_CANDIDATES[0]) {
throw new Error("missing");
}
if (target === LSOF_CANDIDATES[1]) {
return;
}
throw new Error("unexpected");
});
await expect(resolveLsofCommand()).resolves.toBe(LSOF_CANDIDATES[1]);
expect(accessSpy).toHaveBeenCalledTimes(2);
accessSpy.mockImplementation(async () => {
throw new Error("missing");
});
await expect(resolveLsofCommand()).resolves.toBe("lsof");
});
it("mirrors candidate resolution for the sync helper", () => {
const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation((target) => {
if (target === LSOF_CANDIDATES[0]) {
throw new Error("missing");
}
if (target === LSOF_CANDIDATES[1]) {
return undefined;
}
throw new Error("unexpected");
});
expect(resolveLsofCommandSync()).toBe(LSOF_CANDIDATES[1]);
expect(accessSpy).toHaveBeenCalledTimes(2);
accessSpy.mockImplementation(() => {
throw new Error("missing");
});
expect(resolveLsofCommandSync()).toBe("lsof");
});
});

View File

@ -3,7 +3,15 @@ import { drainFormattedSystemEvents } from "../auto-reply/reply/session-updates.
import type { OpenClawConfig } from "../config/config.js";
import { resolveMainSessionKey } from "../config/sessions.js";
import { isCronSystemEvent } from "./heartbeat-runner.js";
import { enqueueSystemEvent, peekSystemEvents, resetSystemEventsForTest } from "./system-events.js";
import {
drainSystemEventEntries,
enqueueSystemEvent,
hasSystemEvents,
isSystemEventContextChanged,
peekSystemEventEntries,
peekSystemEvents,
resetSystemEventsForTest,
} from "./system-events.js";
const cfg = {} as unknown as OpenClawConfig;
const mainKey = resolveMainSessionKey(cfg);
@ -56,6 +64,50 @@ describe("system events (session routing)", () => {
expect(second).toBe(false);
});
it("normalizes context keys when checking for context changes", () => {
const key = "agent:main:test-context";
expect(isSystemEventContextChanged(key, " build:123 ")).toBe(true);
enqueueSystemEvent("Node connected", {
sessionKey: key,
contextKey: " BUILD:123 ",
});
expect(isSystemEventContextChanged(key, "build:123")).toBe(false);
expect(isSystemEventContextChanged(key, "build:456")).toBe(true);
expect(isSystemEventContextChanged(key)).toBe(true);
});
it("returns cloned event entries and resets duplicate suppression after drain", () => {
const key = "agent:main:test-entry-clone";
enqueueSystemEvent("Node connected", {
sessionKey: key,
contextKey: "build:123",
});
const peeked = peekSystemEventEntries(key);
expect(hasSystemEvents(key)).toBe(true);
expect(peeked).toHaveLength(1);
peeked[0].text = "mutated";
expect(peekSystemEvents(key)).toEqual(["Node connected"]);
expect(drainSystemEventEntries(key).map((entry) => entry.text)).toEqual(["Node connected"]);
expect(hasSystemEvents(key)).toBe(false);
expect(enqueueSystemEvent("Node connected", { sessionKey: key })).toBe(true);
});
it("keeps only the newest 20 queued events", () => {
const key = "agent:main:test-max-events";
for (let index = 1; index <= 22; index += 1) {
enqueueSystemEvent(`event ${index}`, { sessionKey: key });
}
expect(peekSystemEvents(key)).toEqual(
Array.from({ length: 20 }, (_, index) => `event ${index + 3}`),
);
});
it("filters heartbeat/noise lines, returning undefined", async () => {
const key = "agent:main:test-heartbeat-filter";
enqueueSystemEvent("Read HEARTBEAT.md before continuing", { sessionKey: key });