cc-haha/adapters/common/__tests__/session-store.test.ts
程序员阿江(Relakkes) 978dfb2efb fix(desktop): repair Windows updater and installer flow (#801)
Prevent stale same-version update metadata from surfacing another install prompt, avoid showing a fake desktop version fallback, and configure the Windows NSIS installer to expose install directory selection.

Fixes #801

Tested: bun run verify
Confidence: high
Scope-risk: moderate
2026-06-12 10:39:13 +08:00

117 lines
3.9 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from 'bun:test'
import * as fs from 'node:fs'
import * as path from 'node:path'
import * as os from 'node:os'
import { SessionStore } from '../session-store.js'
describe('SessionStore', () => {
let tmpDir: string
let store: SessionStore
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'session-store-'))
store = new SessionStore(path.join(tmpDir, 'sessions.json'))
})
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true })
})
it('returns null for unknown chatId', () => {
expect(store.get('unknown')).toBeNull()
})
it('stores and retrieves a session', () => {
store.set('chat-1', 'uuid-aaa', '/path/to/project')
const entry = store.get('chat-1')
expect(entry).not.toBeNull()
expect(entry!.sessionId).toBe('uuid-aaa')
expect(entry!.workDir).toBe('/path/to/project')
})
it('overwrites existing entry on set', () => {
store.set('chat-1', 'uuid-aaa', '/old')
store.set('chat-1', 'uuid-bbb', '/new')
expect(store.get('chat-1')!.sessionId).toBe('uuid-bbb')
})
it('deletes an entry', () => {
store.set('chat-1', 'uuid-aaa', '/path')
store.delete('chat-1')
expect(store.get('chat-1')).toBeNull()
})
it('deletes every chat entry bound to a sessionId', () => {
store.set('chat-1', 'uuid-shared', '/project-a')
store.set('chat-2', 'uuid-other', '/project-b')
store.set('chat-3', 'uuid-shared', '/project-c')
const removed = store.deleteBySessionId('uuid-shared')
expect(removed.sort()).toEqual(['chat-1', 'chat-3'])
expect(store.get('chat-1')).toBeNull()
expect(store.get('chat-3')).toBeNull()
expect(store.get('chat-2')!.sessionId).toBe('uuid-other')
const reloaded = new SessionStore(path.join(tmpDir, 'sessions.json'))
expect(reloaded.get('chat-1')).toBeNull()
expect(reloaded.get('chat-3')).toBeNull()
expect(reloaded.get('chat-2')!.sessionId).toBe('uuid-other')
})
it('refreshes from disk before reading so running adapters do not reuse deleted mappings', () => {
store.set('chat-1', 'uuid-stale', '/project')
const serverSideStore = new SessionStore(path.join(tmpDir, 'sessions.json'))
expect(serverSideStore.deleteBySessionId('uuid-stale')).toEqual(['chat-1'])
expect(store.get('chat-1')).toBeNull()
expect(store.listAll()).toEqual([])
})
it('returns an empty list when deleting an unknown sessionId', () => {
store.set('chat-1', 'uuid-aaa', '/project')
expect(store.deleteBySessionId('uuid-missing')).toEqual([])
expect(store.get('chat-1')!.sessionId).toBe('uuid-aaa')
})
it('persists to disk and reloads', () => {
store.set('chat-1', 'uuid-aaa', '/path')
const store2 = new SessionStore(path.join(tmpDir, 'sessions.json'))
expect(store2.get('chat-1')!.sessionId).toBe('uuid-aaa')
})
it('handles missing file gracefully', () => {
const store2 = new SessionStore(path.join(tmpDir, 'nonexistent.json'))
expect(store2.get('anything')).toBeNull()
})
it('uses CLAUDE_CONFIG_DIR for the default session store path', () => {
const previousConfigDir = process.env.CLAUDE_CONFIG_DIR
process.env.CLAUDE_CONFIG_DIR = tmpDir
try {
const defaultStore = new SessionStore()
defaultStore.set('chat-default', 'uuid-default', '/default-project')
const savedPath = path.join(tmpDir, 'adapter-sessions.json')
expect(fs.existsSync(savedPath)).toBe(true)
expect(JSON.parse(fs.readFileSync(savedPath, 'utf8'))['chat-default'].sessionId).toBe('uuid-default')
} finally {
if (previousConfigDir === undefined) {
delete process.env.CLAUDE_CONFIG_DIR
} else {
process.env.CLAUDE_CONFIG_DIR = previousConfigDir
}
}
})
it('lists all entries', () => {
store.set('chat-1', 'uuid-1', '/a')
store.set('chat-2', 'uuid-2', '/b')
const all = store.listAll()
expect(all).toHaveLength(2)
})
})