Files
knowledge-governance/web/src/qaOrgSync.test.ts
T

73 lines
3.1 KiB
TypeScript

import { describe, expect, it, vi, afterEach } from 'vitest'
import { parseDirectorySync, startDirectorySync, fetchDirectorySync, completedSyncAction } from './qaOrgSync'
afterEach(() => vi.unstubAllGlobals())
const idle = { configured: true, worker_ready: true, job: null }
describe('directory synchronization contract', () => {
it('reloads a completed job even on first observation, without replacing an unsaved draft', () => {
const status = parseDirectorySync({
...idle,
job: {
id: 'new-job',
state: 'completed',
attempt: 1,
max_attempts: 3,
updated_at: '2026-10-17T08:00:00Z',
error_code: '',
result: { directory_id: 'd', departments: 1, people: 1, synced_at: '2026-10-17T08:00:00Z' },
},
})
expect(completedSyncAction(status, '', false)).toBe('reload')
expect(completedSyncAction(status, '', true)).toBe('preserve')
expect(completedSyncAction(status, 'new-job', false)).toBe('none')
expect(completedSyncAction({ ...status, job: { ...status.job!, state: 'running' } }, '', false)).toBe('none')
})
it('keeps unknown/failed state distinct from empty data', () => {
expect(parseDirectorySync(idle).job).toBeNull()
expect(() => parseDirectorySync({ ...idle, worker_ready: 'yes' })).toThrow()
expect(() => parseDirectorySync({ ...idle, job: { id: 'x', state: 'mystery' } })).toThrow()
const result = { directory_id: 'd', departments: 327, people: 1534, synced_at: '2026-10-17T07:00:00Z' }
const job = {
id: 'j',
state: 'completed',
attempt: 1,
max_attempts: 3,
updated_at: '2026-10-17T07:01:00Z',
error_code: '',
result,
}
expect(parseDirectorySync({ ...idle, job }).job?.result?.people).toBe(1534)
expect(() => parseDirectorySync({ ...idle, job: { ...job, result: { ...result, people: -1 } } })).toThrow()
})
it('sends a stable idempotency header and no tenant, credential or executable path', async () => {
const fetcher = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ data: { job_id: 'j', deduplicated: false }, operation: 'replayed' }),
})
vi.stubGlobal('fetch', fetcher)
expect((await startDirectorySync('same-action')).operation).toBe('replayed')
expect(fetcher).toHaveBeenCalledWith(
'/api/v1/qa-ops/organization/sync',
expect.objectContaining({
method: 'POST',
credentials: 'same-origin',
body: '{}',
headers: expect.objectContaining({
'Idempotency-Key': 'same-action',
'X-Governance-Action': 'qa-organization',
}),
})
)
})
it('reads live job state and surfaces server failure instead of a successful empty result', async () => {
vi.stubGlobal(
'fetch',
vi
.fn()
.mockResolvedValueOnce({ ok: true, json: async () => ({ data: idle }) })
.mockResolvedValueOnce({ ok: false, status: 503, json: async () => ({ error: '执行器未在线' }) })
)
expect((await fetchDirectorySync()).worker_ready).toBe(true)
await expect(startDirectorySync('same-action')).rejects.toThrow('执行器未在线')
})
})