Marketplace backend (agor-pro/src/marketplace.rs): fetch catalog from GitHub, download+verify+extract plugins, install/uninstall/update with SHA-256 checksum verification and path traversal protection. 6 Tauri plugin commands. PluginMarketplace.svelte: Browse/Installed tabs, search, plugin cards with permission badges, one-click install/uninstall/update. Plugin catalog repo: agents-orchestrator/agor-plugins (3 seed plugins). Plugin scaffolding: scripts/plugin-init.sh. 7 marketplace vitest tests, 3 Rust tests.
99 lines
4.6 KiB
TypeScript
99 lines
4.6 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-Commercial
|
|
// Marketplace tests — catalog fetch, install, uninstall, update flows.
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
vi.mock('@tauri-apps/api/core', () => ({
|
|
invoke: vi.fn(),
|
|
}));
|
|
|
|
describe('Marketplace Bridge', async () => {
|
|
const { invoke } = await import('@tauri-apps/api/core');
|
|
const mockInvoke = vi.mocked(invoke);
|
|
|
|
beforeEach(() => {
|
|
mockInvoke.mockReset();
|
|
});
|
|
|
|
it('exports all marketplace functions', async () => {
|
|
const bridge = await import('../../src/lib/commercial/pro-bridge');
|
|
expect(typeof bridge.proMarketplaceFetchCatalog).toBe('function');
|
|
expect(typeof bridge.proMarketplaceInstalled).toBe('function');
|
|
expect(typeof bridge.proMarketplaceInstall).toBe('function');
|
|
expect(typeof bridge.proMarketplaceUninstall).toBe('function');
|
|
expect(typeof bridge.proMarketplaceCheckUpdates).toBe('function');
|
|
expect(typeof bridge.proMarketplaceUpdate).toBe('function');
|
|
});
|
|
|
|
it('proMarketplaceFetchCatalog calls correct plugin command', async () => {
|
|
const { proMarketplaceFetchCatalog } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce([
|
|
{ id: 'hello-world', name: 'Hello World', version: '1.0.0', author: 'Test',
|
|
description: 'Test plugin', downloadUrl: 'https://example.com/hw.tar.gz',
|
|
checksumSha256: 'abc', permissions: ['palette'], tags: ['example'] },
|
|
]);
|
|
const result = await proMarketplaceFetchCatalog();
|
|
expect(mockInvoke).toHaveBeenCalledWith('plugin:agor-pro|pro_marketplace_fetch_catalog');
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0].id).toBe('hello-world');
|
|
});
|
|
|
|
it('proMarketplaceInstalled returns installed plugins', async () => {
|
|
const { proMarketplaceInstalled } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce([
|
|
{ id: 'hello-world', name: 'Hello World', version: '1.0.0', author: 'Test',
|
|
description: 'Test', permissions: ['palette'],
|
|
installPath: '/home/.config/agor/plugins/hello-world',
|
|
hasUpdate: false, latestVersion: null },
|
|
]);
|
|
const result = await proMarketplaceInstalled();
|
|
expect(result[0].installPath).toContain('hello-world');
|
|
expect(result[0].hasUpdate).toBe(false);
|
|
});
|
|
|
|
it('proMarketplaceInstall calls with pluginId', async () => {
|
|
const { proMarketplaceInstall } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce({
|
|
id: 'git-stats', name: 'Git Stats', version: '1.0.0', author: 'Test',
|
|
description: 'Git stats', permissions: ['palette'],
|
|
installPath: '/home/.config/agor/plugins/git-stats',
|
|
hasUpdate: false, latestVersion: null,
|
|
});
|
|
const result = await proMarketplaceInstall('git-stats');
|
|
expect(mockInvoke).toHaveBeenCalledWith('plugin:agor-pro|pro_marketplace_install', { pluginId: 'git-stats' });
|
|
expect(result.id).toBe('git-stats');
|
|
});
|
|
|
|
it('proMarketplaceUninstall calls with pluginId', async () => {
|
|
const { proMarketplaceUninstall } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce(undefined);
|
|
await proMarketplaceUninstall('hello-world');
|
|
expect(mockInvoke).toHaveBeenCalledWith('plugin:agor-pro|pro_marketplace_uninstall', { pluginId: 'hello-world' });
|
|
});
|
|
|
|
it('proMarketplaceCheckUpdates returns plugins with update flags', async () => {
|
|
const { proMarketplaceCheckUpdates } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce([
|
|
{ id: 'hello-world', name: 'Hello World', version: '1.0.0', author: 'Test',
|
|
description: 'Test', permissions: ['palette'],
|
|
installPath: '/home/.config/agor/plugins/hello-world',
|
|
hasUpdate: true, latestVersion: '2.0.0' },
|
|
]);
|
|
const result = await proMarketplaceCheckUpdates();
|
|
expect(result[0].hasUpdate).toBe(true);
|
|
expect(result[0].latestVersion).toBe('2.0.0');
|
|
});
|
|
|
|
it('proMarketplaceUpdate calls with pluginId', async () => {
|
|
const { proMarketplaceUpdate } = await import('../../src/lib/commercial/pro-bridge');
|
|
mockInvoke.mockResolvedValueOnce({
|
|
id: 'hello-world', name: 'Hello World', version: '2.0.0', author: 'Test',
|
|
description: 'Updated', permissions: ['palette'],
|
|
installPath: '/home/.config/agor/plugins/hello-world',
|
|
hasUpdate: false, latestVersion: null,
|
|
});
|
|
const result = await proMarketplaceUpdate('hello-world');
|
|
expect(mockInvoke).toHaveBeenCalledWith('plugin:agor-pro|pro_marketplace_update', { pluginId: 'hello-world' });
|
|
expect(result.version).toBe('2.0.0');
|
|
});
|
|
});
|