mirror of https://github.com/langgenius/dify.git
478 lines
18 KiB
TypeScript
478 lines
18 KiB
TypeScript
/**
|
|
* Integration Test: Pipeline Data Source Store Composition
|
|
*
|
|
* Tests cross-slice interactions in the pipeline data source Zustand store.
|
|
* The unit-level slice specs test each slice in isolation.
|
|
* This integration test verifies:
|
|
* - Store initialization produces correct defaults across all slices
|
|
* - Cross-slice coordination (e.g. credential shared across slices)
|
|
* - State isolation: changes in one slice do not affect others
|
|
* - Full workflow simulation through credential → source → data path
|
|
*/
|
|
|
|
import type { NotionPage } from '@/models/common'
|
|
import type { CrawlResultItem, FileItem } from '@/models/datasets'
|
|
import type { OnlineDriveFile } from '@/models/pipeline'
|
|
import { createDataSourceStore } from '@/app/components/datasets/documents/create-from-pipeline/data-source/store'
|
|
import { CrawlStep } from '@/models/datasets'
|
|
import { OnlineDriveFileType } from '@/models/pipeline'
|
|
|
|
// --- Factory functions ---
|
|
|
|
const createFileItem = (id: string): FileItem => ({
|
|
fileID: id,
|
|
file: { id, name: `${id}.txt`, size: 1024 } as FileItem['file'],
|
|
progress: 100,
|
|
})
|
|
|
|
const createCrawlResultItem = (url: string, title?: string): CrawlResultItem => ({
|
|
title: title ?? `Page: ${url}`,
|
|
markdown: `# ${title ?? url}\n\nContent for ${url}`,
|
|
description: `Description for ${url}`,
|
|
source_url: url,
|
|
})
|
|
|
|
const createOnlineDriveFile = (id: string, name: string, type = OnlineDriveFileType.file): OnlineDriveFile => ({
|
|
id,
|
|
name,
|
|
size: 2048,
|
|
type,
|
|
})
|
|
|
|
const createNotionPage = (pageId: string): NotionPage => ({
|
|
page_id: pageId,
|
|
page_name: `Page ${pageId}`,
|
|
page_icon: null,
|
|
is_bound: true,
|
|
parent_id: 'parent-1',
|
|
type: 'page',
|
|
workspace_id: 'ws-1',
|
|
})
|
|
|
|
describe('Pipeline Data Source Store Composition - Cross-Slice Integration', () => {
|
|
describe('Store Initialization → All Slices Have Correct Defaults', () => {
|
|
it('should create a store with all five slices combined', () => {
|
|
const store = createDataSourceStore()
|
|
const state = store.getState()
|
|
|
|
// Common slice defaults
|
|
expect(state.currentCredentialId).toBe('')
|
|
expect(state.currentNodeIdRef.current).toBe('')
|
|
|
|
// Local file slice defaults
|
|
expect(state.localFileList).toEqual([])
|
|
expect(state.currentLocalFile).toBeUndefined()
|
|
|
|
// Online document slice defaults
|
|
expect(state.documentsData).toEqual([])
|
|
expect(state.onlineDocuments).toEqual([])
|
|
expect(state.searchValue).toBe('')
|
|
expect(state.selectedPagesId).toEqual(new Set())
|
|
|
|
// Website crawl slice defaults
|
|
expect(state.websitePages).toEqual([])
|
|
expect(state.step).toBe(CrawlStep.init)
|
|
expect(state.previewIndex).toBe(-1)
|
|
|
|
// Online drive slice defaults
|
|
expect(state.breadcrumbs).toEqual([])
|
|
expect(state.prefix).toEqual([])
|
|
expect(state.keywords).toBe('')
|
|
expect(state.selectedFileIds).toEqual([])
|
|
expect(state.onlineDriveFileList).toEqual([])
|
|
expect(state.bucket).toBe('')
|
|
expect(state.hasBucket).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('Cross-Slice Coordination: Shared Credential', () => {
|
|
it('should set credential that is accessible from the common slice', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setCurrentCredentialId('cred-abc-123')
|
|
|
|
expect(store.getState().currentCredentialId).toBe('cred-abc-123')
|
|
})
|
|
|
|
it('should allow credential update independently of all other slices', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setLocalFileList([createFileItem('f1')])
|
|
store.getState().setCurrentCredentialId('cred-xyz')
|
|
|
|
expect(store.getState().currentCredentialId).toBe('cred-xyz')
|
|
expect(store.getState().localFileList).toHaveLength(1)
|
|
})
|
|
})
|
|
|
|
describe('Local File Workflow: Set Files → Verify List → Clear', () => {
|
|
it('should set and retrieve local file list', () => {
|
|
const store = createDataSourceStore()
|
|
const files = [createFileItem('f1'), createFileItem('f2'), createFileItem('f3')]
|
|
|
|
store.getState().setLocalFileList(files)
|
|
|
|
expect(store.getState().localFileList).toHaveLength(3)
|
|
expect(store.getState().localFileList[0].fileID).toBe('f1')
|
|
expect(store.getState().localFileList[2].fileID).toBe('f3')
|
|
})
|
|
|
|
it('should update preview ref when setting file list', () => {
|
|
const store = createDataSourceStore()
|
|
const files = [createFileItem('f-preview')]
|
|
|
|
store.getState().setLocalFileList(files)
|
|
|
|
expect(store.getState().previewLocalFileRef.current).toBeDefined()
|
|
})
|
|
|
|
it('should clear files by setting empty list', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setLocalFileList([createFileItem('f1')])
|
|
expect(store.getState().localFileList).toHaveLength(1)
|
|
|
|
store.getState().setLocalFileList([])
|
|
expect(store.getState().localFileList).toHaveLength(0)
|
|
})
|
|
|
|
it('should set and clear current local file selection', () => {
|
|
const store = createDataSourceStore()
|
|
const file = { id: 'current-file', name: 'current.txt' } as FileItem['file']
|
|
|
|
store.getState().setCurrentLocalFile(file)
|
|
expect(store.getState().currentLocalFile).toBeDefined()
|
|
expect(store.getState().currentLocalFile?.id).toBe('current-file')
|
|
|
|
store.getState().setCurrentLocalFile(undefined)
|
|
expect(store.getState().currentLocalFile).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('Online Document Workflow: Set Documents → Select Pages → Verify', () => {
|
|
it('should set documents data and online documents', () => {
|
|
const store = createDataSourceStore()
|
|
const pages = [createNotionPage('page-1'), createNotionPage('page-2')]
|
|
|
|
store.getState().setOnlineDocuments(pages)
|
|
|
|
expect(store.getState().onlineDocuments).toHaveLength(2)
|
|
expect(store.getState().onlineDocuments[0].page_id).toBe('page-1')
|
|
})
|
|
|
|
it('should update preview ref when setting online documents', () => {
|
|
const store = createDataSourceStore()
|
|
const pages = [createNotionPage('page-preview')]
|
|
|
|
store.getState().setOnlineDocuments(pages)
|
|
|
|
expect(store.getState().previewOnlineDocumentRef.current).toBeDefined()
|
|
expect(store.getState().previewOnlineDocumentRef.current?.page_id).toBe('page-preview')
|
|
})
|
|
|
|
it('should track selected page IDs', () => {
|
|
const store = createDataSourceStore()
|
|
const pages = [createNotionPage('p1'), createNotionPage('p2'), createNotionPage('p3')]
|
|
|
|
store.getState().setOnlineDocuments(pages)
|
|
store.getState().setSelectedPagesId(new Set(['p1', 'p3']))
|
|
|
|
expect(store.getState().selectedPagesId.size).toBe(2)
|
|
expect(store.getState().selectedPagesId.has('p1')).toBe(true)
|
|
expect(store.getState().selectedPagesId.has('p2')).toBe(false)
|
|
expect(store.getState().selectedPagesId.has('p3')).toBe(true)
|
|
})
|
|
|
|
it('should manage search value for filtering documents', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setSearchValue('meeting notes')
|
|
|
|
expect(store.getState().searchValue).toBe('meeting notes')
|
|
})
|
|
|
|
it('should set and clear current document selection', () => {
|
|
const store = createDataSourceStore()
|
|
const page = createNotionPage('current-page')
|
|
|
|
store.getState().setCurrentDocument(page)
|
|
expect(store.getState().currentDocument?.page_id).toBe('current-page')
|
|
|
|
store.getState().setCurrentDocument(undefined)
|
|
expect(store.getState().currentDocument).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('Website Crawl Workflow: Set Pages → Track Step → Preview', () => {
|
|
it('should set website pages and update preview ref', () => {
|
|
const store = createDataSourceStore()
|
|
const pages = [
|
|
createCrawlResultItem('https://example.com'),
|
|
createCrawlResultItem('https://example.com/about'),
|
|
]
|
|
|
|
store.getState().setWebsitePages(pages)
|
|
|
|
expect(store.getState().websitePages).toHaveLength(2)
|
|
expect(store.getState().previewWebsitePageRef.current?.source_url).toBe('https://example.com')
|
|
})
|
|
|
|
it('should manage crawl step transitions', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
expect(store.getState().step).toBe(CrawlStep.init)
|
|
|
|
store.getState().setStep(CrawlStep.running)
|
|
expect(store.getState().step).toBe(CrawlStep.running)
|
|
|
|
store.getState().setStep(CrawlStep.finished)
|
|
expect(store.getState().step).toBe(CrawlStep.finished)
|
|
})
|
|
|
|
it('should set crawl result with data and timing', () => {
|
|
const store = createDataSourceStore()
|
|
const result = {
|
|
data: [createCrawlResultItem('https://test.com')],
|
|
time_consuming: 3.5,
|
|
}
|
|
|
|
store.getState().setCrawlResult(result)
|
|
|
|
expect(store.getState().crawlResult?.data).toHaveLength(1)
|
|
expect(store.getState().crawlResult?.time_consuming).toBe(3.5)
|
|
})
|
|
|
|
it('should manage preview index for page navigation', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setPreviewIndex(2)
|
|
expect(store.getState().previewIndex).toBe(2)
|
|
|
|
store.getState().setPreviewIndex(-1)
|
|
expect(store.getState().previewIndex).toBe(-1)
|
|
})
|
|
|
|
it('should set and clear current website selection', () => {
|
|
const store = createDataSourceStore()
|
|
const page = createCrawlResultItem('https://current.com')
|
|
|
|
store.getState().setCurrentWebsite(page)
|
|
expect(store.getState().currentWebsite?.source_url).toBe('https://current.com')
|
|
|
|
store.getState().setCurrentWebsite(undefined)
|
|
expect(store.getState().currentWebsite).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('Online Drive Workflow: Breadcrumbs → File Selection → Navigation', () => {
|
|
it('should manage breadcrumb navigation', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setBreadcrumbs(['root', 'folder-a', 'subfolder'])
|
|
|
|
expect(store.getState().breadcrumbs).toEqual(['root', 'folder-a', 'subfolder'])
|
|
})
|
|
|
|
it('should support breadcrumb push/pop pattern', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setBreadcrumbs(['root'])
|
|
store.getState().setBreadcrumbs([...store.getState().breadcrumbs, 'level-1'])
|
|
store.getState().setBreadcrumbs([...store.getState().breadcrumbs, 'level-2'])
|
|
|
|
expect(store.getState().breadcrumbs).toEqual(['root', 'level-1', 'level-2'])
|
|
|
|
// Pop back one level
|
|
store.getState().setBreadcrumbs(store.getState().breadcrumbs.slice(0, -1))
|
|
expect(store.getState().breadcrumbs).toEqual(['root', 'level-1'])
|
|
})
|
|
|
|
it('should manage file list and selection', () => {
|
|
const store = createDataSourceStore()
|
|
const files = [
|
|
createOnlineDriveFile('drive-1', 'report.pdf'),
|
|
createOnlineDriveFile('drive-2', 'data.csv'),
|
|
createOnlineDriveFile('drive-3', 'images', OnlineDriveFileType.folder),
|
|
]
|
|
|
|
store.getState().setOnlineDriveFileList(files)
|
|
expect(store.getState().onlineDriveFileList).toHaveLength(3)
|
|
|
|
store.getState().setSelectedFileIds(['drive-1', 'drive-2'])
|
|
expect(store.getState().selectedFileIds).toEqual(['drive-1', 'drive-2'])
|
|
})
|
|
|
|
it('should update preview ref when selecting files', () => {
|
|
const store = createDataSourceStore()
|
|
const files = [
|
|
createOnlineDriveFile('drive-a', 'file-a.txt'),
|
|
createOnlineDriveFile('drive-b', 'file-b.txt'),
|
|
]
|
|
|
|
store.getState().setOnlineDriveFileList(files)
|
|
store.getState().setSelectedFileIds(['drive-b'])
|
|
|
|
expect(store.getState().previewOnlineDriveFileRef.current?.id).toBe('drive-b')
|
|
})
|
|
|
|
it('should manage bucket and prefix for S3-like navigation', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setBucket('my-data-bucket')
|
|
store.getState().setPrefix(['data', '2024'])
|
|
store.getState().setHasBucket(true)
|
|
|
|
expect(store.getState().bucket).toBe('my-data-bucket')
|
|
expect(store.getState().prefix).toEqual(['data', '2024'])
|
|
expect(store.getState().hasBucket).toBe(true)
|
|
})
|
|
|
|
it('should manage keywords for search filtering', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setKeywords('quarterly report')
|
|
expect(store.getState().keywords).toBe('quarterly report')
|
|
})
|
|
})
|
|
|
|
describe('State Isolation: Changes to One Slice Do Not Affect Others', () => {
|
|
it('should keep local file state independent from online document state', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setLocalFileList([createFileItem('local-1')])
|
|
store.getState().setOnlineDocuments([createNotionPage('notion-1')])
|
|
|
|
expect(store.getState().localFileList).toHaveLength(1)
|
|
expect(store.getState().onlineDocuments).toHaveLength(1)
|
|
|
|
// Clearing local files should not affect online documents
|
|
store.getState().setLocalFileList([])
|
|
expect(store.getState().localFileList).toHaveLength(0)
|
|
expect(store.getState().onlineDocuments).toHaveLength(1)
|
|
})
|
|
|
|
it('should keep website crawl state independent from online drive state', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
store.getState().setWebsitePages([createCrawlResultItem('https://site.com')])
|
|
store.getState().setOnlineDriveFileList([createOnlineDriveFile('d1', 'file.txt')])
|
|
|
|
expect(store.getState().websitePages).toHaveLength(1)
|
|
expect(store.getState().onlineDriveFileList).toHaveLength(1)
|
|
|
|
// Clearing website pages should not affect drive files
|
|
store.getState().setWebsitePages([])
|
|
expect(store.getState().websitePages).toHaveLength(0)
|
|
expect(store.getState().onlineDriveFileList).toHaveLength(1)
|
|
})
|
|
|
|
it('should create fully independent store instances', () => {
|
|
const storeA = createDataSourceStore()
|
|
const storeB = createDataSourceStore()
|
|
|
|
storeA.getState().setCurrentCredentialId('cred-A')
|
|
storeA.getState().setLocalFileList([createFileItem('fa-1')])
|
|
|
|
expect(storeA.getState().currentCredentialId).toBe('cred-A')
|
|
expect(storeB.getState().currentCredentialId).toBe('')
|
|
expect(storeB.getState().localFileList).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('Full Workflow Simulation: Credential → Source → Data → Verify', () => {
|
|
it('should support a complete local file upload workflow', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
// Step 1: Set credential
|
|
store.getState().setCurrentCredentialId('upload-cred-1')
|
|
|
|
// Step 2: Set file list
|
|
const files = [createFileItem('upload-1'), createFileItem('upload-2')]
|
|
store.getState().setLocalFileList(files)
|
|
|
|
// Step 3: Select current file for preview
|
|
store.getState().setCurrentLocalFile(files[0].file)
|
|
|
|
// Verify all state is consistent
|
|
expect(store.getState().currentCredentialId).toBe('upload-cred-1')
|
|
expect(store.getState().localFileList).toHaveLength(2)
|
|
expect(store.getState().currentLocalFile?.id).toBe('upload-1')
|
|
expect(store.getState().previewLocalFileRef.current).toBeDefined()
|
|
})
|
|
|
|
it('should support a complete website crawl workflow', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
// Step 1: Set credential
|
|
store.getState().setCurrentCredentialId('crawl-cred-1')
|
|
|
|
// Step 2: Init crawl
|
|
store.getState().setStep(CrawlStep.running)
|
|
|
|
// Step 3: Crawl completes with results
|
|
const crawledPages = [
|
|
createCrawlResultItem('https://docs.example.com/guide'),
|
|
createCrawlResultItem('https://docs.example.com/api'),
|
|
createCrawlResultItem('https://docs.example.com/faq'),
|
|
]
|
|
store.getState().setCrawlResult({ data: crawledPages, time_consuming: 12.5 })
|
|
store.getState().setStep(CrawlStep.finished)
|
|
|
|
// Step 4: Set website pages from results
|
|
store.getState().setWebsitePages(crawledPages)
|
|
|
|
// Step 5: Set preview
|
|
store.getState().setPreviewIndex(1)
|
|
|
|
// Verify all state
|
|
expect(store.getState().currentCredentialId).toBe('crawl-cred-1')
|
|
expect(store.getState().step).toBe(CrawlStep.finished)
|
|
expect(store.getState().websitePages).toHaveLength(3)
|
|
expect(store.getState().crawlResult?.time_consuming).toBe(12.5)
|
|
expect(store.getState().previewIndex).toBe(1)
|
|
expect(store.getState().previewWebsitePageRef.current?.source_url).toBe('https://docs.example.com/guide')
|
|
})
|
|
|
|
it('should support a complete online drive navigation workflow', () => {
|
|
const store = createDataSourceStore()
|
|
|
|
// Step 1: Set credential
|
|
store.getState().setCurrentCredentialId('drive-cred-1')
|
|
|
|
// Step 2: Set bucket
|
|
store.getState().setBucket('company-docs')
|
|
store.getState().setHasBucket(true)
|
|
|
|
// Step 3: Navigate into folders
|
|
store.getState().setBreadcrumbs(['company-docs'])
|
|
store.getState().setPrefix(['projects'])
|
|
const folderFiles = [
|
|
createOnlineDriveFile('proj-1', 'project-alpha', OnlineDriveFileType.folder),
|
|
createOnlineDriveFile('proj-2', 'project-beta', OnlineDriveFileType.folder),
|
|
createOnlineDriveFile('readme', 'README.md', OnlineDriveFileType.file),
|
|
]
|
|
store.getState().setOnlineDriveFileList(folderFiles)
|
|
|
|
// Step 4: Navigate deeper
|
|
store.getState().setBreadcrumbs([...store.getState().breadcrumbs, 'project-alpha'])
|
|
store.getState().setPrefix([...store.getState().prefix, 'project-alpha'])
|
|
|
|
// Step 5: Select files
|
|
store.getState().setOnlineDriveFileList([
|
|
createOnlineDriveFile('doc-1', 'spec.pdf'),
|
|
createOnlineDriveFile('doc-2', 'design.fig'),
|
|
])
|
|
store.getState().setSelectedFileIds(['doc-1'])
|
|
|
|
// Verify full state
|
|
expect(store.getState().currentCredentialId).toBe('drive-cred-1')
|
|
expect(store.getState().bucket).toBe('company-docs')
|
|
expect(store.getState().breadcrumbs).toEqual(['company-docs', 'project-alpha'])
|
|
expect(store.getState().prefix).toEqual(['projects', 'project-alpha'])
|
|
expect(store.getState().onlineDriveFileList).toHaveLength(2)
|
|
expect(store.getState().selectedFileIds).toEqual(['doc-1'])
|
|
expect(store.getState().previewOnlineDriveFileRef.current?.name).toBe('spec.pdf')
|
|
})
|
|
})
|
|
})
|