hermes-agent/apps/desktop/src/contrib/runtime-loader.test.ts
Teknium eaecca4a71 fix(desktop): widen local plugin-root fix — profile-aware root + dir-watch path
Follow-ups on top of #66911's salvaged commit:
- hermes:fs:desktopPluginsRoot now resolves the ACTIVE desktop profile
  (readActiveDesktopProfile) so named profiles keep their own
  profiles/<name>/desktop-plugins root instead of sharing the global one
  (profile-scope concern raised on the PR thread).
- startDirWatch in runtime-loader.ts was a third sibling site still
  deriving the watch path from the backend's hermes_home (added by the
  later fs-watch commit); routed through the same Electron-local
  resolver, with regression coverage.
2026-07-28 23:53:20 -07:00

77 lines
2.8 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { HermesReadDirResult } from '@/global'
import type * as HermesModule from '@/hermes'
import { discoverRuntimePlugins, watchRuntimePlugins } from './runtime-loader'
// getStatus would supply the connected backend's hermes_home — a REMOTE path in
// remote mode. The disk scanner must NOT derive the plugin root from it (#66899).
const getStatus = vi.fn(async () => ({ hermes_home: '/remote/box/.hermes' }))
vi.mock('@/hermes', async importActual => ({
...(await importActual<typeof HermesModule>()),
getStatus: () => getStatus()
}))
const desktopPluginsRoot = vi.fn<() => Promise<string>>()
const readDir = vi.fn<(path: string) => Promise<HermesReadDirResult>>()
const watchDirectory = vi.fn<(path: string) => Promise<{ id: string }>>()
const onPreviewFileChanged = vi.fn()
beforeEach(() => {
desktopPluginsRoot.mockReset()
readDir.mockReset()
watchDirectory.mockReset()
onPreviewFileChanged.mockReset()
getStatus.mockClear()
;(window as unknown as { hermesDesktop: unknown }).hermesDesktop = {
desktopPluginsRoot,
onPreviewFileChanged,
readDir,
watchDirectory
}
})
afterEach(() => {
delete (window as unknown as { hermesDesktop?: unknown }).hermesDesktop
})
describe('scanDiskPlugins (#66899)', () => {
it('scans the Electron-resolved local root, never the backend hermes_home', async () => {
desktopPluginsRoot.mockResolvedValue('/local/.hermes/desktop-plugins')
readDir.mockResolvedValue({ entries: [] })
await discoverRuntimePlugins()
expect(desktopPluginsRoot).toHaveBeenCalled()
expect(readDir).toHaveBeenCalledWith('/local/.hermes/desktop-plugins')
// The remote backend's hermes_home must never feed the local plugin scan.
expect(getStatus).not.toHaveBeenCalled()
expect(readDir).not.toHaveBeenCalledWith('/remote/box/.hermes/desktop-plugins')
})
it('no-ops when the resolver yields no local root', async () => {
desktopPluginsRoot.mockResolvedValue('')
await discoverRuntimePlugins()
expect(readDir).not.toHaveBeenCalled()
})
})
describe('watchRuntimePlugins dir watch (#66899)', () => {
it('watches the Electron-resolved local root, never the backend hermes_home', async () => {
desktopPluginsRoot.mockResolvedValue('/local/.hermes/desktop-plugins')
readDir.mockResolvedValue({ entries: [] })
watchDirectory.mockResolvedValue({ id: 'watch-1' })
watchRuntimePlugins()
// Drain the async scan + startDirWatch chains.
await vi.waitFor(() => expect(watchDirectory).toHaveBeenCalled())
expect(watchDirectory).toHaveBeenCalledWith('/local/.hermes/desktop-plugins')
expect(watchDirectory).not.toHaveBeenCalledWith('/remote/box/.hermes/desktop-plugins')
expect(getStatus).not.toHaveBeenCalled()
})
})