插件系统

This commit is contained in:
YHH
2025-10-11 09:26:36 +08:00
parent 279c1d9bc9
commit ae71af856b
5 changed files with 889 additions and 0 deletions

View File

@@ -0,0 +1,396 @@
import { Core } from '../../src/Core';
import { IPlugin } from '../../src/Core/Plugin';
import { PluginManager } from '../../src/Core/PluginManager';
import type { ServiceContainer } from '../../src/Core/ServiceContainer';
describe('插件系统', () => {
let core: Core;
beforeEach(() => {
core = Core.create({ debug: false });
});
afterEach(() => {
Core.destroy();
});
describe('基本功能', () => {
it('应该能够安装插件', async () => {
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
installed = false;
install() {
this.installed = true;
}
uninstall() {
this.installed = false;
}
}
const plugin = new TestPlugin();
await Core.installPlugin(plugin);
expect(plugin.installed).toBe(true);
expect(Core.isPluginInstalled('test-plugin')).toBe(true);
});
it('应该能够卸载插件', async () => {
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
installed = false;
install() {
this.installed = true;
}
uninstall() {
this.installed = false;
}
}
const plugin = new TestPlugin();
await Core.installPlugin(plugin);
expect(plugin.installed).toBe(true);
await Core.uninstallPlugin('test-plugin');
expect(plugin.installed).toBe(false);
expect(Core.isPluginInstalled('test-plugin')).toBe(false);
});
it('应该能够获取已安装的插件', async () => {
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
const plugin = new TestPlugin();
await Core.installPlugin(plugin);
const installed = Core.getPlugin('test-plugin');
expect(installed).toBe(plugin);
expect(installed?.version).toBe('1.0.0');
});
it('应该拒绝重复安装同一个插件', async () => {
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
const plugin1 = new TestPlugin();
const plugin2 = new TestPlugin();
await Core.installPlugin(plugin1);
await Core.installPlugin(plugin2);
expect(Core.getPlugin('test-plugin')).toBe(plugin1);
});
});
describe('依赖管理', () => {
it('应该检查插件依赖', async () => {
class BasePlugin implements IPlugin {
readonly name = 'base-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
class DependentPlugin implements IPlugin {
readonly name = 'dependent-plugin';
readonly version = '1.0.0';
readonly dependencies = ['base-plugin'] as const;
install() {}
uninstall() {}
}
const dependentPlugin = new DependentPlugin();
await expect(Core.installPlugin(dependentPlugin)).rejects.toThrow(
'unmet dependencies'
);
});
it('应该允许按正确顺序安装有依赖的插件', async () => {
class BasePlugin implements IPlugin {
readonly name = 'base-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
class DependentPlugin implements IPlugin {
readonly name = 'dependent-plugin';
readonly version = '1.0.0';
readonly dependencies = ['base-plugin'] as const;
install() {}
uninstall() {}
}
const basePlugin = new BasePlugin();
const dependentPlugin = new DependentPlugin();
await Core.installPlugin(basePlugin);
await Core.installPlugin(dependentPlugin);
expect(Core.isPluginInstalled('base-plugin')).toBe(true);
expect(Core.isPluginInstalled('dependent-plugin')).toBe(true);
});
it('应该防止卸载被依赖的插件', async () => {
class BasePlugin implements IPlugin {
readonly name = 'base-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
class DependentPlugin implements IPlugin {
readonly name = 'dependent-plugin';
readonly version = '1.0.0';
readonly dependencies = ['base-plugin'] as const;
install() {}
uninstall() {}
}
await Core.installPlugin(new BasePlugin());
await Core.installPlugin(new DependentPlugin());
await expect(Core.uninstallPlugin('base-plugin')).rejects.toThrow(
'required by'
);
});
});
describe('服务注册', () => {
it('应该允许插件注册服务', async () => {
class TestService {
public value = 42;
dispose() {}
}
class ServicePlugin implements IPlugin {
readonly name = 'service-plugin';
readonly version = '1.0.0';
install(core: Core, services: ServiceContainer) {
services.registerSingleton(TestService);
}
uninstall() {}
}
await Core.installPlugin(new ServicePlugin());
const service = Core.services.resolve(TestService);
expect(service.value).toBe(42);
});
it('应该允许插件访问Core和ServiceContainer', async () => {
let capturedCore: Core | null = null;
let capturedServices: ServiceContainer | null = null;
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
install(core: Core, services: ServiceContainer) {
capturedCore = core;
capturedServices = services;
}
uninstall() {}
}
await Core.installPlugin(new TestPlugin());
expect(capturedCore).toBe(Core.Instance);
expect(capturedServices).toBe(Core.services);
});
});
describe('异步插件', () => {
it('应该支持异步安装', async () => {
class AsyncPlugin implements IPlugin {
readonly name = 'async-plugin';
readonly version = '1.0.0';
initialized = false;
async install() {
await new Promise(resolve => setTimeout(resolve, 10));
this.initialized = true;
}
uninstall() {}
}
const plugin = new AsyncPlugin();
await Core.installPlugin(plugin);
expect(plugin.initialized).toBe(true);
});
it('应该支持异步卸载', async () => {
class AsyncPlugin implements IPlugin {
readonly name = 'async-plugin';
readonly version = '1.0.0';
cleaned = false;
install() {}
async uninstall() {
await new Promise(resolve => setTimeout(resolve, 10));
this.cleaned = true;
}
}
const plugin = new AsyncPlugin();
await Core.installPlugin(plugin);
await Core.uninstallPlugin('async-plugin');
expect(plugin.cleaned).toBe(true);
});
});
describe('错误处理', () => {
it('应该捕获安装错误', async () => {
class FailingPlugin implements IPlugin {
readonly name = 'failing-plugin';
readonly version = '1.0.0';
install() {
throw new Error('安装失败');
}
uninstall() {}
}
await expect(Core.installPlugin(new FailingPlugin())).rejects.toThrow(
'安装失败'
);
expect(Core.isPluginInstalled('failing-plugin')).toBe(false);
});
it('应该捕获卸载错误', async () => {
class FailingPlugin implements IPlugin {
readonly name = 'failing-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {
throw new Error('卸载失败');
}
}
await Core.installPlugin(new FailingPlugin());
await expect(Core.uninstallPlugin('failing-plugin')).rejects.toThrow(
'卸载失败'
);
});
});
describe('PluginManager直接使用', () => {
it('应该能够从ServiceContainer获取PluginManager', () => {
const pluginManager = Core.services.resolve(PluginManager);
expect(pluginManager).toBeDefined();
});
it('应该能够获取所有插件', async () => {
class Plugin1 implements IPlugin {
readonly name = 'plugin1';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
class Plugin2 implements IPlugin {
readonly name = 'plugin2';
readonly version = '2.0.0';
install() {}
uninstall() {}
}
await Core.installPlugin(new Plugin1());
await Core.installPlugin(new Plugin2());
const pluginManager = Core.services.resolve(PluginManager);
const allPlugins = pluginManager.getAllPlugins();
expect(allPlugins).toHaveLength(2);
expect(allPlugins.map(p => p.name)).toContain('plugin1');
expect(allPlugins.map(p => p.name)).toContain('plugin2');
});
it('应该能够获取插件元数据', async () => {
class TestPlugin implements IPlugin {
readonly name = 'test-plugin';
readonly version = '1.0.0';
install() {}
uninstall() {}
}
await Core.installPlugin(new TestPlugin());
const pluginManager = Core.services.resolve(PluginManager);
const metadata = pluginManager.getMetadata('test-plugin');
expect(metadata).toBeDefined();
expect(metadata?.name).toBe('test-plugin');
expect(metadata?.version).toBe('1.0.0');
expect(metadata?.state).toBe('installed');
expect(metadata?.installedAt).toBeDefined();
});
});
describe('生命周期', () => {
it('应该在Core销毁时卸载所有插件', async () => {
const uninstallCalls: string[] = [];
class Plugin1 implements IPlugin {
readonly name = 'plugin1';
readonly version = '1.0.0';
install() {}
uninstall() {
uninstallCalls.push('plugin1');
}
}
class Plugin2 implements IPlugin {
readonly name = 'plugin2';
readonly version = '1.0.0';
readonly dependencies = ['plugin1'] as const;
install() {}
uninstall() {
uninstallCalls.push('plugin2');
}
}
await Core.installPlugin(new Plugin1());
await Core.installPlugin(new Plugin2());
Core.destroy();
expect(uninstallCalls).toContain('plugin1');
expect(uninstallCalls).toContain('plugin2');
});
});
});