





















@@ -73,6 +73,36 @@ function createManifestlessClaudeBundleIndex(params: {
7373});
7474}
757576+function expectDiagnosticsContainCode(diagnostics: readonly { code?: unknown }[], code: string) {
77+expect(diagnostics.some((diagnostic) => diagnostic.code === code)).toBe(true);
78+}
79+80+function expectDiagnosticsContainSource(
81+diagnostics: readonly { source?: unknown }[],
82+source: string,
83+) {
84+expect(diagnostics.some((diagnostic) => diagnostic.source === source)).toBe(true);
85+}
86+87+function expectDiagnosticsDoNotContainSource(
88+diagnostics: readonly { source?: unknown }[],
89+source: string,
90+) {
91+expect(diagnostics.some((diagnostic) => diagnostic.source === source)).toBe(false);
92+}
93+94+function requirePluginRecord(
95+plugins: InstalledPluginIndex["plugins"],
96+pluginId: string,
97+): InstalledPluginIndex["plugins"][number] {
98+const plugin = plugins.find((candidate) => candidate.pluginId === pluginId);
99+expect(plugin, `plugin ${pluginId}`).toBeDefined();
100+if (!plugin) {
101+throw new Error(`expected plugin ${pluginId}`);
102+}
103+return plugin;
104+}
105+76106describe("loadPluginRegistrySnapshotWithMetadata", () => {
77107it("recovers managed npm plugins missing from a stale persisted registry", () => {
78108const tempRoot = makeTempDir();
@@ -105,28 +135,18 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
105135});
106136107137expect(result.source).toBe("derived");
108-expect(result.diagnostics).toContainEqual(
109-expect.objectContaining({ code: "persisted-registry-stale-source" }),
110-);
111-expect(result.snapshot.installRecords).toMatchObject({
112-whatsapp: {
113-source: "npm",
114-spec: "@openclaw/whatsapp@2026.5.2",
115-installPath: whatsappDir,
116-version: "2026.5.2",
117-resolvedName: "@openclaw/whatsapp",
118-resolvedVersion: "2026.5.2",
119-resolvedSpec: "@openclaw/whatsapp@2026.5.2",
120-},
138+expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
139+expect(result.snapshot.installRecords.whatsapp).toEqual({
140+source: "npm",
141+spec: "@openclaw/whatsapp@2026.5.2",
142+installPath: whatsappDir,
143+version: "2026.5.2",
144+resolvedName: "@openclaw/whatsapp",
145+resolvedVersion: "2026.5.2",
146+resolvedSpec: "@openclaw/whatsapp@2026.5.2",
121147});
122-expect(result.snapshot.plugins).toEqual(
123-expect.arrayContaining([
124-expect.objectContaining({
125-pluginId: "whatsapp",
126-origin: "global",
127-}),
128-]),
129-);
148+const whatsappPlugin = requirePluginRecord(result.snapshot.plugins, "whatsapp");
149+expect(whatsappPlugin.origin).toBe("global");
130150});
131151132152it("keeps persisted manifestless Claude bundles on the fast path", () => {
@@ -169,15 +189,11 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
169189if (!record?.packageJson?.fileSignature || !record.manifestFile) {
170190throw new Error("expected package plugin index record with file signatures");
171191}
172-expect(record.manifestFile).toEqual(
173-expect.objectContaining({
174-size: fs.statSync(path.join(rootDir, "openclaw.plugin.json")).size,
175-}),
192+expect(record.manifestFile.size).toBe(
193+fs.statSync(path.join(rootDir, "openclaw.plugin.json")).size,
176194);
177-expect(record.packageJson.fileSignature).toEqual(
178-expect.objectContaining({
179-size: fs.statSync(path.join(rootDir, "package.json")).size,
180-}),
195+expect(record.packageJson.fileSignature.size).toBe(
196+fs.statSync(path.join(rootDir, "package.json")).size,
181197);
182198writePersistedInstalledPluginIndexSync(index, { stateDir });
183199@@ -222,9 +238,7 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
222238});
223239224240expect(result.source).toBe("derived");
225-expect(result.diagnostics).toContainEqual(
226-expect.objectContaining({ code: "persisted-registry-stale-source" }),
227-);
241+expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
228242});
229243230244it("detects same-size same-mtime package.json replacements", () => {
@@ -253,9 +267,7 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
253267});
254268255269expect(result.source).toBe("derived");
256-expect(result.diagnostics).toContainEqual(
257-expect.objectContaining({ code: "persisted-registry-stale-source" }),
258-);
270+expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
259271});
260272261273it("detects package.json replacements even when stored stat fields still match", () => {
@@ -304,9 +316,7 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
304316});
305317306318expect(result.source).toBe("derived");
307-expect(result.diagnostics).toContainEqual(
308-expect.objectContaining({ code: "persisted-registry-stale-source" }),
309-);
319+expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
310320});
311321312322it("treats persisted registry as stale when a plugin diagnostic source path no longer exists", () => {
@@ -342,19 +352,13 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
342352const result = loadPluginRegistrySnapshotWithMetadata({ config, env, stateDir });
343353344354expect(result.source).toBe("derived");
345-expect(result.snapshot.diagnostics).not.toContainEqual(
346-expect.objectContaining({ source: ghostDir }),
347-);
348-expect(result.snapshot.plugins).toContainEqual(
349-expect.objectContaining({
350-pluginId: "lossless-claw",
351-origin: "global",
352-source: fs.realpathSync(path.join(npmPluginDir, "dist", "index.js")),
353-}),
354-);
355-expect(result.diagnostics).toContainEqual(
356-expect.objectContaining({ code: "persisted-registry-stale-source" }),
355+expectDiagnosticsDoNotContainSource(result.snapshot.diagnostics, ghostDir);
356+const losslessPlugin = requirePluginRecord(result.snapshot.plugins, "lossless-claw");
357+expect(losslessPlugin.origin).toBe("global");
358+expect(losslessPlugin.source).toBe(
359+fs.realpathSync(path.join(npmPluginDir, "dist", "index.js")),
357360);
361+expectDiagnosticsContainCode(result.diagnostics, "persisted-registry-stale-source");
358362});
359363360364it("keeps persisted registry when a non-plugin diagnostic source path still does not exist", () => {
@@ -378,9 +382,7 @@ describe("loadPluginRegistrySnapshotWithMetadata", () => {
378382const result = loadPluginRegistrySnapshotWithMetadata({ config, env, stateDir });
379383380384expect(result.source).toBe("persisted");
381-expect(result.snapshot.diagnostics).toContainEqual(
382-expect.objectContaining({ source: missingConfiguredPath }),
383-);
385+expectDiagnosticsContainSource(result.snapshot.diagnostics, missingConfiguredPath);
384386expect(result.diagnostics).toStrictEqual([]);
385387});
386388});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。