initialize Electron desktop app and remove unused files
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const listConversationsMock = vi.fn(async (ctx: any) => { ctx.body = { sessions: [{ id: 'conversation-1' }] } })
|
||||
const getConversationMessagesMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id, messages: [] } })
|
||||
const getConversationMessagesPaginatedMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id, messages: [], pagination: {} } })
|
||||
const listMock = vi.fn(async (ctx: any) => { ctx.body = { sessions: [{ id: 's1' }] } })
|
||||
const countMock = vi.fn(async (ctx: any) => { ctx.body = { count: 1 } })
|
||||
const listHermesSessionsMock = vi.fn(async (ctx: any) => { ctx.body = { sessions: [{ id: 'hermes-1' }] } })
|
||||
const getHermesSessionMock = vi.fn(async (ctx: any) => { ctx.body = { session: { id: ctx.params.id } } })
|
||||
const importHermesSessionMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id } })
|
||||
const searchMock = vi.fn(async (ctx: any) => { ctx.body = { results: [{ id: 'search-1' }] } })
|
||||
const getMock = vi.fn(async (ctx: any) => { ctx.body = { session: { id: ctx.params.id } } })
|
||||
const getContextMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id, messages: [] } })
|
||||
const removeMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const renameMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const archiveMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const unarchiveMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const setWorkspaceMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const setModelMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const listWorkspaceFoldersMock = vi.fn(async (ctx: any) => { ctx.body = { folders: [] } })
|
||||
const createWorkspaceFolderMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const renameWorkspaceFolderMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const deleteWorkspaceFolderMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const usageBatchMock = vi.fn(async (ctx: any) => { ctx.body = {} })
|
||||
const usageSingleMock = vi.fn(async (ctx: any) => { ctx.body = { input_tokens: 0, output_tokens: 0 } })
|
||||
const usageStatsMock = vi.fn(async (ctx: any) => { ctx.body = { total_input_tokens: 0, total_output_tokens: 0 } })
|
||||
const contextLengthMock = vi.fn(async (ctx: any) => { ctx.body = { context_length: 256000 } })
|
||||
const batchRemoveMock = vi.fn(async (ctx: any) => { ctx.body = { deleted: 1, failed: 0, errors: [] } })
|
||||
const exportSessionMock = vi.fn(async (ctx: any) => { ctx.body = JSON.stringify({ id: ctx.params.id }) })
|
||||
const listWorkspaceRunChangesMock = vi.fn(async (ctx: any) => { ctx.body = { changes: [] } })
|
||||
const getWorkspaceRunChangeFileMock = vi.fn(async (ctx: any) => { ctx.body = { file: null } })
|
||||
const listWorkspaceFilesMock = vi.fn(async (ctx: any) => { ctx.body = { entries: [], path: '' } })
|
||||
const readWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { content: '' } })
|
||||
const writeWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const mkdirWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const deleteWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const renameWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
const copyWorkspaceFileMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
|
||||
vi.mock('../../packages/server/src/controllers/hermes/sessions', () => ({
|
||||
listConversations: listConversationsMock,
|
||||
getConversationMessages: getConversationMessagesMock,
|
||||
getConversationMessagesPaginated: getConversationMessagesPaginatedMock,
|
||||
list: listMock,
|
||||
count: countMock,
|
||||
listHermesSessions: listHermesSessionsMock,
|
||||
getHermesSession: getHermesSessionMock,
|
||||
importHermesSession: importHermesSessionMock,
|
||||
search: searchMock,
|
||||
get: getMock,
|
||||
getContext: getContextMock,
|
||||
remove: removeMock,
|
||||
batchRemove: batchRemoveMock,
|
||||
rename: renameMock,
|
||||
archive: archiveMock,
|
||||
unarchive: unarchiveMock,
|
||||
setWorkspace: setWorkspaceMock,
|
||||
setModel: setModelMock,
|
||||
listWorkspaceFolders: listWorkspaceFoldersMock,
|
||||
createWorkspaceFolder: createWorkspaceFolderMock,
|
||||
renameWorkspaceFolder: renameWorkspaceFolderMock,
|
||||
deleteWorkspaceFolder: deleteWorkspaceFolderMock,
|
||||
usageBatch: usageBatchMock,
|
||||
usageSingle: usageSingleMock,
|
||||
usageStats: usageStatsMock,
|
||||
contextLength: contextLengthMock,
|
||||
exportSession: exportSessionMock,
|
||||
listWorkspaceRunChanges: listWorkspaceRunChangesMock,
|
||||
getWorkspaceRunChangeFile: getWorkspaceRunChangeFileMock,
|
||||
listWorkspaceFiles: listWorkspaceFilesMock,
|
||||
readWorkspaceFile: readWorkspaceFileMock,
|
||||
writeWorkspaceFile: writeWorkspaceFileMock,
|
||||
mkdirWorkspaceFile: mkdirWorkspaceFileMock,
|
||||
deleteWorkspaceFile: deleteWorkspaceFileMock,
|
||||
renameWorkspaceFile: renameWorkspaceFileMock,
|
||||
copyWorkspaceFile: copyWorkspaceFileMock,
|
||||
}))
|
||||
|
||||
describe('session routes', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
listConversationsMock.mockClear()
|
||||
getConversationMessagesMock.mockClear()
|
||||
getConversationMessagesPaginatedMock.mockClear()
|
||||
listMock.mockClear()
|
||||
countMock.mockClear()
|
||||
listHermesSessionsMock.mockClear()
|
||||
getHermesSessionMock.mockClear()
|
||||
importHermesSessionMock.mockClear()
|
||||
searchMock.mockClear()
|
||||
getMock.mockClear()
|
||||
getContextMock.mockClear()
|
||||
removeMock.mockClear()
|
||||
renameMock.mockClear()
|
||||
archiveMock.mockClear()
|
||||
unarchiveMock.mockClear()
|
||||
setModelMock.mockClear()
|
||||
listWorkspaceFoldersMock.mockClear()
|
||||
createWorkspaceFolderMock.mockClear()
|
||||
renameWorkspaceFolderMock.mockClear()
|
||||
deleteWorkspaceFolderMock.mockClear()
|
||||
listWorkspaceRunChangesMock.mockClear()
|
||||
getWorkspaceRunChangeFileMock.mockClear()
|
||||
listWorkspaceFilesMock.mockClear()
|
||||
readWorkspaceFileMock.mockClear()
|
||||
writeWorkspaceFileMock.mockClear()
|
||||
mkdirWorkspaceFileMock.mockClear()
|
||||
deleteWorkspaceFileMock.mockClear()
|
||||
renameWorkspaceFileMock.mockClear()
|
||||
copyWorkspaceFileMock.mockClear()
|
||||
})
|
||||
|
||||
it('registers conversations, session list, and search routes', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const paths = sessionRoutes.stack.map((entry: any) => entry.path)
|
||||
|
||||
expect(paths).toEqual(expect.arrayContaining([
|
||||
'/api/hermes/sessions/conversations',
|
||||
'/api/hermes/sessions/conversations/:id/messages',
|
||||
'/api/hermes/sessions/conversations/:id/messages/paginated',
|
||||
'/api/hermes/sessions',
|
||||
'/api/hermes/sessions/count',
|
||||
'/api/hermes/sessions/hermes',
|
||||
'/api/hermes/sessions/hermes/:id',
|
||||
'/api/hermes/sessions/hermes/:id/import',
|
||||
'/api/hermes/search/sessions',
|
||||
'/api/hermes/sessions/search',
|
||||
'/api/hermes/sessions/usage',
|
||||
'/api/hermes/usage/stats',
|
||||
'/api/hermes/sessions/context-length',
|
||||
'/api/hermes/sessions/:id/context',
|
||||
'/api/hermes/sessions/:id/workspace-run-changes',
|
||||
'/api/hermes/sessions/:id/workspace-run-changes/:changeId/files/:fileId',
|
||||
'/api/hermes/sessions/:id/workspace-files/list',
|
||||
'/api/hermes/sessions/:id/workspace-file/read',
|
||||
'/api/hermes/sessions/:id/workspace-file/write',
|
||||
'/api/hermes/sessions/:id/workspace-file/mkdir',
|
||||
'/api/hermes/sessions/:id/workspace-file/delete',
|
||||
'/api/hermes/sessions/:id/workspace-file/rename',
|
||||
'/api/hermes/sessions/:id/workspace-file/copy',
|
||||
'/api/hermes/sessions/:id',
|
||||
'/api/hermes/sessions/:id/export',
|
||||
'/api/hermes/sessions/:id/usage',
|
||||
'/api/hermes/sessions/:id/rename',
|
||||
'/api/hermes/sessions/:id/archive',
|
||||
'/api/hermes/sessions/:id/unarchive',
|
||||
'/api/hermes/sessions/:id/model',
|
||||
'/api/hermes/workspace/folders',
|
||||
'/api/hermes/workspace/folders/rename',
|
||||
]))
|
||||
})
|
||||
|
||||
it('delegates session count route before the session id route', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const countLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/count')
|
||||
const idLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id')
|
||||
expect(sessionRoutes.stack.indexOf(countLayer)).toBeLessThan(sessionRoutes.stack.indexOf(idLayer))
|
||||
|
||||
const ctx: any = { query: { source: 'cli' }, body: null, params: {} }
|
||||
await countLayer.stack[0](ctx)
|
||||
|
||||
expect(countMock).toHaveBeenCalledWith(ctx)
|
||||
expect(getMock).not.toHaveBeenCalled()
|
||||
expect(ctx.body).toEqual({ count: 1 })
|
||||
})
|
||||
|
||||
it('delegates session context route to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/context')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { query: {}, body: null, params: { id: 'session-1' } }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(getContextMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ session_id: 'session-1', messages: [] })
|
||||
})
|
||||
|
||||
it('delegates workspace folder routes to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const listLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/workspace/folders' && entry.methods.includes('HEAD'))
|
||||
const createLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/workspace/folders' && entry.methods.includes('POST'))
|
||||
const renameLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/workspace/folders/rename')
|
||||
const deleteLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/workspace/folders' && entry.methods.includes('DELETE'))
|
||||
|
||||
const listCtx: any = { query: {}, request: { body: {} }, body: null, params: {} }
|
||||
await listLayer.stack[0](listCtx)
|
||||
expect(listWorkspaceFoldersMock).toHaveBeenCalledWith(listCtx)
|
||||
|
||||
const createCtx: any = { query: {}, request: { body: { parentPath: '', name: 'new-folder' } }, body: null, params: {} }
|
||||
await createLayer.stack[0](createCtx)
|
||||
expect(createWorkspaceFolderMock).toHaveBeenCalledWith(createCtx)
|
||||
|
||||
const renameCtx: any = { query: {}, request: { body: { path: 'old-folder', name: 'new-folder' } }, body: null, params: {} }
|
||||
await renameLayer.stack[0](renameCtx)
|
||||
expect(renameWorkspaceFolderMock).toHaveBeenCalledWith(renameCtx)
|
||||
|
||||
const deleteCtx: any = { query: {}, request: { body: { path: 'new-folder' } }, body: null, params: {} }
|
||||
await deleteLayer.stack[0](deleteCtx)
|
||||
expect(deleteWorkspaceFolderMock).toHaveBeenCalledWith(deleteCtx)
|
||||
})
|
||||
|
||||
it('delegates session workspace file routes to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const listLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-files/list')
|
||||
const readLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/read')
|
||||
const writeLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/write')
|
||||
const mkdirLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/mkdir')
|
||||
const deleteLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/delete')
|
||||
const renameLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/rename')
|
||||
const copyLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/workspace-file/copy')
|
||||
|
||||
const ctx: any = { query: {}, request: { body: {} }, body: null, params: { id: 'session-1' } }
|
||||
await listLayer.stack[0](ctx)
|
||||
await readLayer.stack[0](ctx)
|
||||
await writeLayer.stack[0](ctx)
|
||||
await mkdirLayer.stack[0](ctx)
|
||||
await deleteLayer.stack[0](ctx)
|
||||
await renameLayer.stack[0](ctx)
|
||||
await copyLayer.stack[0](ctx)
|
||||
|
||||
expect(listWorkspaceFilesMock).toHaveBeenCalledWith(ctx)
|
||||
expect(readWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
expect(writeWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
expect(mkdirWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
expect(deleteWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
expect(renameWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
expect(copyWorkspaceFileMock).toHaveBeenCalledWith(ctx)
|
||||
})
|
||||
|
||||
it('delegates session search to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/search/sessions')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { query: { q: 'docker', limit: '8' }, body: null, params: {} }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(searchMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ results: [{ id: 'search-1' }] })
|
||||
})
|
||||
|
||||
it('keeps the legacy search path wired to the same controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/search')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { query: { q: 'docker' }, body: null, params: {} }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(searchMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ results: [{ id: 'search-1' }] })
|
||||
})
|
||||
|
||||
it('delegates conversations list and detail routes', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const listLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/conversations')
|
||||
const detailLayer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/conversations/:id/messages')
|
||||
|
||||
const listCtx: any = { query: {}, body: null, params: {} }
|
||||
await listLayer.stack[0](listCtx)
|
||||
expect(listConversationsMock).toHaveBeenCalledWith(listCtx)
|
||||
expect(listCtx.body).toEqual({ sessions: [{ id: 'conversation-1' }] })
|
||||
|
||||
const detailCtx: any = { params: { id: 'child-session' }, query: {}, body: null }
|
||||
await detailLayer.stack[0](detailCtx)
|
||||
expect(getConversationMessagesMock).toHaveBeenCalledWith(detailCtx)
|
||||
expect(detailCtx.body).toEqual({ session_id: 'child-session', messages: [] })
|
||||
})
|
||||
|
||||
it('delegates Hermes session import to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/hermes/:id/import')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { params: { id: 'hermes-abc' }, query: {}, request: { body: { profile: 'default' } }, body: null }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(importHermesSessionMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ session_id: 'hermes-abc' })
|
||||
})
|
||||
|
||||
it('delegates session export to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/export')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { params: { id: 'session-abc' }, query: {}, body: null, set: vi.fn() }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(exportSessionMock).toHaveBeenCalledWith(ctx)
|
||||
})
|
||||
|
||||
it('delegates session archive to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/archive')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { params: { id: 'session-abc' }, query: {}, body: null }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(archiveMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ ok: true })
|
||||
})
|
||||
|
||||
it('delegates session unarchive to the controller', async () => {
|
||||
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
|
||||
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/unarchive')
|
||||
const handler = layer.stack[0]
|
||||
const ctx: any = { params: { id: 'session-abc' }, query: {}, body: null }
|
||||
|
||||
await handler(ctx)
|
||||
|
||||
expect(unarchiveMock).toHaveBeenCalledWith(ctx)
|
||||
expect(ctx.body).toEqual({ ok: true })
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user