Files
openclaw/src/infra/warning-filter.test.ts
Tyler Yust e4651d6afa Memory/QMD: reuse default model cache and skip ENOENT warnings (#12114)
* Memory/QMD: symlink default model cache into custom XDG_CACHE_HOME

QmdMemoryManager overrides XDG_CACHE_HOME to isolate the qmd index
per-agent, but this also moves where qmd looks for its ML models
(~2.1GB). Since models are installed at the default location
(~/.cache/qmd/models/), every qmd invocation would attempt to
re-download them from HuggingFace and time out.

Fix: on initialization, symlink ~/.cache/qmd/models/ into the custom
XDG_CACHE_HOME path so the index stays isolated per-agent while the
shared models are reused. The symlink is only created when the default
models directory exists and the target path does not already exist.

Includes tests for the three key scenarios: symlink creation, existing
directory preservation, and graceful skip when no default models exist.

* Memory/QMD: skip model symlink warning on ENOENT

* test: stabilize warning-filter visibility assertion (#12114) (thanks @tyler6204)

* fix: add changelog entry for QMD cache reuse (#12114) (thanks @tyler6204)

* fix: handle plain context-overflow strings in compaction detection (#12114) (thanks @tyler6204)
2026-02-08 23:43:08 -08:00

98 lines
3.0 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { installProcessWarningFilter, shouldIgnoreWarning } from "./warning-filter.js";
const warningFilterKey = Symbol.for("openclaw.warning-filter");
const baseEmitWarning = process.emitWarning.bind(process);
function resetWarningFilterInstallState(): void {
const globalState = globalThis as typeof globalThis & {
[warningFilterKey]?: { installed: boolean };
};
delete globalState[warningFilterKey];
process.emitWarning = baseEmitWarning;
}
describe("warning filter", () => {
beforeEach(() => {
resetWarningFilterInstallState();
});
afterEach(() => {
resetWarningFilterInstallState();
vi.restoreAllMocks();
});
it("suppresses known deprecation and experimental warning signatures", () => {
expect(
shouldIgnoreWarning({
name: "DeprecationWarning",
code: "DEP0040",
message: "The punycode module is deprecated.",
}),
).toBe(true);
expect(
shouldIgnoreWarning({
name: "DeprecationWarning",
code: "DEP0060",
message: "The `util._extend` API is deprecated.",
}),
).toBe(true);
expect(
shouldIgnoreWarning({
name: "ExperimentalWarning",
message: "SQLite is an experimental feature and might change at any time",
}),
).toBe(true);
});
it("keeps unknown warnings visible", () => {
expect(
shouldIgnoreWarning({
name: "DeprecationWarning",
code: "DEP9999",
message: "Totally new warning",
}),
).toBe(false);
});
it("installs once and suppresses known warnings at emit time", async () => {
const seenWarnings: Array<{ code?: string; name: string; message: string }> = [];
const onWarning = (warning: Error & { code?: string }) => {
seenWarnings.push({
code: warning.code,
name: warning.name,
message: warning.message,
});
};
process.on("warning", onWarning);
try {
installProcessWarningFilter();
installProcessWarningFilter();
installProcessWarningFilter();
const emitWarning = (...args: unknown[]) =>
(process.emitWarning as unknown as (...warningArgs: unknown[]) => void)(...args);
emitWarning(
"The `util._extend` API is deprecated. Please use Object.assign() instead.",
"DeprecationWarning",
"DEP0060",
);
emitWarning("The `util._extend` API is deprecated. Please use Object.assign() instead.", {
type: "DeprecationWarning",
code: "DEP0060",
});
await new Promise((resolve) => setImmediate(resolve));
expect(seenWarnings.find((warning) => warning.code === "DEP0060")).toBeUndefined();
emitWarning("Visible warning", { type: "Warning", code: "OPENCLAW_TEST_WARNING" });
await new Promise((resolve) => setImmediate(resolve));
expect(
seenWarnings.find((warning) => warning.code === "OPENCLAW_TEST_WARNING"),
).toBeDefined();
} finally {
process.off("warning", onWarning);
}
});
});