test: strengthen regression coverage and trim low-value checks

This commit is contained in:
Peter Steinberger
2026-03-22 07:37:17 +00:00
parent f537ea90ed
commit b4656f193a
28 changed files with 656 additions and 212 deletions

View File

@@ -97,7 +97,6 @@ async function expectActiveInProcessLockIsNotReclaimed(params?: {
describe("acquireSessionWriteLock", () => {
it("reuses locks across symlinked session paths", async () => {
if (process.platform === "win32") {
expect(true).toBe(true);
return;
}
@@ -110,12 +109,24 @@ describe("acquireSessionWriteLock", () => {
const sessionReal = path.join(realDir, "sessions.json");
const sessionLink = path.join(linkDir, "sessions.json");
const realLockPath = `${sessionReal}.lock`;
const linkLockPath = `${sessionLink}.lock`;
const lockA = await acquireSessionWriteLock({ sessionFile: sessionReal, timeoutMs: 500 });
const lockB = await acquireSessionWriteLock({ sessionFile: sessionLink, timeoutMs: 500 });
await lockB.release();
await lockA.release();
await expect(fs.access(realLockPath)).resolves.toBeUndefined();
await expect(fs.access(linkLockPath)).resolves.toBeUndefined();
const [realCanonicalLockPath, linkCanonicalLockPath] = await Promise.all([
fs.realpath(realLockPath),
fs.realpath(linkLockPath),
]);
expect(linkCanonicalLockPath).toBe(realCanonicalLockPath);
await expectLockRemovedOnlyAfterFinalRelease({
lockPath: realLockPath,
firstLock: lockA,
secondLock: lockB,
});
} finally {
await fs.rm(root, { recursive: true, force: true });
}

View File

@@ -1,19 +1,5 @@
import { describe, expect, it } from "vitest";
import * as channelWeb from "../channel-web.js";
import { afterEach, describe, expect, it, vi } from "vitest";
import { normalizeChatType } from "./chat-type.js";
import * as webEntry from "./web/index.js";
describe("channel-web barrel", () => {
it("exports the expected web helpers", () => {
expect(channelWeb.createWaSocket).toBeTypeOf("function");
expect(channelWeb.loginWeb).toBeTypeOf("function");
expect(channelWeb.monitorWebChannel).toBeTypeOf("function");
expect(channelWeb.sendMessageWhatsApp).toBeTypeOf("function");
expect(channelWeb.monitorWebInbox).toBeTypeOf("function");
expect(channelWeb.pickWebChannel).toBeTypeOf("function");
expect(channelWeb.WA_WEB_AUTH_DIR).toBeTruthy();
});
});
describe("normalizeChatType", () => {
const cases: Array<{ name: string; value: string | undefined; expected: string | undefined }> = [
@@ -42,17 +28,43 @@ describe("normalizeChatType", () => {
});
});
describe("channels/web entrypoint", () => {
it("re-exports web channel helpers", () => {
expect(webEntry.createWaSocket).toBe(channelWeb.createWaSocket);
expect(webEntry.loginWeb).toBe(channelWeb.loginWeb);
expect(webEntry.logWebSelfId).toBe(channelWeb.logWebSelfId);
expect(webEntry.monitorWebInbox).toBe(channelWeb.monitorWebInbox);
expect(webEntry.monitorWebChannel).toBe(channelWeb.monitorWebChannel);
expect(webEntry.pickWebChannel).toBe(channelWeb.pickWebChannel);
expect(webEntry.sendMessageWhatsApp).toBe(channelWeb.sendMessageWhatsApp);
expect(webEntry.WA_WEB_AUTH_DIR).toBe(channelWeb.WA_WEB_AUTH_DIR);
expect(webEntry.waitForWaConnection).toBe(channelWeb.waitForWaConnection);
expect(webEntry.webAuthExists).toBe(channelWeb.webAuthExists);
describe("WA_WEB_AUTH_DIR", () => {
afterEach(() => {
vi.doUnmock("../plugins/runtime/runtime-whatsapp-boundary.js");
vi.resetModules();
});
it("resolves lazily and caches across the legacy and channels/web entrypoints", async () => {
const resolveWaWebAuthDir = vi.fn(() => "/tmp/openclaw-whatsapp-auth");
vi.resetModules();
vi.doMock("../plugins/runtime/runtime-whatsapp-boundary.js", () => ({
createWaSocket: vi.fn(),
extractMediaPlaceholder: vi.fn(),
extractText: vi.fn(),
formatError: vi.fn(),
getStatusCode: vi.fn(),
logWebSelfId: vi.fn(),
loginWeb: vi.fn(),
logoutWeb: vi.fn(),
monitorWebChannel: vi.fn(),
monitorWebInbox: vi.fn(),
pickWebChannel: vi.fn(),
resolveHeartbeatRecipients: vi.fn(),
resolveWaWebAuthDir,
runWebHeartbeatOnce: vi.fn(),
sendMessageWhatsApp: vi.fn(),
sendReactionWhatsApp: vi.fn(),
waitForWaConnection: vi.fn(),
webAuthExists: vi.fn(),
}));
const channelWeb = await import("../channel-web.js");
const webEntry = await import("./web/index.js");
expect(resolveWaWebAuthDir).not.toHaveBeenCalled();
expect(String(channelWeb.WA_WEB_AUTH_DIR)).toBe("/tmp/openclaw-whatsapp-auth");
expect(String(webEntry.WA_WEB_AUTH_DIR)).toBe("/tmp/openclaw-whatsapp-auth");
expect(resolveWaWebAuthDir).toHaveBeenCalledTimes(1);
});
});

View File

@@ -11,7 +11,9 @@ describe("pairing adapters", () => {
const lower = createPairingPrefixStripper(/^nextcloud:/i, (entry) => entry.toLowerCase());
expect(strip("telegram:123")).toBe("123");
expect(strip("tg:123")).toBe("123");
expect(strip(" telegram:123 ")).toBe("123");
expect(lower("nextcloud:USER")).toBe("user");
expect(lower(" nextcloud:USER ")).toBe("user");
});
it("builds text pairing adapters", async () => {

View File

@@ -6,7 +6,7 @@ export function createPairingPrefixStripper(
prefixRe: RegExp,
map: (entry: string) => string = (entry) => entry,
): NonNullable<ChannelPairingAdapter["normalizeAllowEntry"]> {
return (entry) => map(entry.replace(prefixRe, ""));
return (entry) => map(entry.trim().replace(prefixRe, "").trim());
}
export function createLoggedPairingApprovalNotifier(

View File

@@ -1,18 +1,22 @@
import { describe, expect, it } from "vitest";
import { buildGatewayAuthConfig } from "./configure.js";
function expectGeneratedTokenFromInput(token: string | undefined, literalToAvoid = "undefined") {
function expectGeneratedTokenFromInput(
token: string | undefined,
forbiddenValues: string[] = ["undefined"],
) {
const result = buildGatewayAuthConfig({
mode: "token",
token,
});
expect(result?.mode).toBe("token");
expect(result?.token).toBeDefined();
expect(result?.token).not.toBe(literalToAvoid);
expect(typeof result?.token).toBe("string");
if (typeof result?.token !== "string") {
throw new Error("Expected generated token to be a string.");
}
for (const forbiddenValue of forbiddenValues) {
expect(result.token).not.toBe(forbiddenValue);
}
expect(result.token.length).toBeGreaterThan(0);
}
@@ -70,10 +74,37 @@ describe("buildGatewayAuthConfig", () => {
it("generates random token for missing, empty, and coerced-literal token inputs", () => {
expectGeneratedTokenFromInput(undefined);
expectGeneratedTokenFromInput("");
expectGeneratedTokenFromInput(" ");
expectGeneratedTokenFromInput("undefined");
expectGeneratedTokenFromInput("null", "null");
expectGeneratedTokenFromInput("", [""]);
expectGeneratedTokenFromInput(" ", [""]);
expectGeneratedTokenFromInput("undefined", ["undefined"]);
expectGeneratedTokenFromInput("null", ["null"]);
});
it("trims and preserves explicit token values", () => {
const result = buildGatewayAuthConfig({
mode: "token",
token: " abc123 ",
});
expect(result).toEqual({ mode: "token", token: "abc123" });
});
it("trims password values before storing them", () => {
const result = buildGatewayAuthConfig({
mode: "password",
password: " secret ", // pragma: allowlist secret
});
expect(result).toEqual({ mode: "password", password: "secret" }); // pragma: allowlist secret
});
it("keeps password mode valid even when the trimmed password becomes empty", () => {
const result = buildGatewayAuthConfig({
mode: "password",
password: " ",
});
expect(result).toEqual({ mode: "password" });
});
it("preserves SecretRef tokens when token mode is selected", () => {

View File

@@ -14,13 +14,20 @@ function mockProcReads(entries: Record<string, string>) {
}
async function withLinuxProcessPlatform<T>(run: () => Promise<T>): Promise<T> {
return withProcessPlatform("linux", run);
}
async function withProcessPlatform<T>(
platform: NodeJS.Platform,
run: () => Promise<T>,
): Promise<T> {
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
if (!originalPlatformDescriptor) {
throw new Error("missing process.platform descriptor");
}
Object.defineProperty(process, "platform", {
...originalPlatformDescriptor,
value: "linux",
value: platform,
});
try {
vi.resetModules();
@@ -102,12 +109,10 @@ describe("getProcessStartTime", () => {
});
it("returns null on non-Linux platforms", () => {
if (process.platform === "linux") {
// On actual Linux, this test is trivially satisfied by the other tests.
expect(true).toBe(true);
return;
}
expect(getProcessStartTime(process.pid)).toBeNull();
return withProcessPlatform("darwin", async () => {
const { getProcessStartTime: fresh } = await import("./pid-alive.js");
expect(fresh(process.pid)).toBeNull();
});
});
it("returns null for invalid PIDs", () => {