initialize Electron desktop app and remove unused files
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
// @vitest-environment jsdom
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useChatStore } from '@/stores/hermes/chat'
|
||||
import { archiveSession, fetchSessions } from '@/api/hermes/sessions'
|
||||
|
||||
vi.mock('@/api/hermes/sessions', () => ({
|
||||
archiveSession: vi.fn(),
|
||||
fetchSessions: vi.fn(),
|
||||
fetchSessionMessagesPage: vi.fn(),
|
||||
fetchWorkspaceRunChangesForSession: vi.fn(async () => []),
|
||||
fetchWorkspaceRunChangeFile: vi.fn(async () => null),
|
||||
deleteSession: vi.fn(),
|
||||
setSessionModel: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/hermes/chat', () => ({
|
||||
startRunViaSocket: vi.fn(),
|
||||
resumeSession: vi.fn((_sessionId: string, cb: (data: any) => void) => {
|
||||
cb({ session_id: _sessionId, isWorking: false, messages: [] })
|
||||
}),
|
||||
registerSessionHandlers: vi.fn(),
|
||||
unregisterSessionHandlers: vi.fn(),
|
||||
getChatRunSocket: vi.fn(() => ({ emit: vi.fn() })),
|
||||
respondToolApproval: vi.fn(),
|
||||
respondClarify: vi.fn(),
|
||||
onPeerUserMessage: vi.fn(() => vi.fn()),
|
||||
onSessionCommand: vi.fn(() => vi.fn()),
|
||||
onSessionTitleUpdated: vi.fn(() => vi.fn()),
|
||||
onSessionWorkspaceUpdated: vi.fn(() => vi.fn()),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
getActiveProfileName: () => 'default',
|
||||
}))
|
||||
|
||||
vi.mock('@/api/hermes/download', () => ({
|
||||
getDownloadUrl: (_path: string, name: string) => `/download/${name}`,
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/completion-sound', () => ({
|
||||
primeCompletionSound: vi.fn(),
|
||||
playCompletionSound: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/completion-notification', () => ({
|
||||
showCompletionNotification: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/session-sync', () => ({
|
||||
subscribeSessionSync: vi.fn(() => vi.fn()),
|
||||
publishSessionSync: vi.fn(),
|
||||
}))
|
||||
|
||||
function makeSession(id: string, times: Partial<{ started_at: number; ended_at: number | null; last_active: number }>) {
|
||||
return {
|
||||
id,
|
||||
profile: 'default',
|
||||
source: 'cli',
|
||||
title: id,
|
||||
preview: '',
|
||||
started_at: times.started_at ?? 1,
|
||||
ended_at: times.ended_at ?? null,
|
||||
last_active: times.last_active,
|
||||
message_count: 0,
|
||||
tool_call_count: 0,
|
||||
input_tokens: 0,
|
||||
output_tokens: 0,
|
||||
model: 'gpt-test',
|
||||
provider: 'test',
|
||||
}
|
||||
}
|
||||
|
||||
describe('chat session ordering', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.clear()
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
it('orders sessions by the newest known activity timestamp, including started_at', async () => {
|
||||
vi.mocked(fetchSessions)
|
||||
.mockResolvedValueOnce([
|
||||
makeSession('older-active-session', { started_at: 100, last_active: 900 }),
|
||||
makeSession('new-session-with-stale-last-active', { started_at: 1000, last_active: 200 }),
|
||||
] as any)
|
||||
.mockResolvedValueOnce([])
|
||||
|
||||
const store = useChatStore()
|
||||
await store.loadSessions()
|
||||
|
||||
expect(store.sessions.map(session => session.id)).toEqual([
|
||||
'new-session-with-stale-last-active',
|
||||
'older-active-session',
|
||||
])
|
||||
expect(store.sessions[0].updatedAt).toBe(1000_000)
|
||||
})
|
||||
|
||||
it('removes an archived session from the runtime session list', async () => {
|
||||
vi.mocked(fetchSessions)
|
||||
.mockResolvedValueOnce([
|
||||
makeSession('active-session', { started_at: 1000, last_active: 1000 }),
|
||||
makeSession('archive-me', { started_at: 900, last_active: 900 }),
|
||||
] as any)
|
||||
.mockResolvedValueOnce([])
|
||||
.mockResolvedValueOnce([
|
||||
makeSession('active-session', { started_at: 1000, last_active: 1000 }),
|
||||
] as any)
|
||||
.mockResolvedValueOnce([])
|
||||
vi.mocked(archiveSession).mockResolvedValueOnce(true)
|
||||
|
||||
const store = useChatStore()
|
||||
await store.loadSessions()
|
||||
const ok = await store.archiveSession('archive-me')
|
||||
|
||||
expect(ok).toBe(true)
|
||||
expect(archiveSession).toHaveBeenCalledWith('archive-me')
|
||||
expect(store.sessions.map(session => session.id)).toEqual(['active-session'])
|
||||
})
|
||||
|
||||
it('clears the active session when archiving the only runtime session', async () => {
|
||||
vi.mocked(fetchSessions)
|
||||
.mockResolvedValueOnce([
|
||||
makeSession('archive-me', { started_at: 1000, last_active: 1000 }),
|
||||
] as any)
|
||||
.mockResolvedValueOnce([])
|
||||
vi.mocked(archiveSession).mockResolvedValueOnce(true)
|
||||
|
||||
const store = useChatStore()
|
||||
await store.loadSessions()
|
||||
|
||||
expect(store.activeSessionId).toBe('archive-me')
|
||||
|
||||
const ok = await store.archiveSession('archive-me')
|
||||
|
||||
expect(ok).toBe(true)
|
||||
expect(archiveSession).toHaveBeenCalledWith('archive-me')
|
||||
expect(store.sessions).toEqual([])
|
||||
expect(store.activeSessionId).toBeNull()
|
||||
expect(store.activeSession).toBeNull()
|
||||
})
|
||||
it('keeps a failed initial session load retryable and restores sessions on retry', async () => {
|
||||
vi.mocked(fetchSessions).mockRejectedValue(new Error('backend starting'))
|
||||
const store = useChatStore()
|
||||
expect(await store.loadSessions()).toBe(false)
|
||||
expect(store.sessionsLoaded).toBe(false)
|
||||
vi.mocked(fetchSessions).mockResolvedValue([makeSession('restored', { started_at: 1000 })] as any)
|
||||
expect(await store.loadSessions()).toBe(true)
|
||||
expect(store.sessionsLoaded).toBe(true)
|
||||
expect(store.activeSessionId).toBe('restored')
|
||||
vi.mocked(fetchSessions).mockRejectedValue(new Error('restart'))
|
||||
expect(await store.loadSessions()).toBe(false)
|
||||
expect(store.sessions.map(session => session.id)).toEqual(['restored'])
|
||||
})
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user