"use strict"; const assert = require("node:assert/strict"); const test = require("node:test"); const { PluginManager } = require("./pluginManager.cjs"); test("failed activation leaves a newly installed plugin disabled", async () => { const plugin = { id: "com.example.activation-failure", enabled: true }; let enabled = true; const manager = new PluginManager({ database: { close() {}, listPlugins: () => [], setEnabled(pluginId, nextEnabled) { assert.equal(pluginId, plugin.id); enabled = nextEnabled; }, }, packageStore: { async initialize() {}, async install() { return plugin; }, }, runtimeSupervisor: { async start() { throw new Error("activation failed"); }, async startEnabled() {}, async stop() {}, }, }); await assert.rejects(manager.install("/tmp/plugin.ncpkg", { enable: true }), /activation failed/); assert.equal(enabled, false); }); test("management mutations are serialized in invocation order", async () => { const calls = []; let releaseInstall; const installBlocked = new Promise((resolve) => { releaseInstall = resolve; }); const manager = new PluginManager({ database: { close() {}, getActivePlugin: (pluginId) => ({ id: pluginId, enabled: false }), listPlugins: () => [], setEnabled(pluginId, enabled) { calls.push(`enabled:${pluginId}:${enabled}`); }, }, packageStore: { async initialize() {}, async install() { calls.push("install:start"); await installBlocked; calls.push("install:end"); return { id: "com.example.serial", enabled: false }; }, }, runtimeSupervisor: { async startEnabled() {}, async stop(pluginId) { calls.push(`stop:${pluginId}`); }, }, }); const install = manager.install("/tmp/plugin.ncpkg"); const disable = manager.setEnabled("com.example.serial", false); await new Promise((resolve) => setImmediate(resolve)); assert.deepEqual(calls, ["install:start"]); releaseInstall(); await Promise.all([install, disable]); assert.deepEqual(calls, [ "install:start", "install:end", "enabled:com.example.serial:false", "stop:com.example.serial", ]); }); test("explicit enable clears the active version quarantine before starting", async () => { const calls = []; const pluginId = "com.example.recover"; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ id: pluginId, activeVersion: "1.0.0", enabled: false, runtime: { quarantinedAt: 123 }, }), listPlugins: () => [], clearQuarantine(id, version) { calls.push(`recover:${id}@${version}`); }, setEnabled(id, enabled) { calls.push(`enabled:${id}:${enabled}`); }, }, packageStore: { async initialize() {} }, runtimeSupervisor: { async startEnabled() {}, async start(id) { calls.push(`start:${id}`); }, }, }); await manager.setEnabled(pluginId, true); assert.deepEqual(calls, [ `recover:${pluginId}@1.0.0`, `enabled:${pluginId}:true`, `start:${pluginId}`, ]); }); test("installing an enabled version replaces the active runtime", async () => { const calls = []; const plugin = { id: "com.example.upgrade", enabled: true }; const manager = new PluginManager({ database: { close() {}, listPlugins: () => [], setEnabled(pluginId, enabled) { calls.push(`enabled:${pluginId}:${enabled}`); }, }, packageStore: { async initialize() {}, async install(_archivePath, options) { await options.beforeActivate({ pluginId: plugin.id, version: "2.0.0", previousPlugin: { id: plugin.id, enabled: true, activeVersion: "1.0.0", }, reason: "switch-active-version", }); calls.push("activate:2.0.0"); return plugin; }, }, runtimeSupervisor: { async startEnabled() {}, async stop(pluginId) { calls.push(`stop:${pluginId}`); }, async start(pluginId) { calls.push(`start:${pluginId}`); }, }, }); assert.equal(await manager.install("/tmp/upgrade.ncpkg"), plugin); assert.deepEqual(calls, [ "enabled:com.example.upgrade:false", "stop:com.example.upgrade", "activate:2.0.0", "start:com.example.upgrade", ]); }); test("failed version preparation restores the previously enabled runtime", async () => { const calls = []; const pluginId = "com.example.upgrade-rollback"; let activePlugin = { id: pluginId, enabled: true, activeVersion: "1.0.0", }; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ ...activePlugin }), listPlugins: () => [], setEnabled(id, enabled) { assert.equal(id, pluginId); activePlugin = { ...activePlugin, enabled }; calls.push(`enabled:${enabled}`); }, }, packageStore: { async initialize() {}, async install(_archivePath, options) { await options.beforeActivate({ pluginId, version: "2.0.0", previousPlugin: { ...activePlugin }, reason: "switch-active-version", }); calls.push("prepare:failed"); throw new Error("database switch failed"); }, }, runtimeSupervisor: { async startEnabled() {}, async stop(id) { calls.push(`stop:${id}`); }, async start(id) { calls.push(`start:${id}`); }, }, }); await assert.rejects(manager.install("/tmp/upgrade.ncpkg"), /database switch failed/); assert.deepEqual(calls, [ "enabled:false", `stop:${pluginId}`, "prepare:failed", "enabled:true", `start:${pluginId}`, ]); assert.equal(activePlugin.enabled, true); }); test("failed upgraded-version activation restores and restarts the previous version", async () => { const calls = []; const pluginId = "com.example.upgrade-activation-rollback"; let activePlugin = { id: pluginId, enabled: true, activeVersion: "1.0.0", }; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ ...activePlugin }), listPlugins: () => [], setActiveVersion(id, version, options) { assert.equal(id, pluginId); assert.deepEqual(options, { enabled: true, expectedActiveVersion: "2.0.0" }); activePlugin = { ...activePlugin, activeVersion: version, enabled: options.enabled }; calls.push(`active:${version}:${options.enabled}`); }, setEnabled(id, enabled) { assert.equal(id, pluginId); activePlugin = { ...activePlugin, enabled }; calls.push(`enabled:${enabled}`); }, }, packageStore: { async initialize() {}, async install(_archivePath, options) { await options.beforeActivate({ pluginId, version: "2.0.0", previousPlugin: { ...activePlugin }, reason: "switch-active-version", }); activePlugin = { ...activePlugin, activeVersion: "2.0.0", enabled: true }; calls.push("active:2.0.0:true"); return { ...activePlugin }; }, }, runtimeSupervisor: { async startEnabled() {}, async stop(id) { calls.push(`stop:${id}`); }, async start(id) { calls.push(`start:${id}@${activePlugin.activeVersion}`); if (activePlugin.activeVersion === "2.0.0") throw new Error("new activation failed"); }, }, }); await assert.rejects(manager.install("/tmp/upgrade.ncpkg"), /new activation failed/); assert.deepEqual(calls, [ "enabled:false", `stop:${pluginId}`, "active:2.0.0:true", `start:${pluginId}@2.0.0`, "enabled:false", "active:1.0.0:true", `start:${pluginId}@1.0.0`, ]); assert.deepEqual(activePlugin, { id: pluginId, enabled: true, activeVersion: "1.0.0" }); }); test("failed previous-version restart leaves the restored version disabled", async () => { const pluginId = "com.example.upgrade-rollback-fails"; let activePlugin = { id: pluginId, enabled: true, activeVersion: "1.0.0" }; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ ...activePlugin }), listPlugins: () => [], setActiveVersion(_id, version, options) { activePlugin = { ...activePlugin, activeVersion: version, enabled: options.enabled }; }, setEnabled(_id, enabled) { activePlugin = { ...activePlugin, enabled }; }, }, packageStore: { async initialize() {}, async install(_archivePath, options) { await options.beforeActivate({ pluginId, version: "2.0.0", previousPlugin: { ...activePlugin }, reason: "switch-active-version", }); activePlugin = { ...activePlugin, activeVersion: "2.0.0", enabled: true }; return { ...activePlugin }; }, }, runtimeSupervisor: { async startEnabled() {}, async stop() {}, async start() { throw new Error("runtime unavailable"); }, }, }); await assert.rejects(manager.install("/tmp/upgrade.ncpkg"), /runtime unavailable/); assert.deepEqual(activePlugin, { id: pluginId, enabled: false, activeVersion: "1.0.0" }); }); test("failed restoration leaves the previous plugin disabled", async () => { const calls = []; const pluginId = "com.example.upgrade-broken-rollback"; let activePlugin = { id: pluginId, enabled: true, activeVersion: "1.0.0" }; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ ...activePlugin }), listPlugins: () => [], setEnabled(id, enabled) { assert.equal(id, pluginId); activePlugin = { ...activePlugin, enabled }; calls.push(`enabled:${enabled}`); }, }, packageStore: { async initialize() {}, async install(_archivePath, options) { await options.beforeActivate({ pluginId, version: "2.0.0", previousPlugin: { ...activePlugin }, reason: "replace-active-files", }); throw new Error("replacement failed"); }, }, runtimeSupervisor: { async startEnabled() {}, async stop() { calls.push("stop"); }, async start() { calls.push("start:failed"); throw new Error("previous package is invalid"); }, }, }); await assert.rejects(manager.install("/tmp/upgrade.ncpkg"), /replacement failed/); assert.deepEqual(calls, [ "enabled:false", "stop", "enabled:true", "start:failed", "enabled:false", ]); assert.equal(activePlugin.enabled, false); }); test("uninstall disables lazy activation before stopping and removing code", async () => { const calls = []; const pluginId = "com.example.remove"; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ id: pluginId, enabled: true }), listPlugins: () => [], setEnabled(id, enabled) { calls.push(`enabled:${id}:${enabled}`); }, }, packageStore: { async initialize() {}, async uninstall(id) { calls.push(`uninstall:${id}`); return true; }, }, runtimeSupervisor: { async startEnabled() {}, async stop(id) { calls.push(`stop:${id}`); }, }, }); assert.equal(await manager.uninstall(pluginId), true); assert.deepEqual(calls, [ `enabled:${pluginId}:false`, `stop:${pluginId}`, `uninstall:${pluginId}`, ]); }); test("uninstall never removes package code when runtime cleanup fails", async () => { let uninstallCalls = 0; const pluginId = "com.example.contained"; const manager = new PluginManager({ database: { close() {}, getActivePlugin: () => ({ id: pluginId, enabled: true }), listPlugins: () => [], setEnabled() {}, }, packageStore: { async initialize() {}, async uninstall() { uninstallCalls += 1; return true; }, }, runtimeSupervisor: { async startEnabled() {}, async stop() { throw new Error("companion cleanup failed"); }, }, }); await assert.rejects(manager.uninstall(pluginId), /companion cleanup failed/); assert.equal(uninstallCalls, 0); }); test("concurrent shutdown callers share one complete shutdown operation", async () => { let databaseCloses = 0; let supervisorShutdowns = 0; let beforeCloses = 0; const manager = new PluginManager({ database: { close() { databaseCloses += 1; }, listPlugins: () => [], }, packageStore: { async initialize() {} }, runtimeSupervisor: { async startEnabled() {}, async shutdown() { supervisorShutdowns += 1; }, }, async beforeClose() { beforeCloses += 1; }, }); await manager.initialize(); const first = manager.shutdown(); const second = manager.shutdown(); assert.equal(first, second); await Promise.all([first, second]); assert.equal(supervisorShutdowns, 1); assert.equal(beforeCloses, 1); assert.equal(databaseCloses, 1); }); test("shutdown closes the database even when secure-service cleanup fails", async () => { let databaseCloses = 0; const manager = new PluginManager({ database: { close() { databaseCloses += 1; }, listPlugins: () => [], }, packageStore: { async initialize() {} }, runtimeSupervisor: { async startEnabled() {}, async shutdown() {} }, async beforeClose() { throw new Error("secure cleanup failed"); }, }); await manager.initialize(); await assert.rejects(manager.shutdown(), /secure cleanup failed/); assert.equal(databaseCloses, 1); }); test("shutdown cancels an in-flight runtime mutation before waiting for its queue", async () => { const calls = []; let rejectStart; let markStartEntered; const startEntered = new Promise((resolve) => { markStartEntered = resolve; }); const pluginId = "com.example.shutdown-placement"; const manager = new PluginManager({ database: { close() { calls.push("database:close"); }, getActivePlugin: () => ({ id: pluginId, activeVersion: "1.0.0", enabled: false, runtime: { quarantinedAt: null }, }), listPlugins: () => [], setEnabled(_id, enabled) { calls.push(`enabled:${enabled}`); }, }, packageStore: { async initialize() {} }, runtimeSupervisor: { async startEnabled() {}, start() { calls.push("runtime:start"); markStartEntered(); return new Promise((_resolve, reject) => { rejectStart = reject; }); }, async shutdown() { calls.push("runtime:shutdown"); rejectStart(new Error("placement cancelled by shutdown")); }, }, }); await manager.initialize(); const enabling = manager.setEnabled(pluginId, true); await startEntered; const shuttingDown = manager.shutdown(); await assert.rejects(enabling, /placement cancelled by shutdown/); await shuttingDown; assert.deepEqual(calls, [ "enabled:true", "runtime:start", "runtime:shutdown", "enabled:false", "database:close", ]); });