cc-haha/desktop/src/stores/pluginStore.test.ts
程序员阿江(Relakkes) de63d82633 fix: keep desktop plugin changes visible without restarting chats
Plugin enablement already has a live CLI reload control path, so desktop now applies plugin changes by refreshing the active session instead of waiting for a future conversation startup. The server forwards reload_plugins to the active CLI session, refreshes session slash-command cache, and notifies the client. The desktop plugin store automatically reloads after mutating plugin state, and the empty-session composer refetches skills when plugin capabilities change.

Constraint: Existing CLI exposes reload_plugins as the supported hot-refresh mechanism for commands, agents, plugins, and MCP state.

Rejected: Start a hidden replacement CLI process | higher cost, extra process lifecycle risk, and less precise than the existing control channel.

Confidence: high

Scope-risk: moderate

Directive: Keep plugin refresh routed through reload_plugins unless the CLI control contract is removed or changed.

Tested: bun test src/server/__tests__/plugins.test.ts

Tested: cd desktop && bun run test --run src/stores/pluginStore.test.ts src/__tests__/pluginsSettings.test.tsx src/pages/EmptySession.test.tsx

Tested: bun run check:server

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build

Not-tested: bun run check:desktop is blocked by a pre-existing color-mix compatibility failure in desktop/src/theme/globals.css.
2026-05-15 19:51:01 +08:00

92 lines
2.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { usePluginStore } from './pluginStore'
import { pluginsApi } from '../api/plugins'
vi.mock('../api/plugins', () => ({
pluginsApi: {
list: vi.fn(),
detail: vi.fn(),
enable: vi.fn(),
disable: vi.fn(),
update: vi.fn(),
uninstall: vi.fn(),
reload: vi.fn(),
},
}))
const mockedPluginsApi = vi.mocked(pluginsApi)
describe('pluginStore', () => {
beforeEach(() => {
vi.clearAllMocks()
mockedPluginsApi.list.mockResolvedValue({
plugins: [],
marketplaces: [],
summary: { total: 0, enabled: 0, errorCount: 0, marketplaceCount: 0 },
})
mockedPluginsApi.reload.mockResolvedValue({
ok: true,
summary: {
enabled: 1,
disabled: 0,
skills: 1,
agents: 0,
hooks: 0,
mcpServers: 0,
lspServers: 0,
errors: 0,
},
session: {
applied: true,
commands: 1,
agents: 0,
plugins: 1,
mcpServers: 0,
errors: 0,
},
})
usePluginStore.setState({
plugins: [],
marketplaces: [],
summary: null,
selectedPlugin: null,
lastReloadSummary: null,
isLoading: false,
isDetailLoading: false,
isApplying: false,
error: null,
})
})
it('reloads the active CLI session after enabling a plugin', async () => {
mockedPluginsApi.enable.mockResolvedValue({
ok: true,
message: 'enabled',
})
const message = await usePluginStore
.getState()
.enablePlugin('draw@test', 'user', '/workspace/project', 'session-1')
expect(message).toBe('enabled')
expect(mockedPluginsApi.enable).toHaveBeenCalledWith({
id: 'draw@test',
scope: 'user',
})
expect(mockedPluginsApi.reload).toHaveBeenCalledWith(
'/workspace/project',
'session-1',
)
expect(usePluginStore.getState().lastReloadSummary).toEqual({
enabled: 1,
disabled: 0,
skills: 1,
agents: 0,
hooks: 0,
mcpServers: 0,
lspServers: 0,
errors: 0,
})
})
})