83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import type { TojuPluginManifest } from '../../../../shared-kernel';
|
|
import { resolvePluginLoadOrder } from './plugin-dependency-resolver.logic';
|
|
|
|
function manifest(id: string, overrides: Partial<TojuPluginManifest> = {}): TojuPluginManifest {
|
|
return {
|
|
apiVersion: '1.0.0',
|
|
compatibility: {
|
|
minimumTojuVersion: '1.0.0'
|
|
},
|
|
description: `${id} plugin`,
|
|
entrypoint: './main.js',
|
|
id,
|
|
kind: 'client',
|
|
schemaVersion: 1,
|
|
title: id,
|
|
version: '1.0.0',
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe('plugin dependency resolver', () => {
|
|
it('orders required dependencies before dependants', () => {
|
|
const featurePlugin = manifest('feature.chat', { relationships: { requires: [{ id: 'library.base' }] } });
|
|
const result = resolvePluginLoadOrder([{ manifest: featurePlugin }, { manifest: manifest('library.base') }]);
|
|
|
|
expect(result.blocked).toEqual([]);
|
|
expect(result.ordered.map((entry) => entry.id)).toEqual(['library.base', 'feature.chat']);
|
|
});
|
|
|
|
it('uses priority then plugin id for otherwise independent plugins', () => {
|
|
const result = resolvePluginLoadOrder([
|
|
{ manifest: manifest('plugin.zed') },
|
|
{ manifest: manifest('plugin.bootstrap', { load: { priority: 'bootstrap' } }) },
|
|
{ manifest: manifest('plugin.alpha') }
|
|
]);
|
|
|
|
expect(result.ordered.map((entry) => entry.id)).toEqual([
|
|
'plugin.bootstrap',
|
|
'plugin.alpha',
|
|
'plugin.zed'
|
|
]);
|
|
});
|
|
|
|
it('blocks missing dependencies and leaves valid plugins loadable', () => {
|
|
const blockedPlugin = manifest('plugin.blocked', { relationships: { requires: [{ id: 'missing.library' }] } });
|
|
const result = resolvePluginLoadOrder([{ manifest: manifest('plugin.valid') }, { manifest: blockedPlugin }]);
|
|
|
|
expect(result.ordered.map((entry) => entry.id)).toEqual(['plugin.valid']);
|
|
expect(result.blocked).toContainEqual({
|
|
message: 'Missing required plugin missing.library',
|
|
pluginId: 'plugin.blocked',
|
|
reason: 'missingDependency'
|
|
});
|
|
});
|
|
|
|
it('detects duplicate ids and cycles', () => {
|
|
const result = resolvePluginLoadOrder([
|
|
{ manifest: manifest('plugin.duplicate') },
|
|
{ manifest: manifest('plugin.duplicate') },
|
|
{ manifest: manifest('plugin.a', { relationships: { after: ['plugin.b'] } }) },
|
|
{ manifest: manifest('plugin.b', { relationships: { after: ['plugin.a'] } }) }
|
|
]);
|
|
|
|
expect(result.blocked).toEqual(expect.arrayContaining([
|
|
{
|
|
message: 'Duplicate plugin id',
|
|
pluginId: 'plugin.duplicate',
|
|
reason: 'duplicate'
|
|
},
|
|
{
|
|
message: 'Plugin load order contains a cycle',
|
|
pluginId: 'plugin.a',
|
|
reason: 'cycle'
|
|
},
|
|
{
|
|
message: 'Plugin load order contains a cycle',
|
|
pluginId: 'plugin.b',
|
|
reason: 'cycle'
|
|
}
|
|
]));
|
|
});
|
|
});
|