Files
EasyCLIProxyAPI/tests/pluginMock.test.ts
T
lzt404 d29a1af69b feat: add plugin management and redesign dashboard and usage analytics
- Add plugin store, version selection, configuration, and OAuth login
- Support Muse (Meta) OAuth device authorization
- Redesign home dashboard and add batch model health checks
- Support per-credential provider editing, removal, and reordering
- Improve usage charts and track actual response models
- Preserve installed plugins and runtime data during portable rebuilds
- Update translations, documentation, and related tests
2026-10-04 17:31:52 +08:00

72 lines
5.1 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { createBrowserMockRuntime } from '../src/mocks/browserMockRuntime';
const request = (runtime: ReturnType<typeof createBrowserMockRuntime>, method: string, path: string, body?: unknown, query?: unknown) =>
runtime.invoke('management_request', { request: { method, path, body, query } });
describe('browser plugin mock', () => {
test('exposes installed plugins, update catalog and a non-network resource page', async () => {
const runtime = createBrowserMockRuntime('running');
expect(await runtime.invoke('get_plugin_support')).toBe(true);
expect(await request(runtime, 'GET', '/plugins')).toMatchObject({
plugins_enabled: true,
plugins: [{ id: 'request-inspector', effective_enabled: true, menus: [{ menu: 'Inspector status' }] }],
});
expect(await request(runtime, 'GET', '/plugins/store')).toMatchObject({ plugins: [
{ id: 'request-inspector', installed: true, update_available: true },
{ id: 'response-tags', source_id: 'community-demo', installed: false },
] });
const url = await runtime.invoke('get_plugin_resource_url', { pluginId: 'request-inspector', menuIndex: 0 });
expect(url).toStartWith('data:text/html;charset=utf-8,');
await expect(runtime.invoke('get_plugin_resource_url', { pluginId: 'request-inspector', menuIndex: 1 })).rejects.toThrow('Plugin page is unavailable');
});
test('keeps configuration, global state and resource availability synchronized', async () => {
const runtime = createBrowserMockRuntime('running');
const path = '/config/plugins/configs/request-inspector';
await request(runtime, 'PUT', path, { enabled: true, priority: 5, options: { first: 1 }, literal: null });
await request(runtime, 'PATCH', path, { options: { second: false }, nullable: null });
expect(await request(runtime, 'GET', path)).toEqual({ enabled: true, priority: 5, options: { first: 1, second: false }, literal: null, nullable: null });
await request(runtime, 'DELETE', `${path}/literal`);
await request(runtime, 'PUT', `${path}/enabled`, false);
await expect(runtime.invoke('get_plugin_resource_url', { pluginId: 'request-inspector', menuIndex: 0 })).rejects.toThrow('Plugin page is unavailable');
await request(runtime, 'PATCH', '/config/plugins', { enabled: false, 'store-sources': [], 'store-auth': [] });
expect(await runtime.invoke('get_core_config_settings')).toMatchObject({ pluginsEnabled: false });
expect(await runtime.invoke('get_extended_core_config')).toMatchObject({ plugins: { enabled: false, configs: { 'request-inspector': { priority: 5 } } } });
expect(await request(runtime, 'GET', '/plugins/store')).toMatchObject({ plugins: [{ id: 'request-inspector' }] });
const config = await request(runtime, 'GET', path) as Record<string, unknown>;
expect(config).not.toHaveProperty('literal');
config.priority = 99;
expect(await request(runtime, 'GET', path)).toHaveProperty('priority', 5);
});
test('persists installs and deletes and honors the selected source and version', async () => {
const runtime = createBrowserMockRuntime('running');
await expect(request(runtime, 'POST', '/plugins/store/response-tags/install', {}, { source: 'official' })).rejects.toThrow('not_found');
expect(await request(runtime, 'POST', '/plugins/store/response-tags/install', {}, { source: 'community-demo', version: 'v0.4.0' })).toMatchObject({
status: 'installed', id: 'response-tags', source_id: 'community-demo', version: '0.4.0', restart_required: false,
});
expect(await request(runtime, 'GET', '/config/plugins/configs/response-tags')).toEqual({ enabled: true });
expect(await request(runtime, 'DELETE', '/plugins/response-tags')).toMatchObject({ file_deleted: true, configured_removed: true });
await expect(request(runtime, 'GET', '/config/plugins/configs/response-tags')).rejects.toThrow('Management API error (404): not_found');
expect(await request(runtime, 'GET', '/plugins')).toMatchObject({ plugins: [{ id: 'request-inspector' }] });
});
test('respects core lifecycle and explicit error scenarios', async () => {
const stopped = createBrowserMockRuntime('stopped');
await expect(stopped.invoke('get_plugin_support')).rejects.toThrow('core is not ready');
await stopped.invoke('start_core_process');
expect(await stopped.invoke('get_plugin_support')).toBe(true);
await stopped.invoke('stop_core_process');
await expect(request(stopped, 'GET', '/plugins')).rejects.toThrow('core is not ready');
const empty = createBrowserMockRuntime('empty');
await empty.invoke('install_bundled_core');
await empty.invoke('start_core_process');
expect(await request(empty, 'GET', '/plugins')).toMatchObject({ plugins_enabled: false, plugins: [] });
const error = createBrowserMockRuntime('error');
await expect(error.invoke('get_plugin_support')).rejects.toThrow('Browser Mock error scenario');
await expect(error.invoke('get_plugin_resource_url')).rejects.toThrow('Browser Mock error scenario');
await expect(request(error, 'GET', '/plugins')).rejects.toThrow('Browser Mock error scenario');
});
});