dify/web/features/deployments/create-release/state/__tests__/dsl-enabled.spec.ts

206 lines
5.9 KiB
TypeScript

import type { QueryClient } from '@tanstack/react-query'
import { skipToken } from '@tanstack/react-query'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createQueryAtomTestStore } from '@/test/query-atom'
type QueryOptions = {
enabled?: boolean
input?: unknown
}
type InfiniteQueryOptions = QueryOptions & {
input?: (pageParam: number) => unknown
}
const mockSourceAppsInfiniteOptions = vi.hoisted(() => vi.fn())
const mockPrecheckReleaseQueryOptions = vi.hoisted(() => vi.fn())
const mockCreateRelease = vi.hoisted(() => vi.fn())
vi.mock('../../../shared/domain/feature-flags', () => ({
isDeploymentDslImportEnabled: true,
}))
vi.mock('@/service/client', () => ({
consoleQuery: {
apps: {
get: {
infiniteOptions: (options: InfiniteQueryOptions) => {
mockSourceAppsInfiniteOptions(options)
return {
...options,
queryKey: ['sourceApps', options.input],
queryFn: async () => ({
data: [],
has_more: false,
limit: 20,
page: 1,
total: 0,
}),
}
},
},
},
enterprise: {
releaseService: {
listReleaseSummaries: {
key: ({ input }: { input?: unknown } = {}) =>
input === undefined ? ['listReleaseSummaries'] : ['listReleaseSummaries', input],
},
listReleases: {
key: ({ input }: { input?: unknown } = {}) =>
input === undefined ? ['listReleases'] : ['listReleases', input],
},
precheckRelease: {
queryOptions: (options: QueryOptions) => {
mockPrecheckReleaseQueryOptions(options)
return {
...options,
queryKey: ['precheckRelease', options.input],
}
},
},
createRelease: {
mutationOptions: () => ({
mutationKey: ['createRelease'],
mutationFn: mockCreateRelease,
}),
},
},
},
},
}))
async function loadState() {
return await import('../index')
}
async function mountedStore() {
const state = await loadState()
const { queryClient, store } = createQueryAtomTestStore()
const unsubscribe = store.sub(state.createReleaseFormIsSubmittingAtom, () => undefined)
return {
queryClient,
state,
store,
unsubscribe,
}
}
function workflowDsl() {
return ['app:', ' mode: workflow', ' name: Release source'].join('\n')
}
function setDslFileContentResult(queryClient: QueryClient, file: File, fileReadVersion: number) {
queryClient.setQueryData(
['createReleaseDslFileContent', fileReadVersion, file, file.name, file.size, file.lastModified],
workflowDsl(),
)
}
function setPrecheckReleaseResult(
queryClient: QueryClient,
input: {
body: {
appInstanceId: string
dsl: string
}
},
) {
queryClient.setQueryData(['precheckRelease', input], {
gateCommitId: 'gate-commit-1',
canCreate: true,
unsupportedNodes: [],
})
}
describe('create release state with DSL import enabled', () => {
beforeEach(() => {
vi.clearAllMocks()
mockCreateRelease.mockReset()
})
it('should enable source app search with the current search text while creating from an app', async () => {
const { state, store, unsubscribe } = await mountedStore()
store.set(state.createReleaseAppInstanceIdAtom, 'app-instance-1')
store.set(state.openCreateReleaseDialogAtom)
store.set(state.createReleaseSourceAppSearchTextAtom, 'customer')
store.get(state.createReleaseSourceAppsQueryAtom)
const sourceAppsQuery = mockSourceAppsInfiniteOptions.mock.lastCall?.[0] as InfiniteQueryOptions
expect(sourceAppsQuery.enabled).toBe(true)
expect(sourceAppsQuery.input?.(2)).toEqual({
query: {
page: 2,
limit: 20,
name: 'customer',
mode: 'workflow',
},
})
unsubscribe()
})
it('should skip DSL release precheck input until DSL content is ready', async () => {
const { state, store, unsubscribe } = await mountedStore()
store.set(state.createReleaseAppInstanceIdAtom, 'app-instance-1')
store.set(state.openCreateReleaseDialogAtom)
store.set(state.selectCreateReleaseSourceModeAtom, 'dsl')
store.get(state.isCheckingCreateReleaseContentAtom)
expect(mockPrecheckReleaseQueryOptions).toHaveBeenLastCalledWith(
expect.objectContaining({
enabled: false,
input: skipToken,
}),
)
unsubscribe()
})
it('should submit a DSL release with encoded workflow content', async () => {
const { queryClient, state, store, unsubscribe } = await mountedStore()
const response = {
release: {
displayName: 'Release from DSL',
},
}
const file = new File([workflowDsl()], 'workflow.yml', { type: 'text/yaml' })
mockCreateRelease.mockResolvedValue(response)
store.set(state.createReleaseAppInstanceIdAtom, 'app-instance-1')
store.set(state.openCreateReleaseDialogAtom)
store.set(state.selectCreateReleaseSourceModeAtom, 'dsl')
store.set(state.updateCreateReleaseDslFileAtom, file)
setDslFileContentResult(queryClient, file, 2)
const encodedDslContent = store.get(state.createReleaseEncodedDslContentAtom)
setPrecheckReleaseResult(queryClient, {
body: {
appInstanceId: 'app-instance-1',
dsl: encodedDslContent,
},
})
store.set(state.createReleaseNameFieldAtom, ' Release from DSL ')
store.set(state.createReleaseDescriptionFieldAtom, ' Imported workflow ')
const result = await store.set(state.submitCreateReleaseFormAtom)
expect(result).toBe(response)
expect(encodedDslContent).not.toBe('')
expect(mockCreateRelease.mock.calls[0]?.[0]).toEqual({
body: {
appInstanceId: 'app-instance-1',
displayName: 'Release from DSL',
description: 'Imported workflow',
createAppInstance: false,
dsl: encodedDslContent,
},
})
unsubscribe()
})
})