mirror of
https://github.com/langgenius/dify.git
synced 2026-07-28 07:28:34 +08:00
119 lines
3.9 KiB
TypeScript
119 lines
3.9 KiB
TypeScript
import type {
|
|
DocumentProcessingTask,
|
|
LogicalDocument,
|
|
} from '@dify/contracts/knowledge-fs/types.gen'
|
|
import {
|
|
documentDisplayStatus,
|
|
newestTaskByDocument,
|
|
taskCanRetry,
|
|
taskIsActive,
|
|
taskNeedsAttention,
|
|
taskVersionIsAfter,
|
|
} from '../document-model'
|
|
|
|
const document = (overrides: Partial<LogicalDocument> = {}): LogicalDocument => ({
|
|
active: {
|
|
contentHash: 'hash',
|
|
createdAt: '2026-07-20T10:00:00Z',
|
|
documentAssetId: 'asset-1',
|
|
documentAssetVersion: 1,
|
|
documentId: 'document-1',
|
|
knowledgeSpaceId: 'space-1',
|
|
mimeType: 'text/plain',
|
|
revision: 2,
|
|
sizeBytes: 10,
|
|
state: 'active',
|
|
},
|
|
activeRevision: 2,
|
|
createdAt: '2026-07-20T10:00:00Z',
|
|
id: 'document-1',
|
|
knowledgeSpaceId: 'space-1',
|
|
rowVersion: 1,
|
|
status: 'ready',
|
|
title: 'Document',
|
|
updatedAt: '2026-07-20T10:00:00Z',
|
|
userMetadata: {},
|
|
...overrides,
|
|
})
|
|
|
|
const task = (overrides: Partial<DocumentProcessingTask> = {}): DocumentProcessingTask => ({
|
|
createdAt: '2026-07-20T10:00:00Z',
|
|
documentId: 'document-1',
|
|
documentRevision: 3,
|
|
id: 'task-1',
|
|
knowledgeSpaceId: 'space-1',
|
|
progressPercent: 10,
|
|
stage: 'queued',
|
|
state: 'queued',
|
|
updatedAt: '2026-07-20T10:00:00Z',
|
|
...overrides,
|
|
})
|
|
|
|
describe('new Knowledge document model', () => {
|
|
it('chooses the highest revision before a more recently updated stale task', () => {
|
|
const result = newestTaskByDocument([
|
|
task({ documentRevision: 2, id: 'stale', updatedAt: '2026-07-20T10:10:00Z' }),
|
|
task({ documentRevision: 3, id: 'current', updatedAt: '2026-07-20T10:01:00Z' }),
|
|
])
|
|
|
|
expect(result.get('document-1')?.id).toBe('current')
|
|
})
|
|
|
|
it('compares task versions without losing sub-millisecond precision', () => {
|
|
expect(taskVersionIsAfter('2026-07-20T10:00:00.123789Z', '2026-07-20T10:00:00.123456Z')).toBe(
|
|
true,
|
|
)
|
|
expect(taskVersionIsAfter('2026-07-20T10:00:00.000001Z', '2026-07-20T10:00:00Z')).toBe(true)
|
|
expect(taskVersionIsAfter('2026-07-20T10:00:00Z', '2026-07-20T10:00:00.000001Z')).toBe(false)
|
|
})
|
|
|
|
it('selects the newest task when versions differ only after milliseconds', () => {
|
|
const result = newestTaskByDocument([
|
|
task({ id: 'older', updatedAt: '2026-07-20T10:00:00.123456Z' }),
|
|
task({ id: 'newer', updatedAt: '2026-07-20T10:00:00.123789Z' }),
|
|
])
|
|
|
|
expect(result.get('document-1')?.id).toBe('newer')
|
|
})
|
|
|
|
it.each([
|
|
['dispatch_pending', 'queued'],
|
|
['queued', 'queued'],
|
|
['retry_wait', 'queued'],
|
|
['running', 'processing'],
|
|
['failed', 'failed'],
|
|
] as const)('maps a latest %s task to %s', (state, expected) => {
|
|
expect(documentDisplayStatus(document(), task({ state }))).toBe(expected)
|
|
})
|
|
|
|
it('keeps a last ready revision available after a canceled re-index', () => {
|
|
expect(documentDisplayStatus(document(), task({ state: 'canceled' }))).toBe('ready')
|
|
expect(
|
|
documentDisplayStatus(
|
|
document({ active: null, activeRevision: undefined, status: 'pending' }),
|
|
task({ state: 'canceled' }),
|
|
),
|
|
).toBe('failed')
|
|
})
|
|
|
|
it('does not let an older failed task override the active revision', () => {
|
|
expect(documentDisplayStatus(document(), task({ documentRevision: 1, state: 'failed' }))).toBe(
|
|
'ready',
|
|
)
|
|
})
|
|
|
|
it('maps a disabled source and deleting lifecycle to the designed disabled state', () => {
|
|
expect(documentDisplayStatus(document(), undefined, true)).toBe('disabled')
|
|
expect(documentDisplayStatus(document({ status: 'deleting' }))).toBe('disabled')
|
|
})
|
|
|
|
it('keeps task badge and actions aligned with the contract states', () => {
|
|
expect(taskIsActive(task({ state: 'running' }))).toBe(true)
|
|
expect(taskNeedsAttention(task({ state: 'failed' }))).toBe(true)
|
|
expect(taskCanRetry(task({ state: 'canceled' }))).toBe(false)
|
|
expect(taskNeedsAttention(task({ state: 'canceled' }))).toBe(false)
|
|
expect(taskNeedsAttention(task({ state: 'succeeded' }))).toBe(false)
|
|
expect(taskCanRetry(task({ state: 'superseded' }))).toBe(false)
|
|
})
|
|
})
|