diff --git a/src/plugins/plugin-registry.test.ts b/src/plugins/plugin-registry.test.ts index 36f46d68ccc..1304a3d2a6c 100644 --- a/src/plugins/plugin-registry.test.ts +++ b/src/plugins/plugin-registry.test.ts @@ -149,6 +149,49 @@ function createIndex( }; } +function requireRecord(value: unknown, label: string): Record { + expect(typeof value, label).toBe("object"); + expect(value, label).not.toBeNull(); + return value as Record; +} + +function requireArray(value: unknown, label: string): unknown[] { + expect(Array.isArray(value), label).toBe(true); + return value as unknown[]; +} + +function expectFields(record: Record, expected: Record) { + for (const [key, value] of Object.entries(expected)) { + expect(record[key], key).toEqual(value); + } +} + +function expectPluginRecordFields(record: unknown, expected: Record) { + expectFields(requireRecord(record, "plugin record"), expected); +} + +function expectDiagnosticCodes(diagnostics: unknown, expectedCodes: string[]) { + const codes = requireArray(diagnostics, "diagnostics").map( + (diagnostic) => requireRecord(diagnostic, "diagnostic").code, + ); + expect(codes).toEqual(expectedCodes); +} + +function expectInstallRecord( + installRecords: unknown, + pluginId: string, + expected: Record, +) { + const records = requireRecord(installRecords, "install records"); + expectFields(requireRecord(records[pluginId], `${pluginId} install record`), expected); +} + +function expectSnapshotPluginIds(snapshot: InstalledPluginIndex, expectedPluginIds: string[]) { + expect(listPluginRecords({ index: snapshot }).map((plugin) => plugin.pluginId)).toEqual( + expectedPluginIds, + ); +} + describe("plugin registry facade", () => { it("resolves relative plugin API paths against the plugin root", () => { const pluginRoot = path.join(makeTempDir(), "plugins", "demo"); @@ -181,7 +224,7 @@ describe("plugin registry facade", () => { }); expect(listPluginRecords({ index }).map((plugin) => plugin.pluginId)).toEqual(["demo"]); - expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({ + expectPluginRecordFields(getPluginRecord({ index, pluginId: "demo" }), { pluginId: "demo", enabled: true, }); @@ -246,7 +289,7 @@ describe("plugin registry facade", () => { preferPersisted: false, }); - expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({ + expectPluginRecordFields(getPluginRecord({ index, pluginId: "demo" }), { pluginId: "demo", enabled: false, }); @@ -344,26 +387,19 @@ describe("plugin registry facade", () => { expect(normalizePluginId("openai-chat")).toBe("openai"); expect(normalizePluginId("unknown-plugin")).toBe("unknown-plugin"); - expect( - normalizePluginsConfigWithRegistry( - { - allow: ["openai-chat"], - entries: { - "OpenAI-Codex": { - enabled: false, - }, + const normalizedConfig = normalizePluginsConfigWithRegistry( + { + allow: ["openai-chat"], + entries: { + "OpenAI-Codex": { + enabled: false, }, }, - index, - ), - ).toMatchObject({ - allow: ["openai"], - entries: { - openai: { - enabled: false, - }, }, - }); + index, + ); + expect(normalizedConfig.allow).toEqual(["openai"]); + expect(normalizedConfig.entries?.openai?.enabled).toBe(false); }); it("normalizes plugin config ids from a provided manifest registry without rereading manifests", () => { @@ -387,17 +423,14 @@ describe("plugin registry facade", () => { }); expect(normalizePluginId("demo-chat")).toBe("demo"); - expect( - normalizePluginsConfigWithRegistry( - { - allow: ["demo-chat"], - }, - index, - { manifestRegistry: lookUpTable.manifestRegistry }, - ), - ).toMatchObject({ - allow: ["demo"], - }); + const normalizedConfig = normalizePluginsConfigWithRegistry( + { + allow: ["demo-chat"], + }, + index, + { manifestRegistry: lookUpTable.manifestRegistry }, + ); + expect(normalizedConfig.allow).toEqual(["demo"]); }); it("reads the persisted registry before deriving from discovered candidates", async () => { @@ -462,12 +495,8 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-source" }), - ]); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-source"]); + expectSnapshotPluginIds(result.snapshot, ["demo"]); }); it("falls back to the derived registry when persisted manifest metadata is stale", async () => { @@ -501,9 +530,7 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-source" }), - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-source"]); expect(result.snapshot.plugins[0]?.manifestHash).not.toBe(persisted.plugins[0]?.manifestHash); }); @@ -543,9 +570,7 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-source" }), - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-source"]); expect(result.snapshot.plugins[0]?.packageJson?.hash).not.toBe( persisted.plugins[0]?.packageJson?.hash, ); @@ -583,9 +608,7 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-source" }), - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-source"]); expect(result.snapshot.plugins[0]?.packageJson).toBeUndefined(); }); @@ -617,12 +640,8 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-source" }), - ]); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-source"]); + expectSnapshotPluginIds(result.snapshot, ["demo"]); }); it("falls back to the derived registry when persisted policy is stale", async () => { @@ -655,17 +674,11 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-stale-policy" }), - ]); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); - expect(result.snapshot.installRecords).toMatchObject({ - persisted: { - source: "npm", - spec: "persisted-plugin@1.0.0", - }, + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-stale-policy"]); + expectSnapshotPluginIds(result.snapshot, ["demo"]); + expectInstallRecord(result.snapshot.installRecords, "persisted", { + source: "npm", + spec: "persisted-plugin@1.0.0", }); }); @@ -681,12 +694,8 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ code: "persisted-registry-missing" }), - ]); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-missing"]); + expectSnapshotPluginIds(result.snapshot, ["demo"]); }); it("derives fresh config-scoped registries when the persisted registry is missing", () => { @@ -740,15 +749,11 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(result.diagnostics).toEqual([ - expect.objectContaining({ - code: "persisted-registry-disabled", - message: expect.stringContaining("deprecated break-glass compatibility switch"), - }), - ]); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); + expectDiagnosticCodes(result.diagnostics, ["persisted-registry-disabled"]); + expect(String(requireRecord(result.diagnostics[0], "diagnostic").message)).toContain( + "deprecated break-glass compatibility switch", + ); + expectSnapshotPluginIds(result.snapshot, ["demo"]); }); it("derives a fresh registry without dropping persisted install records", async () => { @@ -776,14 +781,10 @@ describe("plugin registry facade", () => { }); expect(result.source).toBe("derived"); - expect(listPluginRecords({ index: result.snapshot }).map((plugin) => plugin.pluginId)).toEqual([ - "demo", - ]); - expect(result.snapshot.installRecords).toMatchObject({ - persisted: { - source: "npm", - spec: "persisted-plugin@1.0.0", - }, + expectSnapshotPluginIds(result.snapshot, ["demo"]); + expectInstallRecord(result.snapshot.installRecords, "persisted", { + source: "npm", + spec: "persisted-plugin@1.0.0", }); }); @@ -794,16 +795,11 @@ describe("plugin registry facade", () => { const candidate = createCandidate(pluginDir); const env = hermeticEnv(); - await expect( - inspectPluginRegistry({ stateDir, candidates: [candidate], env }), - ).resolves.toMatchObject({ - state: "missing", - refreshReasons: ["missing"], - persisted: null, - current: { - plugins: [expect.objectContaining({ pluginId: "demo" })], - }, - }); + const missingInspect = await inspectPluginRegistry({ stateDir, candidates: [candidate], env }); + expect(missingInspect.state).toBe("missing"); + expect(missingInspect.refreshReasons).toEqual(["missing"]); + expect(missingInspect.persisted).toBeNull(); + expect(missingInspect.current.plugins.map((plugin) => plugin.pluginId)).toEqual(["demo"]); await refreshPluginRegistry({ reason: "manual", @@ -812,15 +808,10 @@ describe("plugin registry facade", () => { env, }); - await expect( - inspectPluginRegistry({ stateDir, candidates: [candidate], env }), - ).resolves.toMatchObject({ - state: "fresh", - refreshReasons: [], - persisted: { - plugins: [expect.objectContaining({ pluginId: "demo" })], - }, - }); + const freshInspect = await inspectPluginRegistry({ stateDir, candidates: [candidate], env }); + expect(freshInspect.state).toBe("fresh"); + expect(freshInspect.refreshReasons).toEqual([]); + expect(freshInspect.persisted?.plugins.map((plugin) => plugin.pluginId)).toEqual(["demo"]); }); it("preserves install records when refreshing the persisted registry", async () => { @@ -846,15 +837,16 @@ describe("plugin registry facade", () => { env: hermeticEnv(), }); - await expect(readPersistedInstalledPluginIndex({ stateDir })).resolves.toMatchObject({ - installRecords: { - missing: { - source: "npm", - spec: "missing-plugin@1.0.0", - installPath: path.join(stateDir, "plugins", "missing"), - }, - }, - plugins: [], + const persisted = await readPersistedInstalledPluginIndex({ stateDir }); + expect(persisted).not.toBeNull(); + if (!persisted) { + throw new Error("Expected persisted plugin index"); + } + expectInstallRecord(persisted.installRecords, "missing", { + source: "npm", + spec: "missing-plugin@1.0.0", + installPath: path.join(stateDir, "plugins", "missing"), }); + expect(persisted.plugins).toEqual([]); }); });