diff --git a/docs/en/references/data/README.md b/docs/en/references/data/README.md index cd247160c5..abccd93508 100644 --- a/docs/en/references/data/README.md +++ b/docs/en/references/data/README.md @@ -21,6 +21,9 @@ This is the main entry point for Cherry Studio's data management documentation. - [API Types](./api-types.md) - API type system, schemas, error handling - [V2 Migration Guide](./v2-migration-guide.md) - Migration system +### Testing +- [Test Mocks](../../../../tests/__mocks__/README.md) - Unified mocks for Cache, Preference, and DataApi + --- ## Choosing the Right System @@ -191,3 +194,4 @@ const { data: files } = useQuery('/files') - `src/renderer/src/data/CacheService.ts` - Cache service - `src/renderer/src/data/PreferenceService.ts` - Preference service - `src/renderer/src/data/hooks/` - React hooks + diff --git a/tests/__mocks__/README.md b/tests/__mocks__/README.md index 89ac4d348a..789cbf0cea 100644 --- a/tests/__mocks__/README.md +++ b/tests/__mocks__/README.md @@ -1,608 +1,323 @@ # Test Mocks -这个目录包含了项目中使用的统一测试模拟(mocks)。这些模拟按照进程类型组织,避免重名冲突,并在相应的测试设置文件中全局配置。 +Unified test mocks for the project, organized by process type and globally configured in test setup files. -## 🎯 统一模拟概述 +## Overview -### 已实现的统一模拟 +### Available Mocks -#### Renderer Process Mocks -- ✅ **PreferenceService** - 渲染进程偏好设置服务模拟 -- ✅ **DataApiService** - 渲染进程数据API服务模拟 -- ✅ **CacheService** - 渲染进程三层缓存服务模拟 -- ✅ **useDataApi hooks** - 数据API钩子模拟 (useQuery, useMutation, usePaginatedQuery, etc.) -- ✅ **usePreference hooks** - 偏好设置钩子模拟 (usePreference, useMultiplePreferences) -- ✅ **useCache hooks** - 缓存钩子模拟 (useCache, useSharedCache, usePersistCache) +| Process | Mock | Description | +|---------|------|-------------| +| Renderer | `CacheService` | Three-tier cache (memory/shared/persist) | +| Renderer | `DataApiService` | HTTP client for Data API | +| Renderer | `PreferenceService` | User preferences | +| Renderer | `useDataApi` | Data API hooks (useQuery, useMutation, etc.) | +| Renderer | `usePreference` | Preference hooks | +| Renderer | `useCache` | Cache hooks | +| Main | `CacheService` | Internal + shared cache | +| Main | `DataApiService` | API coordinator | +| Main | `PreferenceService` | Preference service | -#### Main Process Mocks -- ✅ **PreferenceService** - 主进程偏好设置服务模拟 -- ✅ **DataApiService** - 主进程数据API服务模拟 -- ✅ **CacheService** - 主进程缓存服务模拟 - -### 🌟 核心优势 - -- **进程分离**: 按照renderer/main分开组织,避免重名冲突 -- **自动应用**: 无需在每个测试文件中单独模拟 -- **完整API覆盖**: 实现了所有服务和钩子的完整API -- **类型安全**: 完全支持 TypeScript,保持与真实服务的类型兼容性 -- **现实行为**: 模拟提供现实的默认值和行为模式 -- **高度可定制**: 支持为特定测试定制行为 -- **测试工具**: 内置丰富的测试工具函数 - -### 📁 文件结构 +### File Structure ``` tests/__mocks__/ -├── README.md # 本文档 -├── renderer/ # 渲染进程模拟 -│ ├── PreferenceService.ts # 渲染进程偏好设置服务模拟 -│ ├── DataApiService.ts # 渲染进程数据API服务模拟 -│ ├── CacheService.ts # 渲染进程缓存服务模拟 -│ ├── useDataApi.ts # 数据API钩子模拟 -│ ├── usePreference.ts # 偏好设置钩子模拟 -│ └── useCache.ts # 缓存钩子模拟 -├── main/ # 主进程模拟 -│ ├── PreferenceService.ts # 主进程偏好设置服务模拟 -│ ├── DataApiService.ts # 主进程数据API服务模拟 -│ └── CacheService.ts # 主进程缓存服务模拟 -├── RendererLoggerService.ts # 渲染进程日志服务模拟 -└── MainLoggerService.ts # 主进程日志服务模拟 +├── renderer/ +│ ├── CacheService.ts +│ ├── DataApiService.ts +│ ├── PreferenceService.ts +│ ├── useDataApi.ts +│ ├── usePreference.ts +│ └── useCache.ts +├── main/ +│ ├── CacheService.ts +│ ├── DataApiService.ts +│ └── PreferenceService.ts +├── RendererLoggerService.ts +└── MainLoggerService.ts ``` -### 🔧 测试设置 - -#### Renderer Process Tests -在 `tests/renderer.setup.ts` 中配置了所有渲染进程模拟: - -```typescript -// 自动加载 renderer/ 目录下的模拟 -vi.mock('@data/PreferenceService', async () => { - const { MockPreferenceService } = await import('./__mocks__/renderer/PreferenceService') - return MockPreferenceService -}) -// ... 其他渲染进程模拟 -``` - -#### Main Process Tests -在 `tests/main.setup.ts` 中配置了所有主进程模拟: - -```typescript -// 自动加载 main/ 目录下的模拟 -vi.mock('@main/data/PreferenceService', async () => { - const { MockMainPreferenceServiceExport } = await import('./__mocks__/main/PreferenceService') - return MockMainPreferenceServiceExport -}) -// ... 其他主进程模拟 -``` - -## PreferenceService Mock - -### 简介 - -`PreferenceService.ts` 提供了 PreferenceService 的统一模拟实现,用于所有渲染进程测试。这个模拟: - -- ✅ **自动应用**:在 `renderer.setup.ts` 中全局配置,无需在每个测试文件中单独模拟 -- ✅ **完整API**:实现了 PreferenceService 的所有方法(get, getMultiple, set, etc.) -- ✅ **合理默认值**:提供了常用偏好设置的默认值 -- ✅ **可定制**:支持为特定测试定制默认值 -- ✅ **类型安全**:完全支持 TypeScript 类型检查 - -### 默认值 - -模拟提供了以下默认偏好设置: - -```typescript -// 导出偏好设置 -'data.export.markdown.force_dollar_math': false -'data.export.markdown.exclude_citations': false -'data.export.markdown.standardize_citations': true -'data.export.markdown.show_model_name': false -'data.export.markdown.show_model_provider': false - -// UI偏好设置 -'ui.language': 'en' -'ui.theme': 'light' -'ui.font_size': 14 - -// AI偏好设置 -'ai.default_model': 'gpt-4' -'ai.temperature': 0.7 -'ai.max_tokens': 2000 - -// 功能开关 -'feature.web_search': true -'feature.reasoning': false -'feature.tool_calling': true -``` - -### 基本使用 - -由于模拟已经全局配置,大多数测试可以直接使用 PreferenceService,无需额外设置: - -```typescript -import { preferenceService } from '@data/PreferenceService' - -describe('MyComponent', () => { - it('should use preference values', async () => { - // PreferenceService 已经被自动模拟 - const value = await preferenceService.get('ui.theme') - expect(value).toBe('light') // 使用默认值 - }) -}) -``` - -### 高级使用 - -#### 1. 修改单个测试的偏好值 - -```typescript -import { preferenceService } from '@data/PreferenceService' -import { vi } from 'vitest' - -describe('Custom preferences', () => { - it('should work with custom preference values', async () => { - // 为这个测试修改特定值 - ;(preferenceService.get as any).mockImplementation((key: string) => { - if (key === 'ui.theme') return Promise.resolve('dark') - // 其他键使用默认模拟行为 - return vi.fn().mockResolvedValue(null)() - }) - - const theme = await preferenceService.get('ui.theme') - expect(theme).toBe('dark') - }) -}) -``` - -#### 2. 重置模拟状态 - -```typescript -import { preferenceService } from '@data/PreferenceService' - -describe('Mock state management', () => { - beforeEach(() => { - // 重置模拟到初始状态 - if ('_resetMockState' in preferenceService) { - ;(preferenceService as any)._resetMockState() - } - }) -}) -``` - -#### 3. 检查模拟内部状态 - -```typescript -import { preferenceService } from '@data/PreferenceService' - -describe('Mock inspection', () => { - it('should allow inspecting mock state', () => { - // 查看当前模拟状态 - if ('_getMockState' in preferenceService) { - const state = (preferenceService as any)._getMockState() - console.log('Current mock state:', state) - } - }) -}) -``` - -#### 4. 为整个测试套件定制默认值 - -如果需要为特定的测试文件定制默认值,可以在该文件中重新模拟: - -```typescript -import { vi } from 'vitest' - -// 重写全局模拟,添加自定义默认值 -vi.mock('@data/PreferenceService', async () => { - const { createMockPreferenceService } = await import('tests/__mocks__/PreferenceService') - - // 定制默认值 - const customDefaults = { - 'my.custom.setting': 'custom_value', - 'ui.theme': 'dark' // 覆盖默认值 - } - - return { - preferenceService: createMockPreferenceService(customDefaults) - } -}) -``` - -### 测试验证 - -可以验证 PreferenceService 方法是否被正确调用: - -```typescript -import { preferenceService } from '@data/PreferenceService' -import { vi } from 'vitest' - -describe('Preference service calls', () => { - it('should call preference service methods', async () => { - await preferenceService.get('ui.theme') - - // 验证方法调用 - expect(preferenceService.get).toHaveBeenCalledWith('ui.theme') - expect(preferenceService.get).toHaveBeenCalledTimes(1) - }) -}) -``` - -### 添加新的默认值 - -当项目中添加新的偏好设置时,请在 `PreferenceService.ts` 的 `mockPreferenceDefaults` 中添加相应的默认值: - -```typescript -export const mockPreferenceDefaults: Record = { - // 现有默认值... - - // 新增默认值 - 'new.feature.enabled': true, - 'new.feature.config': { option: 'value' } -} -``` - -这样可以确保所有测试都能使用合理的默认值,减少测试失败的可能性。 - -## DataApiService Mock - -### 简介 - -`DataApiService.ts` 提供了数据API服务的统一模拟,支持所有HTTP方法和高级功能。 - -### 功能特性 - -- **完整HTTP支持**: GET, POST, PUT, PATCH, DELETE -- **订阅系统**: subscribe/unsubscribe 模拟 -- **连接管理**: connect/disconnect/ping 方法 -- **智能模拟数据**: 基于路径自动生成合理的响应 - -### 基本使用 - -```typescript -import { dataApiService } from '@data/DataApiService' - -describe('API Integration', () => { - it('should fetch topics', async () => { - // 自动模拟,返回预设的主题列表 - const response = await dataApiService.get('/api/topics') - expect(response.success).toBe(true) - expect(response.data.topics).toHaveLength(2) - }) -}) -``` - -### 高级使用 - -```typescript -import { MockDataApiUtils } from 'tests/__mocks__/DataApiService' - -describe('Custom API behavior', () => { - beforeEach(() => { - MockDataApiUtils.resetMocks() - }) - - it('should handle custom responses', async () => { - // 设置特定路径的自定义响应 - MockDataApiUtils.setCustomResponse('/api/topics', 'GET', { - topics: [{ id: 'custom', name: 'Custom Topic' }] - }) - - const response = await dataApiService.get('/api/topics') - expect(response.data.topics[0].name).toBe('Custom Topic') - }) - - it('should simulate errors', async () => { - // 模拟错误响应 - MockDataApiUtils.setErrorResponse('/api/topics', 'GET', 'Network error') - - const response = await dataApiService.get('/api/topics') - expect(response.success).toBe(false) - expect(response.error?.message).toBe('Network error') - }) -}) -``` - -## CacheService Mock - -### 简介 - -`CacheService.ts` 提供了三层缓存系统的完整模拟:内存缓存、共享缓存和持久化缓存。 - -### 功能特性 - -- **三层架构**: 内存、共享、持久化缓存 -- **订阅系统**: 支持缓存变更订阅 -- **TTL支持**: 模拟缓存过期(简化版) -- **Hook引用跟踪**: 模拟生产环境的引用管理 -- **默认值**: 基于缓存schema的智能默认值 - -### 基本使用 +### Test Setup + +Mocks are globally configured in setup files: +- **Renderer**: `tests/renderer.setup.ts` +- **Main**: `tests/main.setup.ts` + +--- + +## Renderer Mocks + +### CacheService + +Three-tier cache system with type-safe and casual (dynamic key) methods. + +#### Methods + +| Category | Method | Signature | +|----------|--------|-----------| +| Memory (typed) | `get` | `(key: K) => UseCacheSchema[K]` | +| Memory (typed) | `set` | `(key: K, value, ttl?) => void` | +| Memory (typed) | `has` | `(key: K) => boolean` | +| Memory (typed) | `delete` | `(key: K) => boolean` | +| Memory (typed) | `hasTTL` | `(key: K) => boolean` | +| Memory (casual) | `getCasual` | `(key: string) => T \| undefined` | +| Memory (casual) | `setCasual` | `(key, value, ttl?) => void` | +| Memory (casual) | `hasCasual` | `(key: string) => boolean` | +| Memory (casual) | `deleteCasual` | `(key: string) => boolean` | +| Memory (casual) | `hasTTLCasual` | `(key: string) => boolean` | +| Shared (typed) | `getShared` | `(key: K) => SharedCacheSchema[K]` | +| Shared (typed) | `setShared` | `(key: K, value, ttl?) => void` | +| Shared (typed) | `hasShared` | `(key: K) => boolean` | +| Shared (typed) | `deleteShared` | `(key: K) => boolean` | +| Shared (typed) | `hasSharedTTL` | `(key: K) => boolean` | +| Shared (casual) | `getSharedCasual` | `(key: string) => T \| undefined` | +| Shared (casual) | `setSharedCasual` | `(key, value, ttl?) => void` | +| Shared (casual) | `hasSharedCasual` | `(key: string) => boolean` | +| Shared (casual) | `deleteSharedCasual` | `(key: string) => boolean` | +| Shared (casual) | `hasSharedTTLCasual` | `(key: string) => boolean` | +| Persist | `getPersist` | `(key: K) => RendererPersistCacheSchema[K]` | +| Persist | `setPersist` | `(key: K, value) => void` | +| Persist | `hasPersist` | `(key) => boolean` | +| Hook mgmt | `registerHook` | `(key: string) => void` | +| Hook mgmt | `unregisterHook` | `(key: string) => void` | +| Ready state | `isSharedCacheReady` | `() => boolean` | +| Ready state | `onSharedCacheReady` | `(callback) => () => void` | +| Lifecycle | `subscribe` | `(key, callback) => () => void` | +| Lifecycle | `cleanup` | `() => void` | + +#### Usage ```typescript import { cacheService } from '@data/CacheService' +import { MockCacheUtils } from 'tests/__mocks__/renderer/CacheService' -describe('Cache Operations', () => { - it('should store and retrieve cache values', () => { - // 设置缓存值 - cacheService.set('user.preferences', { theme: 'dark' }) +describe('Cache', () => { + beforeEach(() => MockCacheUtils.resetMocks()) - // 获取缓存值 - const preferences = cacheService.get('user.preferences') - expect(preferences.theme).toBe('dark') + it('basic usage', () => { + cacheService.setCasual('key', { data: 'value' }, 5000) + expect(cacheService.getCasual('key')).toEqual({ data: 'value' }) }) - it('should work with persist cache', () => { - // 持久化缓存操作 - cacheService.setPersist('app.last_opened_topic', 'topic123') - const lastTopic = cacheService.getPersist('app.last_opened_topic') - expect(lastTopic).toBe('topic123') + it('with test utilities', () => { + MockCacheUtils.setInitialState({ + memory: [['key', 'value']], + shared: [['shared.key', 'shared']], + persist: [['persist.key', 'persist']] + }) }) }) ``` -### 高级测试工具 +--- + +### DataApiService + +HTTP client with subscriptions and retry configuration. + +#### Methods + +| Method | Signature | +|--------|-----------| +| `get` | `(path, options?) => Promise` | +| `post` | `(path, options) => Promise` | +| `put` | `(path, options) => Promise` | +| `patch` | `(path, options) => Promise` | +| `delete` | `(path, options?) => Promise` | +| `subscribe` | `(options, callback) => () => void` | +| `configureRetry` | `(options) => void` | +| `getRetryConfig` | `() => RetryOptions` | +| `getRequestStats` | `() => { pendingRequests, activeSubscriptions }` | + +#### Usage ```typescript -import { MockCacheUtils } from 'tests/__mocks__/CacheService' +import { dataApiService } from '@data/DataApiService' +import { MockDataApiUtils } from 'tests/__mocks__/renderer/DataApiService' -describe('Advanced cache testing', () => { - beforeEach(() => { - MockCacheUtils.resetMocks() +describe('API', () => { + beforeEach(() => MockDataApiUtils.resetMocks()) + + it('basic request', async () => { + const response = await dataApiService.get('/topics') + expect(response.topics).toBeDefined() }) - it('should set initial cache state', () => { - // 设置初始缓存状态 - MockCacheUtils.setInitialState({ - memory: [['theme', 'dark'], ['language', 'en']], - persist: [['app.version', '1.0.0']] - }) - - expect(cacheService.get('theme')).toBe('dark') - expect(cacheService.getPersist('app.version')).toBe('1.0.0') + it('custom response', async () => { + MockDataApiUtils.setCustomResponse('/topics', 'GET', { custom: true }) + const response = await dataApiService.get('/topics') + expect(response.custom).toBe(true) }) - it('should simulate cache changes', () => { - let changeCount = 0 - cacheService.subscribe('theme', () => changeCount++) - - MockCacheUtils.triggerCacheChange('theme', 'light') - expect(changeCount).toBe(1) + it('error simulation', async () => { + MockDataApiUtils.setErrorResponse('/topics', 'GET', new Error('Failed')) + await expect(dataApiService.get('/topics')).rejects.toThrow('Failed') }) }) ``` -## useDataApi Hooks Mock +--- -### 简介 +### useDataApi Hooks -`useDataApi.ts` 提供了所有数据API钩子的统一模拟,包括查询、变更和分页功能。 +React hooks for data operations. -### 支持的钩子 +#### Hooks -- `useQuery` - 数据查询钩子 -- `useMutation` - 数据变更钩子 -- `usePaginatedQuery` - 分页查询钩子 -- `useInvalidateCache` - 缓存失效钩子 -- `prefetch` - 预取函数 +| Hook | Signature | Returns | +|------|-----------|---------| +| `useQuery` | `(path, options?)` | `{ data, loading, error, refetch, mutate }` | +| `useMutation` | `(method, path, options?)` | `{ mutate, loading, error }` | +| `usePaginatedQuery` | `(path, options?)` | `{ items, total, page, loading, error, hasMore, hasPrev, prevPage, nextPage, refresh, reset }` | +| `useInvalidateCache` | `()` | `(keys?) => Promise` | -### 基本使用 +#### Usage ```typescript import { useQuery, useMutation } from '@data/hooks/useDataApi' +import { MockUseDataApiUtils } from 'tests/__mocks__/renderer/useDataApi' -describe('Data API Hooks', () => { - it('should work with useQuery', () => { - const { data, isLoading, error } = useQuery('/api/topics') +describe('Hooks', () => { + beforeEach(() => MockUseDataApiUtils.resetMocks()) - // 默认返回模拟数据 + it('useQuery', () => { + const { data, loading } = useQuery('/topics') + expect(loading).toBe(false) expect(data).toBeDefined() - expect(data.topics).toHaveLength(2) - expect(isLoading).toBe(false) - expect(error).toBeUndefined() }) - it('should work with useMutation', async () => { - const { trigger, isMutating } = useMutation('/api/topics', 'POST') - - const result = await trigger({ name: 'New Topic' }) + it('useMutation', async () => { + const { mutate } = useMutation('POST', '/topics') + const result = await mutate({ body: { name: 'New' } }) expect(result.created).toBe(true) - expect(result.name).toBe('New Topic') + }) + + it('custom data', () => { + MockUseDataApiUtils.mockQueryData('/topics', { custom: true }) + const { data } = useQuery('/topics') + expect(data.custom).toBe(true) }) }) ``` -### 自定义测试行为 +--- + +### useCache Hooks + +React hooks for cache operations. + +| Hook | Signature | Returns | +|------|-----------|---------| +| `useCache` | `(key, initValue?)` | `[value, setValue]` | +| `useSharedCache` | `(key, initValue?)` | `[value, setValue]` | +| `usePersistCache` | `(key)` | `[value, setValue]` | ```typescript -import { MockUseDataApiUtils } from 'tests/__mocks__/useDataApi' +import { useCache } from '@data/hooks/useCache' -describe('Custom hook behavior', () => { - beforeEach(() => { - MockUseDataApiUtils.resetMocks() - }) - - it('should mock loading state', () => { - MockUseDataApiUtils.mockQueryLoading('/api/topics') - - const { data, isLoading } = useQuery('/api/topics') - expect(isLoading).toBe(true) - expect(data).toBeUndefined() - }) - - it('should mock error state', () => { - const error = new Error('API Error') - MockUseDataApiUtils.mockQueryError('/api/topics', error) - - const { data, error: queryError } = useQuery('/api/topics') - expect(queryError).toBe(error) - expect(data).toBeUndefined() - }) -}) +const [value, setValue] = useCache('key', 'default') +setValue('new value') ``` -## usePreference Hooks Mock +--- -### 简介 +### usePreference Hooks -`usePreference.ts` 提供了偏好设置钩子的统一模拟,支持单个和批量偏好管理。 +React hooks for preferences. -### 支持的钩子 - -- `usePreference` - 单个偏好设置钩子 -- `useMultiplePreferences` - 多个偏好设置钩子 - -### 基本使用 +| Hook | Signature | Returns | +|------|-----------|---------| +| `usePreference` | `(key)` | `[value, setValue]` | +| `useMultiplePreferences` | `(keyMap)` | `[values, setValues]` | ```typescript -import { usePreference, useMultiplePreferences } from '@data/hooks/usePreference' +import { usePreference } from '@data/hooks/usePreference' -describe('Preference Hooks', () => { - it('should work with usePreference', async () => { - const [theme, setTheme] = usePreference('ui.theme') - - expect(theme).toBe('light') // 默认值 - - await setTheme('dark') - // 在测试中,可以通过工具函数验证值是否更新 - }) - - it('should work with multiple preferences', async () => { - const [prefs, setPrefs] = useMultiplePreferences({ - theme: 'ui.theme', - lang: 'ui.language' - }) - - expect(prefs.theme).toBe('light') - expect(prefs.lang).toBe('en') - - await setPrefs({ theme: 'dark' }) - }) -}) +const [theme, setTheme] = usePreference('ui.theme') +await setTheme('dark') ``` -### 高级测试 +--- + +## Main Process Mocks + +### Main CacheService + +Internal cache and cross-window shared cache. + +#### Methods + +| Category | Method | Signature | +|----------|--------|-----------| +| Lifecycle | `initialize` | `() => Promise` | +| Lifecycle | `cleanup` | `() => void` | +| Internal | `get` | `(key: string) => T \| undefined` | +| Internal | `set` | `(key, value, ttl?) => void` | +| Internal | `has` | `(key: string) => boolean` | +| Internal | `delete` | `(key: string) => boolean` | +| Shared | `getShared` | `(key: K) => SharedCacheSchema[K] \| undefined` | +| Shared | `setShared` | `(key: K, value, ttl?) => void` | +| Shared | `hasShared` | `(key: K) => boolean` | +| Shared | `deleteShared` | `(key: K) => boolean` | ```typescript -import { MockUsePreferenceUtils } from 'tests/__mocks__/usePreference' +import { MockMainCacheServiceUtils } from 'tests/__mocks__/main/CacheService' -describe('Advanced preference testing', () => { - beforeEach(() => { - MockUsePreferenceUtils.resetMocks() - }) +beforeEach(() => MockMainCacheServiceUtils.resetMocks()) - it('should simulate preference changes', () => { - MockUsePreferenceUtils.setPreferenceValue('ui.theme', 'dark') - - const [theme] = usePreference('ui.theme') - expect(theme).toBe('dark') - }) - - it('should simulate external changes', () => { - let callCount = 0 - MockUsePreferenceUtils.addSubscriber('ui.theme', () => callCount++) - - MockUsePreferenceUtils.simulateExternalPreferenceChange('ui.theme', 'dark') - expect(callCount).toBe(1) - }) -}) +MockMainCacheServiceUtils.setCacheValue('key', 'value') +MockMainCacheServiceUtils.setSharedCacheValue('shared.key', 'shared') ``` -## useCache Hooks Mock +--- -### 简介 +### Main DataApiService -`useCache.ts` 提供了缓存钩子的统一模拟,支持三种缓存层级。 +API coordinator managing ApiServer and IpcAdapter. -### 支持的钩子 - -- `useCache` - 内存缓存钩子 -- `useSharedCache` - 共享缓存钩子 -- `usePersistCache` - 持久化缓存钩子 - -### 基本使用 +| Method | Signature | +|--------|-----------| +| `initialize` | `() => Promise` | +| `shutdown` | `() => Promise` | +| `getSystemStatus` | `() => object` | +| `getApiServer` | `() => ApiServer` | ```typescript -import { useCache, useSharedCache, usePersistCache } from '@data/hooks/useCache' +import { MockMainDataApiServiceUtils } from 'tests/__mocks__/main/DataApiService' -describe('Cache Hooks', () => { - it('should work with useCache', () => { - const [theme, setTheme] = useCache('ui.theme', 'light') +beforeEach(() => MockMainDataApiServiceUtils.resetMocks()) - expect(theme).toBe('light') - setTheme('dark') - // 值立即更新 - }) - - it('should work with different cache types', () => { - const [shared, setShared] = useSharedCache('app.window_count', 1) - const [persist, setPersist] = usePersistCache('app.last_version', '1.0.0') - - expect(shared).toBe(1) - expect(persist).toBe('1.0.0') - }) -}) +MockMainDataApiServiceUtils.simulateInitializationError(new Error('Failed')) ``` -### 测试工具 +--- -```typescript -import { MockUseCacheUtils } from 'tests/__mocks__/useCache' +## Utility Functions -describe('Cache hook testing', () => { - beforeEach(() => { - MockUseCacheUtils.resetMocks() - }) +Each mock exports a `MockXxxUtils` object with testing utilities: - it('should set initial cache state', () => { - MockUseCacheUtils.setMultipleCacheValues({ - memory: [['ui.theme', 'dark']], - shared: [['app.mode', 'development']], - persist: [['user.id', 'user123']] - }) +| Utility | Description | +|---------|-------------| +| `resetMocks()` | Reset all mock state and call counts | +| `setXxxValue()` | Set specific values for testing | +| `getXxxValue()` | Get current mock values | +| `simulateXxx()` | Simulate specific scenarios (errors, expiration, etc.) | +| `getMockCallCounts()` | Get call counts for debugging | - const [theme] = useCache('ui.theme') - const [mode] = useSharedCache('app.mode') - const [userId] = usePersistCache('user.id') +--- - expect(theme).toBe('dark') - expect(mode).toBe('development') - expect(userId).toBe('user123') - }) -}) -``` +## Best Practices -## LoggerService Mock +1. **Use global mocks** - Don't re-mock in individual tests unless necessary +2. **Reset in beforeEach** - Call `MockXxxUtils.resetMocks()` to ensure test isolation +3. **Use utility functions** - Prefer `MockXxxUtils` over direct mock manipulation +4. **Type safety** - Mocks match actual service interfaces -### 简介 +## Troubleshooting -项目还包含了 LoggerService 的模拟: -- `RendererLoggerService.ts` - 渲染进程日志服务模拟 -- `MainLoggerService.ts` - 主进程日志服务模拟 - -这些模拟同样在相应的测试设置文件中全局配置。 - -## 最佳实践 - -1. **优先使用全局模拟**:大多数情况下应该直接使用全局配置的模拟,而不是在每个测试中单独模拟 -2. **合理的默认值**:确保模拟的默认值反映实际应用的常见配置 -3. **文档更新**:当添加新的模拟或修改现有模拟时,请更新相关文档 -4. **类型安全**:保持模拟与实际服务的类型兼容性 -5. **测试隔离**:如果需要修改模拟行为,确保在测试后恢复或在 beforeEach 中重置 - -## 故障排除 - -### 模拟未生效 - -如果发现 PreferenceService 模拟未生效: - -1. 确认测试运行在渲染进程环境中(`vitest.config.ts` 中的 `renderer` 项目) -2. 检查 `tests/renderer.setup.ts` 是否正确配置 -3. 确认导入路径使用的是 `@data/PreferenceService` 而非相对路径 - -### 类型错误 - -如果遇到 TypeScript 类型错误: - -1. 确认模拟实现与实际 PreferenceService 接口匹配 -2. 在测试中使用类型断言:`(preferenceService as any)._getMockState()` -3. 检查是否需要更新模拟的类型定义 \ No newline at end of file +| Issue | Solution | +|-------|----------| +| Mock not applied | Check test runs in correct process (renderer/main in vitest.config.ts) | +| Type errors | Ensure mock matches actual interface, use type assertions if needed | +| State pollution | Call `resetMocks()` in `beforeEach` | +| Import issues | Use path aliases (`@data/CacheService`) not relative paths |