test(web): API 契约测试 — 25 个模块 244 个测试全覆盖
验证每个 API 模块的 URL/HTTP Method/参数序列化: - health(14): patients/appointments/alerts/articles/consultations/ dashboard/deviceReadings/doctors/followUp/healthData/points/ followUpTemplates/api - 基础模块(11): auth/users/roles/orgs/dictionaries/messages/ plugins/pluginData/config-modules/workflow/auditLogs 前端测试总数: 140(store) + 244(api) = 384
This commit is contained in:
127
apps/web/src/api/ai/analysis.test.ts
Normal file
127
apps/web/src/api/ai/analysis.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* AI 模块 API 契约测试(analysis + prompts + suggestions + usage)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { analysisApi } from './analysis'
|
||||
import { promptApi } from './prompts'
|
||||
import { suggestionApi } from './suggestions'
|
||||
import { usageApi } from './usage'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('analysisApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('list 应调用 GET /ai/analysis/history 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await analysisApi.list({ patient_id: 'p-001', analysis_type: 'lab-report', page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/analysis/history', {
|
||||
params: { patient_id: 'p-001', analysis_type: 'lab-report', page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /ai/analysis/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await analysisApi.get('ana-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/analysis/ana-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('promptApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('list 应调用 GET /ai/prompts 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await promptApi.list({ category: 'analysis', page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/prompts', {
|
||||
params: { category: 'analysis', page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('create 应调用 POST /ai/prompts 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '化验解读', system_prompt: '你是专业医生', user_prompt_template: '解读: {report}', model_config: {}, category: 'analysis' }
|
||||
await promptApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/ai/prompts', req)
|
||||
})
|
||||
|
||||
it('activate 应调用 POST /ai/prompts/:id/activate', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await promptApi.activate('prompt-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/ai/prompts/prompt-001/activate')
|
||||
})
|
||||
|
||||
it('rollback 应调用 POST /ai/prompts/:id/rollback', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await promptApi.rollback('prompt-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/ai/prompts/prompt-001/rollback')
|
||||
})
|
||||
})
|
||||
|
||||
describe('suggestionApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('list 应调用 GET /ai/suggestions 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await suggestionApi.list({ analysis_id: 'ana-001', status: 'pending' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/suggestions', {
|
||||
params: { analysis_id: 'ana-001', status: 'pending' },
|
||||
})
|
||||
})
|
||||
|
||||
it('approve 应调用 POST /ai/suggestions/:id/approve 并传递 action', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await suggestionApi.approve('sug-001', 'approve')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/ai/suggestions/sug-001/approve', { action: 'approve' })
|
||||
})
|
||||
|
||||
it('getComparison 应调用 GET /ai/suggestions/:id/comparison', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await suggestionApi.getComparison('sug-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/suggestions/sug-001/comparison')
|
||||
})
|
||||
})
|
||||
|
||||
describe('usageApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('overview 应调用 GET /ai/usage/overview', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await usageApi.overview()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/usage/overview')
|
||||
})
|
||||
|
||||
it('byType 应调用 GET /ai/usage/by-type', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await usageApi.byType()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/ai/usage/by-type')
|
||||
})
|
||||
})
|
||||
51
apps/web/src/api/auditLogs.test.ts
Normal file
51
apps/web/src/api/auditLogs.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* auditLogs API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as auditLogsApi from './auditLogs'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('auditLogs API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listAuditLogs 应调用 GET /audit-logs 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await auditLogsApi.listAuditLogs({ resource_type: 'user', user_id: 'u-001', page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/audit-logs', {
|
||||
params: expect.objectContaining({
|
||||
resource_type: 'user',
|
||||
user_id: 'u-001',
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('listAuditLogs 默认应传 page=1 page_size=20', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await auditLogsApi.listAuditLogs()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/audit-logs', {
|
||||
params: expect.objectContaining({ page: 1, page_size: 20 }),
|
||||
})
|
||||
})
|
||||
})
|
||||
55
apps/web/src/api/auth.test.ts
Normal file
55
apps/web/src/api/auth.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* auth API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as authApi from './auth'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('auth API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('login 应调用 POST /auth/login 并传递用户名密码', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await authApi.login({ username: 'admin', password: '123456' })
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
})
|
||||
})
|
||||
|
||||
it('logout 应调用 POST /auth/logout', async () => {
|
||||
mockPost.mockResolvedValue(undefined)
|
||||
await authApi.logout()
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/auth/logout')
|
||||
})
|
||||
|
||||
it('changePassword 应调用 POST /auth/change-password', async () => {
|
||||
mockPost.mockResolvedValue(undefined)
|
||||
await authApi.changePassword('oldPass', 'newPass')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/auth/change-password', {
|
||||
current_password: 'oldPass',
|
||||
new_password: 'newPass',
|
||||
})
|
||||
})
|
||||
})
|
||||
197
apps/web/src/api/config-modules.test.ts
Normal file
197
apps/web/src/api/config-modules.test.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* config-modules API 契约测试(menus + settings + languages + numberingRules + themes)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as menusApi from './menus'
|
||||
import * as settingsApi from './settings'
|
||||
import * as languagesApi from './languages'
|
||||
import * as numberingApi from './numberingRules'
|
||||
import * as themesApi from './themes'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// menus
|
||||
// ============================================================
|
||||
describe('menus API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('getMenus 应调用 GET /config/menus', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await menusApi.getMenus()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/menus')
|
||||
})
|
||||
|
||||
it('getMenusForUser 应调用 GET /menus/user', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await menusApi.getMenusForUser()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/menus/user')
|
||||
})
|
||||
|
||||
it('batchSaveMenus 应调用 PUT /config/menus 并传递 menus 数组', async () => {
|
||||
mockPut.mockResolvedValue(undefined)
|
||||
const menus = [{ title: '仪表盘', path: '/dashboard' }]
|
||||
await menusApi.batchSaveMenus(menus)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/menus', { menus })
|
||||
})
|
||||
|
||||
it('createMenu 应调用 POST /config/menus', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { title: '新菜单', path: '/new', sort_order: 10 }
|
||||
await menusApi.createMenu(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/config/menus', req)
|
||||
})
|
||||
|
||||
it('deleteMenu 应调用 DELETE /config/menus/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await menusApi.deleteMenu('menu-001', 3)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/config/menus/menu-001', { data: { version: 3 } })
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// settings
|
||||
// ============================================================
|
||||
describe('settings API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('getSetting 应调用 GET /config/settings/:key 并传递 scope 参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await settingsApi.getSetting('site.name', 'global', 'org-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/settings/site.name', {
|
||||
params: { scope: 'global', scope_id: 'org-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('updateSetting 应调用 PUT /config/settings/:key', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await settingsApi.updateSetting('site.name', '新名称', 1)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/settings/site.name', {
|
||||
setting_value: '新名称',
|
||||
version: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('deleteSetting 应调用 DELETE /config/settings/:key', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await settingsApi.deleteSetting('site.name', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/config/settings/site.name', { data: { version: 2 } })
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// languages
|
||||
// ============================================================
|
||||
describe('languages API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listLanguages 应调用 GET /config/languages', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await languagesApi.listLanguages()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/languages')
|
||||
})
|
||||
|
||||
it('updateLanguage 应调用 PUT /config/languages/:code', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await languagesApi.updateLanguage('zh-CN', { is_active: true, name: '简体中文' })
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/languages/zh-CN', {
|
||||
is_active: true,
|
||||
name: '简体中文',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// numberingRules
|
||||
// ============================================================
|
||||
describe('numberingRules API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listNumberingRules 应调用 GET /config/numbering-rules', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await numberingApi.listNumberingRules(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/numbering-rules', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createNumberingRule 应调用 POST /config/numbering-rules', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '患者编号', code: 'patient', prefix: 'P', seq_length: 6 }
|
||||
await numberingApi.createNumberingRule(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/config/numbering-rules', req)
|
||||
})
|
||||
|
||||
it('updateNumberingRule 应调用 PUT /config/numbering-rules/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { prefix: 'HMS', version: 1 }
|
||||
await numberingApi.updateNumberingRule('nr-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/numbering-rules/nr-001', req)
|
||||
})
|
||||
|
||||
it('generateNumber 应调用 POST /config/numbering-rules/:id/generate', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await numberingApi.generateNumber('nr-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/config/numbering-rules/nr-001/generate')
|
||||
})
|
||||
|
||||
it('deleteNumberingRule 应调用 DELETE /config/numbering-rules/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await numberingApi.deleteNumberingRule('nr-001', 1)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/config/numbering-rules/nr-001', { data: { version: 1 } })
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================
|
||||
// themes
|
||||
// ============================================================
|
||||
describe('themes API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('getTheme 应调用 GET /config/themes', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await themesApi.getTheme()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/themes')
|
||||
})
|
||||
|
||||
it('updateTheme 应调用 PUT /config/themes', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const theme = { primary_color: '#1890ff', brand_name: 'HMS' }
|
||||
await themesApi.updateTheme(theme)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/themes', theme)
|
||||
})
|
||||
})
|
||||
96
apps/web/src/api/dictionaries.test.ts
Normal file
96
apps/web/src/api/dictionaries.test.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* dictionaries API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as dictApi from './dictionaries'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('dictionaries API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listDictionaries 应调用 GET /config/dictionaries 并传递分页参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dictApi.listDictionaries(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/dictionaries', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createDictionary 应调用 POST /config/dictionaries', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '性别', code: 'gender', description: '性别字典' }
|
||||
await dictApi.createDictionary(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/config/dictionaries', req)
|
||||
})
|
||||
|
||||
it('updateDictionary 应调用 PUT /config/dictionaries/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '性别(更新)', version: 1 }
|
||||
await dictApi.updateDictionary('dict-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/dictionaries/dict-001', req)
|
||||
})
|
||||
|
||||
it('deleteDictionary 应调用 DELETE /config/dictionaries/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await dictApi.deleteDictionary('dict-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/config/dictionaries/dict-001', {
|
||||
data: { version: 2 },
|
||||
})
|
||||
})
|
||||
|
||||
it('listItemsByCode 应调用 GET /config/dictionaries/items 并传递 code 参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dictApi.listItemsByCode('gender')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/config/dictionaries/items', {
|
||||
params: { code: 'gender' },
|
||||
})
|
||||
})
|
||||
|
||||
it('createDictionaryItem 应调用 POST /config/dictionaries/:id/items', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { label: '男', value: 'male', sort_order: 1 }
|
||||
await dictApi.createDictionaryItem('dict-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/config/dictionaries/dict-001/items', req)
|
||||
})
|
||||
|
||||
it('updateDictionaryItem 应调用 PUT /config/dictionaries/:dictId/items/:itemId', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { label: '女', version: 1 }
|
||||
await dictApi.updateDictionaryItem('dict-001', 'item-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/config/dictionaries/dict-001/items/item-001', req)
|
||||
})
|
||||
|
||||
it('deleteDictionaryItem 应调用 DELETE /config/dictionaries/:dictId/items/:itemId', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await dictApi.deleteDictionaryItem('dict-001', 'item-001', 1)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/config/dictionaries/dict-001/items/item-001', {
|
||||
data: { version: 1 },
|
||||
})
|
||||
})
|
||||
})
|
||||
100
apps/web/src/api/health/alerts.test.ts
Normal file
100
apps/web/src/api/health/alerts.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* alerts API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { alertApi, alertRuleApi } from './alerts'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('alertApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/alerts 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await alertApi.list({ patient_id: 'p-001', status: 'active', page: 1, page_size: 20 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/alerts', {
|
||||
params: { patient_id: 'p-001', status: 'active', page: 1, page_size: 20 },
|
||||
})
|
||||
})
|
||||
|
||||
it('acknowledge 应调用 PUT /health/alerts/:id/acknowledge 并传递 version', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await alertApi.acknowledge('a-001', 2)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/acknowledge', { version: 2 })
|
||||
})
|
||||
|
||||
it('dismiss 应调用 PUT /health/alerts/:id/dismiss', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await alertApi.dismiss('a-001', 1)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/dismiss', { version: 1 })
|
||||
})
|
||||
|
||||
it('resolve 应调用 PUT /health/alerts/:id/resolve', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await alertApi.resolve('a-001', 3)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/resolve', { version: 3 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('alertRuleApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/alert-rules 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await alertRuleApi.list({ device_type: 'blood_pressure', page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/alert-rules', {
|
||||
params: { device_type: 'blood_pressure', page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/alert-rules 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = {
|
||||
name: '血压偏高告警',
|
||||
device_type: 'blood_pressure',
|
||||
condition_type: 'threshold',
|
||||
condition_params: { field: 'systolic', operator: '>', value: 140 },
|
||||
severity: 'high',
|
||||
}
|
||||
await alertRuleApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/alert-rules', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/alert-rules/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { severity: 'critical', version: 1 }
|
||||
await alertRuleApi.update('rule-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/alert-rules/rule-001', req)
|
||||
})
|
||||
|
||||
it('deactivate 应调用 PUT /health/alert-rules/:id/deactivate', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await alertRuleApi.deactivate('rule-001', 2)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/alert-rules/rule-001/deactivate', { version: 2 })
|
||||
})
|
||||
})
|
||||
106
apps/web/src/api/health/appointments.test.ts
Normal file
106
apps/web/src/api/health/appointments.test.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* appointments API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { appointmentApi } from './appointments'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('appointmentApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/appointments 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await appointmentApi.list({ page: 1, page_size: 20, status: 'confirmed', doctor_id: 'd-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/appointments', {
|
||||
params: { page: 1, page_size: 20, status: 'confirmed', doctor_id: 'd-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /health/appointments/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await appointmentApi.get('appt-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/appointments/appt-001')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/appointments 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = {
|
||||
patient_id: 'p-001',
|
||||
doctor_id: 'd-001',
|
||||
appointment_date: '2026-05-10',
|
||||
start_time: '09:00',
|
||||
end_time: '09:30',
|
||||
}
|
||||
await appointmentApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/appointments', req)
|
||||
})
|
||||
|
||||
it('updateStatus 应调用 PUT /health/appointments/:id/status', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { status: 'cancelled', cancel_reason: '时间冲突', version: 2 }
|
||||
await appointmentApi.updateStatus('appt-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/appointments/appt-001/status', req)
|
||||
})
|
||||
|
||||
it('listSchedules 应调用 GET /health/doctor-schedules 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await appointmentApi.listSchedules({ doctor_id: 'd-001', date: '2026-05-10' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/doctor-schedules', {
|
||||
params: { doctor_id: 'd-001', date: '2026-05-10' },
|
||||
})
|
||||
})
|
||||
|
||||
it('createSchedule 应调用 POST /health/doctor-schedules', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = {
|
||||
doctor_id: 'd-001',
|
||||
schedule_date: '2026-05-10',
|
||||
start_time: '08:00',
|
||||
end_time: '12:00',
|
||||
max_appointments: 10,
|
||||
}
|
||||
await appointmentApi.createSchedule(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/doctor-schedules', req)
|
||||
})
|
||||
|
||||
it('updateSchedule 应调用 PUT /health/doctor-schedules/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { max_appointments: 15, version: 1 }
|
||||
await appointmentApi.updateSchedule('sch-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/doctor-schedules/sch-001', req)
|
||||
})
|
||||
|
||||
it('calendar 应调用 GET /health/doctor-schedules/calendar', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await appointmentApi.calendar({ start_date: '2026-05-01', end_date: '2026-05-31', doctor_id: 'd-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/doctor-schedules/calendar', {
|
||||
params: { start_date: '2026-05-01', end_date: '2026-05-31', doctor_id: 'd-001' },
|
||||
})
|
||||
})
|
||||
})
|
||||
173
apps/web/src/api/health/articles.test.ts
Normal file
173
apps/web/src/api/health/articles.test.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* articles API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { articleApi, articleCategoryApi, articleTagApi } from './articles'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('articleApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/articles 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await articleApi.list({ page: 1, page_size: 10, status: 'published', category_id: 'cat-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/articles', {
|
||||
params: { page: 1, page_size: 10, status: 'published', category_id: 'cat-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /health/articles/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await articleApi.get('art-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/articles/art-001')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/articles', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { title: '健康饮食指南', content: '正文内容', content_type: 'markdown' }
|
||||
await articleApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/articles/:id 并传递请求体', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { title: '健康饮食指南(修订)', version: 1 }
|
||||
await articleApi.update('art-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/articles/art-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/articles/:id', async () => {
|
||||
mockDelete.mockResolvedValue({ data: { success: true, data: null } })
|
||||
await articleApi.delete('art-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/articles/art-001')
|
||||
})
|
||||
|
||||
it('submit 应调用 POST /health/articles/:id/submit 并传递 version', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await articleApi.submit('art-001', 2)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/submit', { version: 2 })
|
||||
})
|
||||
|
||||
it('approve 应调用 POST /health/articles/:id/approve', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await articleApi.approve('art-001', 2)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/approve', { version: 2 })
|
||||
})
|
||||
|
||||
it('reject 应调用 POST /health/articles/:id/reject 并传递 review_note', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await articleApi.reject('art-001', 2, '内容需要修改')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/reject', {
|
||||
version: 2,
|
||||
review_note: '内容需要修改',
|
||||
})
|
||||
})
|
||||
|
||||
it('unpublish 应调用 POST /health/articles/:id/unpublish', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await articleApi.unpublish('art-001', 3)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/unpublish', { version: 3 })
|
||||
})
|
||||
|
||||
it('view 应调用 POST /health/articles/:id/view', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await articleApi.view('art-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/view')
|
||||
})
|
||||
})
|
||||
|
||||
describe('articleCategoryApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/article-categories', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await articleCategoryApi.list()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/article-categories')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/article-categories', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '营养健康', sort_order: 1 }
|
||||
await articleCategoryApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/article-categories', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/article-categories/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '营养健康(更新)' }
|
||||
await articleCategoryApi.update('cat-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/article-categories/cat-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/article-categories/:id', async () => {
|
||||
mockDelete.mockResolvedValue({ data: { success: true, data: null } })
|
||||
await articleCategoryApi.delete('cat-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/article-categories/cat-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('articleTagApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/article-tags', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await articleTagApi.list()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/article-tags')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/article-tags', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '高血压', color: '#ff0000' }
|
||||
await articleTagApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/article-tags', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/article-tags/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '高血压管理', version: 1 }
|
||||
await articleTagApi.update('tag-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/article-tags/tag-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/article-tags/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue({ data: { success: true, data: null } })
|
||||
await articleTagApi.delete('tag-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/article-tags/tag-001', { data: { version: 2 } })
|
||||
})
|
||||
})
|
||||
76
apps/web/src/api/health/consultations.test.ts
Normal file
76
apps/web/src/api/health/consultations.test.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* consultations API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { consultationApi } from './consultations'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('consultationApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listSessions 应调用 GET /health/consultation-sessions 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await consultationApi.listSessions({ page: 1, page_size: 20, status: 'active', patient_id: 'p-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions', {
|
||||
params: { page: 1, page_size: 20, status: 'active', patient_id: 'p-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('createSession 应调用 POST /health/consultation-sessions', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { patient_id: 'p-001', doctor_id: 'd-001', consultation_type: 'online' }
|
||||
await consultationApi.createSession(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/consultation-sessions', req)
|
||||
})
|
||||
|
||||
it('getSession 应调用 GET /health/consultation-sessions/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await consultationApi.getSession('sess-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions/sess-001')
|
||||
})
|
||||
|
||||
it('closeSession 应调用 PUT /health/consultation-sessions/:id/close', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
await consultationApi.closeSession('sess-001', { version: 1 })
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/close', { version: 1 })
|
||||
})
|
||||
|
||||
it('listMessages 应调用 GET /health/consultation-sessions/:id/messages 并传递分页参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await consultationApi.listMessages('sess-001', { page: 2, page_size: 50, after_id: 'msg-100' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/messages', {
|
||||
params: { page: 2, page_size: 50, after_id: 'msg-100' },
|
||||
})
|
||||
})
|
||||
|
||||
it('createMessage 应调用 POST /health/consultation-messages', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { session_id: 'sess-001', content_type: 'text', content: '你好' }
|
||||
await consultationApi.createMessage(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/consultation-messages', req)
|
||||
})
|
||||
})
|
||||
105
apps/web/src/api/health/dashboard.test.ts
Normal file
105
apps/web/src/api/health/dashboard.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* dashboard + actionInbox API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { dashboardApi } from './dashboard'
|
||||
import { actionInboxApi } from './actionInbox'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('dashboardApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('getSystemHealth 应调用 GET /health/admin/system-health', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dashboardApi.getSystemHealth()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/system-health')
|
||||
})
|
||||
|
||||
it('getUserActivity 应调用 GET /health/admin/user-activity', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dashboardApi.getUserActivity()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/user-activity')
|
||||
})
|
||||
|
||||
it('getModuleStatus 应调用 GET /health/admin/modules', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dashboardApi.getModuleStatus()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/modules')
|
||||
})
|
||||
|
||||
it('getPointsRecentActivity 应调用 GET /health/points/recent-activity', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dashboardApi.getPointsRecentActivity()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/points/recent-activity')
|
||||
})
|
||||
|
||||
it('getArticleStats 应调用 GET /health/articles/stats', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await dashboardApi.getArticleStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/articles/stats')
|
||||
})
|
||||
})
|
||||
|
||||
describe('actionInboxApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/action-inbox 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await actionInboxApi.list({ status: 'pending', type: 'alert', page: 1, page_size: 20 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/action-inbox', {
|
||||
params: { status: 'pending', type: 'alert', page: 1, page_size: 20 },
|
||||
})
|
||||
})
|
||||
|
||||
it('getThread 应调用 GET /health/action-inbox/:sourceRef/thread', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await actionInboxApi.getThread('ref-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/ref-001/thread')
|
||||
})
|
||||
|
||||
it('getThread 应对特殊字符 URL 编码', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await actionInboxApi.getThread('ref/with:special')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/ref%2Fwith%3Aspecial/thread')
|
||||
})
|
||||
|
||||
it('stats 应调用 GET /health/action-inbox/stats', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await actionInboxApi.stats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/stats')
|
||||
})
|
||||
|
||||
it('team 应调用 GET /health/action-inbox/team', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await actionInboxApi.team()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/team')
|
||||
})
|
||||
})
|
||||
82
apps/web/src/api/health/deviceReadings.test.ts
Normal file
82
apps/web/src/api/health/deviceReadings.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* deviceReadings + devices API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { deviceReadingApi } from './deviceReadings'
|
||||
import { deviceApi } from './devices'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('deviceReadingApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('batchCreate 应调用 POST /health/patients/:id/device-readings/batch', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const data = {
|
||||
device_id: 'dev-001',
|
||||
readings: [
|
||||
{ device_type: 'blood_pressure', values: { systolic: 130, diastolic: 85 }, measured_at: '2026-05-03T08:00:00Z' },
|
||||
],
|
||||
}
|
||||
await deviceReadingApi.batchCreate('p-001', data)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/device-readings/batch', data)
|
||||
})
|
||||
|
||||
it('query 应调用 GET /health/patients/:id/device-readings 并剥离 patient_id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await deviceReadingApi.query({ patient_id: 'p-001', device_type: 'blood_pressure', hours: 24 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/device-readings', {
|
||||
params: { device_type: 'blood_pressure', hours: 24 },
|
||||
})
|
||||
})
|
||||
|
||||
it('queryHourly 应调用 GET /health/patients/:id/device-readings/hourly', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await deviceReadingApi.queryHourly({ patient_id: 'p-001', device_type: 'blood_pressure', days: 7 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/device-readings/hourly', {
|
||||
params: { device_type: 'blood_pressure', days: 7 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('deviceApi', () => {
|
||||
const fakeRes = { data: { data: {} } }
|
||||
|
||||
it('listDevices 应调用 GET /health/devices 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await deviceApi.listDevices({ patient_id: 'p-001', device_type: 'blood_pressure', page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/devices', {
|
||||
params: { patient_id: 'p-001', device_type: 'blood_pressure', page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('unbindDevice 应调用 DELETE /health/devices/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(fakeRes)
|
||||
await deviceApi.unbindDevice('dev-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/devices/dev-001', {
|
||||
data: { version: 2 },
|
||||
})
|
||||
})
|
||||
})
|
||||
67
apps/web/src/api/health/doctors.test.ts
Normal file
67
apps/web/src/api/health/doctors.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* doctors API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { doctorApi } from './doctors'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('doctorApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/doctors 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await doctorApi.list({ page: 1, page_size: 10, search: '王', department: '内科' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/doctors', {
|
||||
params: { page: 1, page_size: 10, search: '王', department: '内科' },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /health/doctors/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await doctorApi.get('d-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/doctors/d-001')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/doctors 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '王医生', department: '内科', title: '主任医师' }
|
||||
await doctorApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/doctors', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/doctors/:id 并传递请求体含 version', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { title: '副主任医师', version: 1 }
|
||||
await doctorApi.update('d-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/doctors/d-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/doctors/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await doctorApi.delete('d-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/doctors/d-001')
|
||||
})
|
||||
})
|
||||
97
apps/web/src/api/health/followUp.test.ts
Normal file
97
apps/web/src/api/health/followUp.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* followUp API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { followUpApi } from './followUp'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('followUpApi - Tasks', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listTasks 应调用 GET /health/follow-up-tasks 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await followUpApi.listTasks({ page: 1, page_size: 20, patient_id: 'p-001', status: 'pending' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/follow-up-tasks', {
|
||||
params: { page: 1, page_size: 20, patient_id: 'p-001', status: 'pending' },
|
||||
})
|
||||
})
|
||||
|
||||
it('getTask 应调用 GET /health/follow-up-tasks/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await followUpApi.getTask('task-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/follow-up-tasks/task-001')
|
||||
})
|
||||
|
||||
it('createTask 应调用 POST /health/follow-up-tasks', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { patient_id: 'p-001', follow_up_type: 'phone', planned_date: '2026-05-10' }
|
||||
await followUpApi.createTask(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/follow-up-tasks', req)
|
||||
})
|
||||
|
||||
it('updateTask 应调用 PUT /health/follow-up-tasks/:id 并传递 version', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { status: 'completed', version: 1 }
|
||||
await followUpApi.updateTask('task-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/follow-up-tasks/task-001', req)
|
||||
})
|
||||
|
||||
it('deleteTask 应调用 DELETE /health/follow-up-tasks/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await followUpApi.deleteTask('task-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/follow-up-tasks/task-001', {
|
||||
data: { version: 2 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('followUpApi - Records', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listRecords 应调用 GET /health/follow-up-records 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await followUpApi.listRecords({ page: 1, page_size: 10, task_id: 'task-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/follow-up-records', {
|
||||
params: { page: 1, page_size: 10, task_id: 'task-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('createRecord 应调用 POST /health/follow-up-tasks/:taskId/records 并注入 task_id', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = {
|
||||
executed_date: '2026-05-10',
|
||||
result: '已完成',
|
||||
patient_condition: '良好',
|
||||
}
|
||||
await followUpApi.createRecord('task-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/follow-up-tasks/task-001/records', {
|
||||
...req,
|
||||
task_id: 'task-001',
|
||||
})
|
||||
})
|
||||
})
|
||||
75
apps/web/src/api/health/followUpTemplates.test.ts
Normal file
75
apps/web/src/api/health/followUpTemplates.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* followUpTemplates API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { followUpTemplateApi } from './followUpTemplates'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('followUpTemplateApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/follow-up-templates 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await followUpTemplateApi.list({ page: 1, page_size: 10, follow_up_type: 'phone', status: 'active' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/follow-up-templates', {
|
||||
params: { page: 1, page_size: 10, follow_up_type: 'phone', status: 'active' },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /health/follow-up-templates/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await followUpTemplateApi.get('tpl-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/follow-up-templates 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = {
|
||||
name: '电话随访模板',
|
||||
follow_up_type: 'phone',
|
||||
fields: [
|
||||
{ label: '患者状态', field_key: 'patient_status', field_type: 'select', required: true, options: '良好,一般,较差' },
|
||||
],
|
||||
}
|
||||
await followUpTemplateApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/follow-up-templates', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/follow-up-templates/:id 并传递请求体', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '更新后模板', status: 'active', version: 1 }
|
||||
await followUpTemplateApi.update('tpl-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/follow-up-templates/:id 并在 body 传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await followUpTemplateApi.delete('tpl-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001', {
|
||||
data: { version: 2 },
|
||||
})
|
||||
})
|
||||
})
|
||||
135
apps/web/src/api/health/healthData.test.ts
Normal file
135
apps/web/src/api/health/healthData.test.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* healthData API 契约测试(体征/化验报告/健康记录/趋势/日常监测)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { healthDataApi } from './healthData'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('healthDataApi - Vital Signs', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listVitalSigns 应调用 GET /health/patients/:id/vital-signs 并传递分页', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await healthDataApi.listVitalSigns('p-001', { page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/vital-signs', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createVitalSigns 应调用 POST /health/patients/:id/vital-signs', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { record_date: '2026-05-03', systolic_bp_morning: 120, diastolic_bp_morning: 80 }
|
||||
await healthDataApi.createVitalSigns('p-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/vital-signs', req)
|
||||
})
|
||||
|
||||
it('updateVitalSigns 应调用 PUT /health/patients/:pid/vital-signs/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { systolic_bp_morning: 125, version: 1 }
|
||||
await healthDataApi.updateVitalSigns('p-001', 'vs-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/vital-signs/vs-001', req)
|
||||
})
|
||||
|
||||
it('deleteVitalSigns 应调用 DELETE /health/patients/:pid/vital-signs/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await healthDataApi.deleteVitalSigns('p-001', 'vs-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/vital-signs/vs-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('healthDataApi - Lab Reports', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listLabReports 应调用 GET /health/patients/:id/lab-reports', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await healthDataApi.listLabReports('p-001', { page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/lab-reports', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createLabReport 应调用 POST /health/patients/:id/lab-reports', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { report_date: '2026-05-03', report_type: 'blood_test' }
|
||||
await healthDataApi.createLabReport('p-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/lab-reports', req)
|
||||
})
|
||||
|
||||
it('reviewLabReport 应调用 PUT /health/patients/:pid/lab-reports/:rid/review', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { version: 1, doctor_notes: '指标正常' }
|
||||
await healthDataApi.reviewLabReport('p-001', 'lr-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/lab-reports/lr-001/review', req)
|
||||
})
|
||||
|
||||
it('deleteLabReport 应调用 DELETE /health/patients/:pid/lab-reports/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await healthDataApi.deleteLabReport('p-001', 'lr-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/lab-reports/lr-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('healthDataApi - Health Records', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listHealthRecords 应调用 GET /health/patients/:id/health-records', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await healthDataApi.listHealthRecords('p-001', { page: 1, page_size: 10 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/health-records', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createHealthRecord 应调用 POST /health/patients/:id/health-records', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { record_type: 'checkup', record_date: '2026-05-03', content: '体检结果正常' }
|
||||
await healthDataApi.createHealthRecord('p-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/health-records', req)
|
||||
})
|
||||
})
|
||||
|
||||
describe('healthDataApi - Trends', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listTrends 应调用 GET /health/patients/:id/trends', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await healthDataApi.listTrends('p-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/trends')
|
||||
})
|
||||
|
||||
it('getIndicatorTimeseries 应调用 GET /health/patients/:id/trends/:indicator 并编码', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await healthDataApi.getIndicatorTimeseries('p-001', 'blood_pressure/systolic')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/trends/blood_pressure%2Fsystolic')
|
||||
})
|
||||
})
|
||||
126
apps/web/src/api/health/patients.test.ts
Normal file
126
apps/web/src/api/health/patients.test.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* patients API 契约测试
|
||||
*
|
||||
* 验证 patientApi 各函数调用正确的 HTTP 方法、URL 路径和参数序列化。
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { patientApi } from './patients'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('patientApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('list 应调用 GET /health/patients 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await patientApi.list({ page: 1, page_size: 20, search: '张三', status: 'active' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients', {
|
||||
params: { page: 1, page_size: 20, search: '张三', status: 'active' },
|
||||
})
|
||||
})
|
||||
|
||||
it('list 应支持 tag_id 过滤参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await patientApi.list({ tag_id: 'tag-001' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients', {
|
||||
params: { tag_id: 'tag-001' },
|
||||
})
|
||||
})
|
||||
|
||||
it('get 应调用 GET /health/patients/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await patientApi.get('p-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001')
|
||||
})
|
||||
|
||||
it('create 应调用 POST /health/patients 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '李四', gender: 'male', birth_date: '1990-01-01' }
|
||||
await patientApi.create(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients', req)
|
||||
})
|
||||
|
||||
it('update 应调用 PUT /health/patients/:id 并传递请求体含 version', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '李四改', version: 2 }
|
||||
await patientApi.update('p-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001', req)
|
||||
})
|
||||
|
||||
it('delete 应调用 DELETE /health/patients/:id 并在 body 中传递 version', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await patientApi.delete('p-001', 3)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001', {
|
||||
data: { version: 3 },
|
||||
})
|
||||
})
|
||||
|
||||
it('manageTags 应调用 POST /health/patients/:id/tags 并传递 tag_ids', async () => {
|
||||
mockPost.mockResolvedValue(undefined)
|
||||
await patientApi.manageTags('p-001', ['tag-1', 'tag-2'])
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/tags', {
|
||||
tag_ids: ['tag-1', 'tag-2'],
|
||||
})
|
||||
})
|
||||
|
||||
it('listFamilyMembers 应调用 GET /health/patients/:id/family-members', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await patientApi.listFamilyMembers('p-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/family-members')
|
||||
})
|
||||
|
||||
it('createFamilyMember 应调用 POST /health/patients/:id/family-members', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '家属A', relationship: 'spouse', phone: '13800138000' }
|
||||
await patientApi.createFamilyMember('p-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/family-members', req)
|
||||
})
|
||||
|
||||
it('updateFamilyMember 应调用 PUT /health/patients/:pid/family-members/:mid', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '家属A改', version: 1 }
|
||||
await patientApi.updateFamilyMember('p-001', 'fm-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/family-members/fm-001', req)
|
||||
})
|
||||
|
||||
it('deleteFamilyMember 应调用 DELETE /health/patients/:pid/family-members/:mid', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await patientApi.deleteFamilyMember('p-001', 'fm-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/family-members/fm-001')
|
||||
})
|
||||
|
||||
it('listTags 应调用 GET /health/patient-tags', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await patientApi.listTags()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/patient-tags')
|
||||
})
|
||||
})
|
||||
230
apps/web/src/api/health/points.test.ts
Normal file
230
apps/web/src/api/health/points.test.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* points API 契约测试(完整覆盖 pointsApi + pointsAdminApi)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('../client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import { pointsApi, pointsAdminApi } from './points'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('pointsAdminApi', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('getPatientAccount 应调用 GET /health/admin/points/patients/:id/account', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsAdminApi.getPatientAccount('p-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/points/patients/p-001/account')
|
||||
})
|
||||
|
||||
it('listPatientTransactions 应调用 GET 并传递分页参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsAdminApi.listPatientTransactions('p-001', { page: 2, page_size: 15 })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/points/patients/p-001/transactions', {
|
||||
params: { page: 2, page_size: 15 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointsApi - Rules', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listRules 应调用 GET /health/admin/points/rules', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.listRules()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/points/rules')
|
||||
})
|
||||
|
||||
it('createRule 应调用 POST /health/admin/points/rules', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { event_type: 'daily_checkin', name: '每日签到', points_value: 10, daily_cap: 1 }
|
||||
await pointsApi.createRule(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/admin/points/rules', req)
|
||||
})
|
||||
|
||||
it('updateRule 应调用 PUT /health/admin/points/rules/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { points_value: 20, version: 1 }
|
||||
await pointsApi.updateRule('rule-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/admin/points/rules/rule-001', {
|
||||
data: req,
|
||||
version: req.version,
|
||||
})
|
||||
})
|
||||
|
||||
it('deleteRule 应调用 DELETE /health/admin/points/rules/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await pointsApi.deleteRule('rule-001', 2)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/admin/points/rules/rule-001', {
|
||||
data: { version: 2 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointsApi - Products', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listProducts 应调用 GET /health/points/products', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.listProducts()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/points/products', { params: undefined })
|
||||
})
|
||||
|
||||
it('createProduct 应调用 POST /health/admin/points/products', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '体检优惠券', product_type: 'service', points_cost: 500, stock: 100 }
|
||||
await pointsApi.createProduct(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/admin/points/products', req)
|
||||
})
|
||||
|
||||
it('updateProduct 应调用 PUT /health/admin/points/products/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { points_cost: 600, version: 1 }
|
||||
await pointsApi.updateProduct('prod-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/admin/points/products/prod-001', {
|
||||
data: req,
|
||||
version: req.version,
|
||||
})
|
||||
})
|
||||
|
||||
it('deleteProduct 应调用 DELETE /health/admin/points/products/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await pointsApi.deleteProduct('prod-001', 1)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/admin/points/products/prod-001', {
|
||||
data: { version: 1 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointsApi - Orders', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listOrders 应调用 GET /health/admin/points/orders', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.listOrders()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/points/orders', { params: undefined })
|
||||
})
|
||||
|
||||
it('verifyOrder 应调用 POST /health/points/verify', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { qr_code: 'QR-123456' }
|
||||
await pointsApi.verifyOrder(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/points/verify', req)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointsApi - Offline Events', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listOfflineEvents 应调用 GET /health/admin/offline-events', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.listOfflineEvents()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/offline-events', { params: undefined })
|
||||
})
|
||||
|
||||
it('createOfflineEvent 应调用 POST /health/admin/offline-events', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { title: '健康讲座', event_date: '2026-05-20', points_reward: 50 }
|
||||
await pointsApi.createOfflineEvent(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/health/admin/offline-events', req)
|
||||
})
|
||||
|
||||
it('updateOfflineEvent 应调用 PUT /health/admin/offline-events/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { title: '健康讲座(更新)', version: 1 }
|
||||
await pointsApi.updateOfflineEvent('evt-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/health/admin/offline-events/evt-001', req)
|
||||
})
|
||||
|
||||
it('deleteOfflineEvent 应调用 DELETE /health/admin/offline-events/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await pointsApi.deleteOfflineEvent('evt-001', 1)
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/health/admin/offline-events/evt-001', {
|
||||
data: { version: 1 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('pointsApi - Statistics', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('getStatistics 应调用 GET /health/admin/points/statistics', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getStatistics()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/points/statistics')
|
||||
})
|
||||
|
||||
it('getPatientStats 应调用 GET /health/admin/statistics/patients', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getPatientStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/patients')
|
||||
})
|
||||
|
||||
it('getConsultationStats 应调用 GET /health/admin/statistics/consultations', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getConsultationStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/consultations')
|
||||
})
|
||||
|
||||
it('getFollowUpStats 应调用 GET /health/admin/statistics/follow-ups', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getFollowUpStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/follow-ups')
|
||||
})
|
||||
|
||||
it('getHealthDataStats 应调用 GET /health/admin/statistics/health-data', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getHealthDataStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/health-data')
|
||||
})
|
||||
|
||||
it('getDialysisStats 应调用 GET /health/admin/statistics/dialysis', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getDialysisStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/dialysis')
|
||||
})
|
||||
|
||||
it('getPersonalStats 应调用 GET /health/admin/statistics/personal-stats', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pointsApi.getPersonalStats()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/personal-stats')
|
||||
})
|
||||
})
|
||||
100
apps/web/src/api/messages.test.ts
Normal file
100
apps/web/src/api/messages.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* messages + messageTemplates API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as messagesApi from './messages'
|
||||
import * as templateApi from './messageTemplates'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('messages API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listMessages 应调用 GET /messages 并传递查询参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await messagesApi.listMessages({ page: 2, page_size: 10, is_read: false, priority: 'high' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/messages', {
|
||||
params: expect.objectContaining({
|
||||
page: 2,
|
||||
page_size: 10,
|
||||
is_read: false,
|
||||
priority: 'high',
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('getUnreadCount 应调用 GET /messages/unread-count', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await messagesApi.getUnreadCount()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/messages/unread-count')
|
||||
})
|
||||
|
||||
it('markRead 应调用 PUT /messages/:id/read', async () => {
|
||||
mockPut.mockResolvedValue({ data: { success: true } })
|
||||
await messagesApi.markRead('msg-001')
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/messages/msg-001/read')
|
||||
})
|
||||
|
||||
it('markAllRead 应调用 PUT /messages/read-all', async () => {
|
||||
mockPut.mockResolvedValue({ data: { success: true } })
|
||||
await messagesApi.markAllRead()
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/messages/read-all')
|
||||
})
|
||||
|
||||
it('deleteMessage 应调用 DELETE /messages/:id', async () => {
|
||||
mockDelete.mockResolvedValue({ data: { success: true } })
|
||||
await messagesApi.deleteMessage('msg-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/messages/msg-001')
|
||||
})
|
||||
|
||||
it('sendMessage 应调用 POST /messages 并传递请求体', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { title: '通知', body: '内容', recipient_id: 'u-001' }
|
||||
await messagesApi.sendMessage(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/messages', req)
|
||||
})
|
||||
})
|
||||
|
||||
describe('messageTemplates API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listTemplates 应调用 GET /message-templates 并传递分页参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await templateApi.listTemplates(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/message-templates', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('createTemplate 应调用 POST /message-templates', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '预约提醒', code: 'appointment_reminder', title_template: '预约提醒', body_template: '您有预约' }
|
||||
await templateApi.createTemplate(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/message-templates', req)
|
||||
})
|
||||
})
|
||||
126
apps/web/src/api/orgs.test.ts
Normal file
126
apps/web/src/api/orgs.test.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* orgs API 契约测试(组织/部门/岗位)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as orgsApi from './orgs'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('organizations API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listOrgTree 应调用 GET /organizations', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await orgsApi.listOrgTree()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/organizations')
|
||||
})
|
||||
|
||||
it('createOrg 应调用 POST /organizations', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '总公司', code: 'HQ' }
|
||||
await orgsApi.createOrg(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/organizations', req)
|
||||
})
|
||||
|
||||
it('updateOrg 应调用 PUT /organizations/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '改名', version: 1 }
|
||||
await orgsApi.updateOrg('org-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/organizations/org-001', req)
|
||||
})
|
||||
|
||||
it('deleteOrg 应调用 DELETE /organizations/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await orgsApi.deleteOrg('org-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/organizations/org-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('departments API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listDeptTree 应调用 GET /organizations/:orgId/departments', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await orgsApi.listDeptTree('org-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/organizations/org-001/departments')
|
||||
})
|
||||
|
||||
it('createDept 应调用 POST /organizations/:orgId/departments', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '内科', code: 'NK' }
|
||||
await orgsApi.createDept('org-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/organizations/org-001/departments', req)
|
||||
})
|
||||
|
||||
it('updateDept 应调用 PUT /departments/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '内科(更新)', version: 1 }
|
||||
await orgsApi.updateDept('dept-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/departments/dept-001', req)
|
||||
})
|
||||
|
||||
it('deleteDept 应调用 DELETE /departments/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await orgsApi.deleteDept('dept-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/departments/dept-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('positions API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listPositions 应调用 GET /departments/:deptId/positions', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await orgsApi.listPositions('dept-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/departments/dept-001/positions')
|
||||
})
|
||||
|
||||
it('createPosition 应调用 POST /departments/:deptId/positions', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '主治医师', code: 'ZYS' }
|
||||
await orgsApi.createPosition('dept-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/departments/dept-001/positions', req)
|
||||
})
|
||||
|
||||
it('updatePosition 应调用 PUT /positions/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '主任医师', version: 1 }
|
||||
await orgsApi.updatePosition('pos-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/positions/pos-001', req)
|
||||
})
|
||||
|
||||
it('deletePosition 应调用 DELETE /positions/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await orgsApi.deletePosition('pos-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/positions/pos-001')
|
||||
})
|
||||
})
|
||||
131
apps/web/src/api/pluginData.test.ts
Normal file
131
apps/web/src/api/pluginData.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* pluginData API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockPatch = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
patch: (...args: unknown[]) => mockPatch(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as pluginDataApi from './pluginData'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('pluginData CRUD', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listPluginData 应调用 GET /plugins/:pid/:entity 并传递分页和过滤参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginDataApi.listPluginData('crm', 'customer', 1, 20, {
|
||||
filter: { status: 'active' },
|
||||
search: '张',
|
||||
sort_by: 'name',
|
||||
sort_order: 'asc',
|
||||
})
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer', {
|
||||
params: expect.objectContaining({
|
||||
page: '1',
|
||||
page_size: '20',
|
||||
search: '张',
|
||||
sort_by: 'name',
|
||||
sort_order: 'asc',
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('getPluginData 应调用 GET /plugins/:pid/:entity/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginDataApi.getPluginData('crm', 'customer', 'rec-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/rec-001')
|
||||
})
|
||||
|
||||
it('createPluginData 应调用 POST /plugins/:pid/:entity 并包裹 data', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const recordData = { name: '客户A', phone: '13800138000' }
|
||||
await pluginDataApi.createPluginData('crm', 'customer', recordData)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer', { data: recordData })
|
||||
})
|
||||
|
||||
it('updatePluginData 应调用 PUT /plugins/:pid/:entity/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const recordData = { name: '客户A(更新)' }
|
||||
await pluginDataApi.updatePluginData('crm', 'customer', 'rec-001', recordData, 2)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/plugins/crm/customer/rec-001', {
|
||||
data: recordData,
|
||||
version: 2,
|
||||
})
|
||||
})
|
||||
|
||||
it('deletePluginData 应调用 DELETE /plugins/:pid/:entity/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await pluginDataApi.deletePluginData('crm', 'customer', 'rec-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/plugins/crm/customer/rec-001')
|
||||
})
|
||||
})
|
||||
|
||||
describe('pluginData 高级查询', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('countPluginData 应调用 GET /plugins/:pid/:entity/count', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginDataApi.countPluginData('crm', 'customer', { filter: { status: 'active' } })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/count', {
|
||||
params: expect.objectContaining({
|
||||
filter: '{"status":"active"}',
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('aggregatePluginData 应调用 GET /plugins/:pid/:entity/aggregate', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginDataApi.aggregatePluginData('crm', 'customer', 'status')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/aggregate', {
|
||||
params: { group_by: 'status' },
|
||||
})
|
||||
})
|
||||
|
||||
it('batchPluginData 应调用 POST /plugins/:pid/:entity/batch', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { action: 'delete', ids: ['rec-1', 'rec-2'] }
|
||||
await pluginDataApi.batchPluginData('crm', 'customer', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/batch', req)
|
||||
})
|
||||
|
||||
it('resolveRefLabels 应调用 POST /plugins/:pid/:entity/resolve-labels', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const fields = { customer_tag_id: ['tag-1', 'tag-2'] }
|
||||
await pluginDataApi.resolveRefLabels('crm', 'customer', fields)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/resolve-labels', { fields })
|
||||
})
|
||||
|
||||
it('importPluginData 应调用 POST /plugins/:pid/:entity/import', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const rows = [{ name: '客户A' }, { name: '客户B' }]
|
||||
await pluginDataApi.importPluginData('crm', 'customer', rows)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/import', { rows })
|
||||
})
|
||||
})
|
||||
132
apps/web/src/api/plugins.test.ts
Normal file
132
apps/web/src/api/plugins.test.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* plugins API 契约测试(插件管理 + 市场)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as pluginsApi from './plugins'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('plugins management API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listPlugins 应调用 GET /admin/plugins 并传递分页和状态', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.listPlugins(1, 10, 'enabled')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/admin/plugins', {
|
||||
params: { page: 1, page_size: 10, status: 'enabled' },
|
||||
})
|
||||
})
|
||||
|
||||
it('getPlugin 应调用 GET /admin/plugins/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.getPlugin('plug-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001')
|
||||
})
|
||||
|
||||
it('installPlugin 应调用 POST /admin/plugins/:id/install', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.installPlugin('plug-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/install')
|
||||
})
|
||||
|
||||
it('enablePlugin 应调用 POST /admin/plugins/:id/enable', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.enablePlugin('plug-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/enable')
|
||||
})
|
||||
|
||||
it('disablePlugin 应调用 POST /admin/plugins/:id/disable', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.disablePlugin('plug-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/disable')
|
||||
})
|
||||
|
||||
it('purgePlugin 应调用 DELETE /admin/plugins/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await pluginsApi.purgePlugin('plug-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/admin/plugins/plug-001')
|
||||
})
|
||||
|
||||
it('getPluginHealth 应调用 GET /admin/plugins/:id/health', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.getPluginHealth('plug-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001/health')
|
||||
})
|
||||
|
||||
it('updatePluginConfig 应调用 PUT /admin/plugins/:id/config', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const config = { theme: 'dark' }
|
||||
await pluginsApi.updatePluginConfig('plug-001', config, 1)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/admin/plugins/plug-001/config', {
|
||||
config,
|
||||
version: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('getPluginSchema 应调用 GET /admin/plugins/:id/schema', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.getPluginSchema('plug-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001/schema')
|
||||
})
|
||||
})
|
||||
|
||||
describe('plugin market API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listMarketEntries 应调用 GET /market/entries', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.listMarketEntries({ page: 1, page_size: 10, category: 'crm' })
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/market/entries', {
|
||||
params: { page: 1, page_size: 10, category: 'crm' },
|
||||
})
|
||||
})
|
||||
|
||||
it('getMarketEntry 应调用 GET /market/entries/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.getMarketEntry('mkt-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/market/entries/mkt-001')
|
||||
})
|
||||
|
||||
it('installFromMarket 应调用 POST /market/entries/:id/install', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await pluginsApi.installFromMarket('mkt-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/market/entries/mkt-001/install')
|
||||
})
|
||||
|
||||
it('submitMarketReview 应调用 POST /market/entries/:id/reviews', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const review = { rating: 5, review_text: '很好用' }
|
||||
await pluginsApi.submitMarketReview('mkt-001', review)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/market/entries/mkt-001/reviews', review)
|
||||
})
|
||||
})
|
||||
86
apps/web/src/api/roles.test.ts
Normal file
86
apps/web/src/api/roles.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* roles API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as rolesApi from './roles'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('roles API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listRoles 应调用 GET /roles 并传递分页参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await rolesApi.listRoles(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/roles', { params: { page: 1, page_size: 10 } })
|
||||
})
|
||||
|
||||
it('getRole 应调用 GET /roles/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await rolesApi.getRole('r-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/roles/r-001')
|
||||
})
|
||||
|
||||
it('createRole 应调用 POST /roles', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '医生', code: 'doctor', description: '医生角色' }
|
||||
await rolesApi.createRole(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/roles', req)
|
||||
})
|
||||
|
||||
it('updateRole 应调用 PUT /roles/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { name: '高级医生', version: 1 }
|
||||
await rolesApi.updateRole('r-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/roles/r-001', req)
|
||||
})
|
||||
|
||||
it('deleteRole 应调用 DELETE /roles/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await rolesApi.deleteRole('r-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/roles/r-001')
|
||||
})
|
||||
|
||||
it('assignPermissions 应调用 POST /roles/:id/permissions', async () => {
|
||||
mockPost.mockResolvedValue(undefined)
|
||||
await rolesApi.assignPermissions('r-001', ['p-1', 'p-2'])
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/roles/r-001/permissions', { permission_ids: ['p-1', 'p-2'] })
|
||||
})
|
||||
|
||||
it('getRolePermissions 应调用 GET /roles/:id/permissions', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await rolesApi.getRolePermissions('r-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/roles/r-001/permissions')
|
||||
})
|
||||
|
||||
it('listPermissions 应调用 GET /permissions', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await rolesApi.listPermissions()
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/permissions')
|
||||
})
|
||||
})
|
||||
83
apps/web/src/api/users.test.ts
Normal file
83
apps/web/src/api/users.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* users API 契约测试
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as usersApi from './users'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('users API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listUsers 应调用 GET /users 并传递分页和搜索参数', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await usersApi.listUsers(2, 10, '张')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/users', {
|
||||
params: { page: 2, page_size: 10, search: '张' },
|
||||
})
|
||||
})
|
||||
|
||||
it('listUsers 空搜索时应传 search 为 undefined', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await usersApi.listUsers(1, 20, '')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/users', {
|
||||
params: { page: 1, page_size: 20, search: undefined },
|
||||
})
|
||||
})
|
||||
|
||||
it('getUser 应调用 GET /users/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await usersApi.getUser('u-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/users/u-001')
|
||||
})
|
||||
|
||||
it('createUser 应调用 POST /users', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { username: 'newuser', password: 'pass123', display_name: '新用户' }
|
||||
await usersApi.createUser(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/users', req)
|
||||
})
|
||||
|
||||
it('updateUser 应调用 PUT /users/:id', async () => {
|
||||
mockPut.mockResolvedValue(fakeRes)
|
||||
const req = { display_name: '改名', version: 1 }
|
||||
await usersApi.updateUser('u-001', req)
|
||||
|
||||
expect(mockPut).toHaveBeenCalledWith('/users/u-001', req)
|
||||
})
|
||||
|
||||
it('deleteUser 应调用 DELETE /users/:id', async () => {
|
||||
mockDelete.mockResolvedValue(undefined)
|
||||
await usersApi.deleteUser('u-001')
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith('/users/u-001')
|
||||
})
|
||||
|
||||
it('assignRoles 应调用 POST /users/:id/roles', async () => {
|
||||
mockPost.mockResolvedValue(undefined)
|
||||
await usersApi.assignRoles('u-001', ['role-1', 'role-2'])
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/users/u-001/roles', { role_ids: ['role-1', 'role-2'] })
|
||||
})
|
||||
})
|
||||
141
apps/web/src/api/workflow.test.ts
Normal file
141
apps/web/src/api/workflow.test.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* workflow API 契约测试(definitions + instances + tasks)
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
const mockPut = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
default: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
},
|
||||
}))
|
||||
|
||||
import * as defApi from './workflowDefinitions'
|
||||
import * as instApi from './workflowInstances'
|
||||
import * as taskApi from './workflowTasks'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('workflowDefinitions API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listProcessDefinitions 应调用 GET /workflow/definitions', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await defApi.listProcessDefinitions(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/workflow/definitions', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('getProcessDefinition 应调用 GET /workflow/definitions/:id', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await defApi.getProcessDefinition('wf-001')
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/workflow/definitions/wf-001')
|
||||
})
|
||||
|
||||
it('createProcessDefinition 应调用 POST /workflow/definitions', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { name: '审批流程', key: 'approval', nodes: [], edges: [] }
|
||||
await defApi.createProcessDefinition(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/definitions', req)
|
||||
})
|
||||
|
||||
it('publishProcessDefinition 应调用 POST /workflow/definitions/:id/publish', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await defApi.publishProcessDefinition('wf-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/definitions/wf-001/publish')
|
||||
})
|
||||
})
|
||||
|
||||
describe('workflowInstances API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('startInstance 应调用 POST /workflow/instances', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { definition_id: 'wf-001', business_key: 'BIZ-001' }
|
||||
await instApi.startInstance(req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/instances', req)
|
||||
})
|
||||
|
||||
it('listInstances 应调用 GET /workflow/instances 并传递分页', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await instApi.listInstances(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/workflow/instances', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('suspendInstance 应调用 POST /workflow/instances/:id/suspend', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await instApi.suspendInstance('inst-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/suspend')
|
||||
})
|
||||
|
||||
it('resumeInstance 应调用 POST /workflow/instances/:id/resume', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await instApi.resumeInstance('inst-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/resume')
|
||||
})
|
||||
|
||||
it('terminateInstance 应调用 POST /workflow/instances/:id/terminate', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
await instApi.terminateInstance('inst-001')
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/terminate')
|
||||
})
|
||||
})
|
||||
|
||||
describe('workflowTasks API', () => {
|
||||
const fakeRes = { data: { success: true, data: {} } }
|
||||
|
||||
it('listPendingTasks 应调用 GET /workflow/tasks/pending', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await taskApi.listPendingTasks(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/workflow/tasks/pending', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('listCompletedTasks 应调用 GET /workflow/tasks/completed', async () => {
|
||||
mockGet.mockResolvedValue(fakeRes)
|
||||
await taskApi.listCompletedTasks(1, 10)
|
||||
|
||||
expect(mockGet).toHaveBeenCalledWith('/workflow/tasks/completed', {
|
||||
params: { page: 1, page_size: 10 },
|
||||
})
|
||||
})
|
||||
|
||||
it('completeTask 应调用 POST /workflow/tasks/:id/complete', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { outcome: 'approved', form_data: { comment: '同意' } }
|
||||
await taskApi.completeTask('task-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/tasks/task-001/complete', req)
|
||||
})
|
||||
|
||||
it('delegateTask 应调用 POST /workflow/tasks/:id/delegate', async () => {
|
||||
mockPost.mockResolvedValue(fakeRes)
|
||||
const req = { delegate_to: 'u-002' }
|
||||
await taskApi.delegateTask('task-001', req)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledWith('/workflow/tasks/task-001/delegate', req)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user