mirror of https://github.com/openclaw/openclaw.git
test: tighten system event and lsof helper coverage
This commit is contained in:
parent
bb84e5e82e
commit
a9b5fe4099
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -3,7 +3,15 @@ import { drainFormattedSystemEvents } from "../auto-reply/reply/session-updates.
|
||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { resolveMainSessionKey } from "../config/sessions.js";
|
import { resolveMainSessionKey } from "../config/sessions.js";
|
||||||
import { isCronSystemEvent } from "./heartbeat-runner.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 cfg = {} as unknown as OpenClawConfig;
|
||||||
const mainKey = resolveMainSessionKey(cfg);
|
const mainKey = resolveMainSessionKey(cfg);
|
||||||
|
|
@ -56,6 +64,50 @@ describe("system events (session routing)", () => {
|
||||||
expect(second).toBe(false);
|
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 () => {
|
it("filters heartbeat/noise lines, returning undefined", async () => {
|
||||||
const key = "agent:main:test-heartbeat-filter";
|
const key = "agent:main:test-heartbeat-filter";
|
||||||
enqueueSystemEvent("Read HEARTBEAT.md before continuing", { sessionKey: key });
|
enqueueSystemEvent("Read HEARTBEAT.md before continuing", { sessionKey: key });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue