Files
openclaw/src/plugins/plugin-scan-existence-cache.test.ts
T
Peter Steinberger 8616c0c374 refactor: finish shared test helper migrations (#120996)
* test: finish shared helper migrations

* test: fix helper migration CI

* style: fix test import ordering

* test(acpx): restore deferred void types

* test: fix helper migrations after rebase
2026-08-09 06:00:06 -07:00

148 lines
5.7 KiB
TypeScript

// Verifies the scan-scoped plugin existence cache: within a scan pass repeated
// probes hit the filesystem once, across scans the filesystem is re-read (no
// process-global staleness), and outside a scan it is a plain passthrough.
import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
import { loadBundleManifest, detectBundleManifestFormat } from "./bundle-manifest.js";
import {
pluginScanExistsSync,
withPluginScanExistenceCache,
} from "./plugin-scan-existence-cache.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
describe("pluginScanExistsSync", () => {
it("is a plain passthrough outside a scan (no caching)", () => {
const dir = tempDirs.make("exists-passthrough-");
const target = path.join(dir, "marker.json");
fs.writeFileSync(target, "{}");
const spy = vi.spyOn(fs, "existsSync");
try {
expect(pluginScanExistsSync(target)).toBe(true);
expect(pluginScanExistsSync(target)).toBe(true);
expect(pluginScanExistsSync(target)).toBe(true);
// No active scan cache: every probe reaches the filesystem.
expect(spy).toHaveBeenCalledTimes(3);
} finally {
spy.mockRestore();
}
});
it("memoizes repeated probes within a single scan pass", () => {
const dir = tempDirs.make("exists-memoize-");
const target = path.join(dir, "marker.json");
fs.writeFileSync(target, "{}");
const spy = vi.spyOn(fs, "existsSync");
try {
const result = withPluginScanExistenceCache(() => {
let hit = false;
for (let i = 0; i < 5; i += 1) {
if (pluginScanExistsSync(target)) {
hit = true;
}
}
return hit;
});
expect(result).toBe(true);
// Five probes of the same path, one filesystem call inside the scan.
expect(spy).toHaveBeenCalledTimes(1);
} finally {
spy.mockRestore();
}
});
it("does not conflate different paths inside a scan", () => {
const dir = tempDirs.make("exists-distinct-");
const present = path.join(dir, "present.json");
const absent = path.join(dir, "absent.json");
fs.writeFileSync(present, "{}");
const spy = vi.spyOn(fs, "existsSync");
try {
withPluginScanExistenceCache(() => {
expect(pluginScanExistsSync(present)).toBe(true);
expect(pluginScanExistsSync(absent)).toBe(false);
// Re-probe both: served from cache, no new filesystem calls.
expect(pluginScanExistsSync(present)).toBe(true);
expect(pluginScanExistsSync(absent)).toBe(false);
});
expect(spy).toHaveBeenCalledTimes(2);
} finally {
spy.mockRestore();
}
});
it("re-reads the filesystem across separate scans (no process-global staleness)", () => {
const dir = tempDirs.make("exists-freshness-");
const target = path.join(dir, "marker.json");
// First scan sees the path absent.
withPluginScanExistenceCache(() => {
expect(pluginScanExistsSync(target)).toBe(false);
});
// Marker appears between scans (e.g. an install/repair pass).
fs.writeFileSync(target, "{}");
// A later scan must observe the new state, not a stale cached false.
withPluginScanExistenceCache(() => {
expect(pluginScanExistsSync(target)).toBe(true);
});
});
});
describe("bundle manifest scan uses the existence cache", () => {
function buildClaudeBundlePlugin(root: string): void {
fs.mkdirSync(path.join(root, ".claude-plugin"), { recursive: true });
fs.writeFileSync(
path.join(root, ".claude-plugin", "plugin.json"),
JSON.stringify({ name: "demo-bundle" }),
);
// Capability marker dirs/files that both detectBundleManifestFormat and
// loadBundleManifest probe for a claude-format bundle.
fs.mkdirSync(path.join(root, "skills"), { recursive: true });
fs.mkdirSync(path.join(root, "commands"), { recursive: true });
fs.mkdirSync(path.join(root, "agents"), { recursive: true });
fs.writeFileSync(path.join(root, ".mcp.json"), "{}");
fs.writeFileSync(path.join(root, ".lsp.json"), "{}");
fs.writeFileSync(path.join(root, "settings.json"), "{}");
}
it("reduces fs.existsSync calls when detect + load run under one scan cache", () => {
const root = tempDirs.make("claude-bundle-");
buildClaudeBundlePlugin(root);
// Run a detect + load pair and count fs.existsSync calls under a spy.
const detectAndLoad = (): void => {
const format = detectBundleManifestFormat(root);
if (format) {
loadBundleManifest({ rootDir: root, bundleFormat: format });
}
};
const countExistsSyncCalls = (run: () => void): number => {
const spy = vi.spyOn(fs, "existsSync");
try {
run();
return spy.mock.calls.length;
} finally {
spy.mockRestore();
}
};
// Baseline: no scan cache → detect and load re-probe the same marker paths.
const uncachedCalls = countExistsSyncCalls(detectAndLoad);
// Same workload, but the detect + load pair shares one scan cache.
const cachedCalls = countExistsSyncCalls(() => withPluginScanExistenceCache(detectAndLoad));
// Both paths must still produce a valid claude bundle manifest.
const manifest = withPluginScanExistenceCache(() => {
const format = detectBundleManifestFormat(root);
return format ? loadBundleManifest({ rootDir: root, bundleFormat: format }) : null;
});
expect(manifest?.ok).toBe(true);
// The cache must eliminate the redundant probes; assert a real reduction
// rather than an exact count so the test is robust to marker-set changes.
expect(cachedCalls).toBeLessThan(uncachedCalls);
expect(cachedCalls).toBeGreaterThan(0);
});
});