diff --git a/src/platform/remote/comfyui/jobs/fetchJobs.test.ts b/src/platform/remote/comfyui/jobs/fetchJobs.test.ts index f3b2ad5a8..56f7fdef6 100644 --- a/src/platform/remote/comfyui/jobs/fetchJobs.test.ts +++ b/src/platform/remote/comfyui/jobs/fetchJobs.test.ts @@ -257,35 +257,66 @@ describe('fetchJobs', () => { }) describe('extractWorkflow', () => { - it('extracts workflow from nested structure', () => { + const validWorkflow = { + version: 0.4, + last_node_id: 1, + last_link_id: 0, + nodes: [], + links: [] + } + + it('extracts and validates workflow from nested structure', async () => { const jobDetail = { ...createMockJob('job1', 'completed'), workflow: { extra_data: { extra_pnginfo: { - workflow: { nodes: [], links: [] } + workflow: validWorkflow } } } } - const workflow = extractWorkflow(jobDetail) + const workflow = await extractWorkflow(jobDetail) - expect(workflow).toEqual({ nodes: [], links: [] }) + expect(workflow).toEqual(validWorkflow) }) - it('returns undefined if workflow not present', () => { + it('returns undefined if workflow not present', async () => { const jobDetail = createMockJob('job1', 'completed') - const workflow = extractWorkflow(jobDetail) + const workflow = await extractWorkflow(jobDetail) expect(workflow).toBeUndefined() }) - it('returns undefined for undefined input', () => { - const workflow = extractWorkflow(undefined) + it('returns undefined for undefined input', async () => { + const workflow = await extractWorkflow(undefined) expect(workflow).toBeUndefined() }) + + it('returns undefined for invalid workflow and logs warning', async () => { + const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const jobDetail = { + ...createMockJob('job1', 'completed'), + workflow: { + extra_data: { + extra_pnginfo: { + workflow: { invalid: 'data' } + } + } + } + } + + const workflow = await extractWorkflow(jobDetail) + + expect(workflow).toBeUndefined() + expect(consoleSpy).toHaveBeenCalledWith( + '[extractWorkflow] Workflow validation failed:', + expect.any(String) + ) + consoleSpy.mockRestore() + }) }) }) diff --git a/src/platform/remote/comfyui/jobs/fetchJobs.ts b/src/platform/remote/comfyui/jobs/fetchJobs.ts index 136f683d6..f5facceff 100644 --- a/src/platform/remote/comfyui/jobs/fetchJobs.ts +++ b/src/platform/remote/comfyui/jobs/fetchJobs.ts @@ -6,6 +6,8 @@ * All distributions use the /jobs endpoint. */ +import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema' +import { validateComfyWorkflow } from '@/platform/workflow/validation/schemas/workflowSchema' import type { PromptId } from '@/schemas/apiSchema' import type { @@ -135,12 +137,25 @@ export async function fetchJobDetail( } /** - * Extracts workflow from job detail response. + * Extracts and validates workflow from job detail response. * The workflow is nested at: workflow.extra_data.extra_pnginfo.workflow - * Full workflow validation happens downstream via validateComfyWorkflow. + * + * Uses Zod validation via validateComfyWorkflow to ensure the workflow + * conforms to the expected schema. Logs validation failures for debugging + * but still returns undefined to allow graceful degradation. */ -export function extractWorkflow(job: JobDetail | undefined): unknown { +export async function extractWorkflow( + job: JobDetail | undefined +): Promise { const parsed = zWorkflowContainer.safeParse(job?.workflow) if (!parsed.success) return undefined - return parsed.data.extra_data?.extra_pnginfo?.workflow + + const rawWorkflow = parsed.data.extra_data?.extra_pnginfo?.workflow + if (!rawWorkflow) return undefined + + const validated = await validateComfyWorkflow(rawWorkflow, (error) => { + console.warn('[extractWorkflow] Workflow validation failed:', error) + }) + + return validated ?? undefined }