chore: snapshot workflow migration progress
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
import { describe, expect, test } from 'vitest'
|
||||
|
||||
import type { WorkflowNodeDefinition } from '../../api/workflows'
|
||||
import { createWorkflowModuleBundleInsertion, getWorkflowModuleBundles } from '../../components/workflows/workflowModuleBundles'
|
||||
import {
|
||||
createWorkflowReferenceBundleInsertion,
|
||||
getWorkflowReferenceBundles,
|
||||
} from '../../components/workflows/workflowReferenceBundles'
|
||||
|
||||
const definitions: WorkflowNodeDefinition[] = [
|
||||
{
|
||||
step: 'order_line_setup',
|
||||
label: 'Order Line Setup',
|
||||
family: 'order_line',
|
||||
module_key: 'order_line.prepare_render_context',
|
||||
category: 'input',
|
||||
description: 'Prepare render context.',
|
||||
node_type: 'inputNode',
|
||||
icon: 'refresh-cw',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'order_line' },
|
||||
output_contract: { context: 'order_line', provides: ['order_line'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.order_line_setup',
|
||||
},
|
||||
{
|
||||
step: 'resolve_template',
|
||||
label: 'Resolve Template',
|
||||
family: 'order_line',
|
||||
module_key: 'rendering.resolve_template',
|
||||
category: 'processing',
|
||||
description: 'Resolve template.',
|
||||
node_type: 'processNode',
|
||||
icon: 'layers',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'order_line', requires: ['order_line'] },
|
||||
output_contract: { context: 'order_line', provides: ['render_template'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.resolve_template',
|
||||
},
|
||||
{
|
||||
step: 'auto_populate_materials',
|
||||
label: 'Auto Populate Materials',
|
||||
family: 'order_line',
|
||||
module_key: 'materials.auto_populate',
|
||||
category: 'processing',
|
||||
description: 'Populate materials.',
|
||||
node_type: 'processNode',
|
||||
icon: 'layers',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'order_line', requires: ['order_line'] },
|
||||
output_contract: { context: 'order_line', provides: ['cad_materials'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.auto_populate_materials',
|
||||
},
|
||||
{
|
||||
step: 'glb_bbox',
|
||||
label: 'Compute Bounding Box',
|
||||
family: 'order_line',
|
||||
module_key: 'geometry.compute_bbox',
|
||||
category: 'processing',
|
||||
description: 'Compute bbox.',
|
||||
node_type: 'processNode',
|
||||
icon: 'layers',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'native',
|
||||
legacy_compatible: false,
|
||||
input_contract: { context: 'order_line', requires: ['order_line'] },
|
||||
output_contract: { context: 'order_line', provides: ['bbox'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: null,
|
||||
},
|
||||
{
|
||||
step: 'material_map_resolve',
|
||||
label: 'Resolve Material Map',
|
||||
family: 'order_line',
|
||||
module_key: 'materials.resolve_map',
|
||||
category: 'processing',
|
||||
description: 'Resolve material map.',
|
||||
node_type: 'processNode',
|
||||
icon: 'layers',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'native',
|
||||
legacy_compatible: false,
|
||||
input_contract: { context: 'order_line', requires: ['order_line'] },
|
||||
output_contract: { context: 'order_line', provides: ['material_map'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: null,
|
||||
},
|
||||
{
|
||||
step: 'blender_still',
|
||||
label: 'Blender Still',
|
||||
family: 'order_line',
|
||||
module_key: 'render.production.still',
|
||||
category: 'rendering',
|
||||
description: 'Render still image.',
|
||||
node_type: 'renderNode',
|
||||
icon: 'camera',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'native',
|
||||
legacy_compatible: false,
|
||||
input_contract: { context: 'order_line', requires: ['order_line'] },
|
||||
output_contract: { context: 'order_line', provides: ['rendered_image'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: null,
|
||||
},
|
||||
{
|
||||
step: 'output_save',
|
||||
label: 'Save Output',
|
||||
family: 'order_line',
|
||||
module_key: 'media.save_output',
|
||||
category: 'output',
|
||||
description: 'Save output.',
|
||||
node_type: 'outputNode',
|
||||
icon: 'download',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'order_line', requires: ['rendered_image'] },
|
||||
output_contract: { context: 'order_line', provides: ['saved_output'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.output_save',
|
||||
},
|
||||
{
|
||||
step: 'notify',
|
||||
label: 'Notify Result',
|
||||
family: 'order_line',
|
||||
module_key: 'notifications.emit',
|
||||
category: 'output',
|
||||
description: 'Notify result.',
|
||||
node_type: 'outputNode',
|
||||
icon: 'bell',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'order_line', requires: ['saved_output'] },
|
||||
output_contract: { context: 'order_line', provides: ['notification'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.notify',
|
||||
},
|
||||
]
|
||||
|
||||
const cadDefinitions: WorkflowNodeDefinition[] = [
|
||||
{
|
||||
step: 'resolve_step_path',
|
||||
label: 'Resolve STEP Path',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.resolve_step_path',
|
||||
category: 'input',
|
||||
description: 'Resolve the STEP path.',
|
||||
node_type: 'inputNode',
|
||||
icon: 'file-code-2',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'cad_file' },
|
||||
output_contract: { context: 'cad_file', provides: ['cad_file_record'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.resolve_step_path',
|
||||
},
|
||||
{
|
||||
step: 'occ_object_extract',
|
||||
label: 'Extract STEP Objects',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.extract_objects',
|
||||
category: 'processing',
|
||||
description: 'Extract objects.',
|
||||
node_type: 'processNode',
|
||||
icon: 'boxes',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'native',
|
||||
legacy_compatible: false,
|
||||
input_contract: { context: 'cad_file', requires: ['cad_file_record'] },
|
||||
output_contract: { context: 'cad_file', provides: ['occ_scene'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: null,
|
||||
},
|
||||
{
|
||||
step: 'occ_glb_export',
|
||||
label: 'Export GLB',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.export_glb',
|
||||
category: 'processing',
|
||||
description: 'Export GLB.',
|
||||
node_type: 'processNode',
|
||||
icon: 'package',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'native',
|
||||
legacy_compatible: false,
|
||||
input_contract: { context: 'cad_file', requires: ['occ_scene'] },
|
||||
output_contract: { context: 'cad_file', provides: ['glb_preview'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: null,
|
||||
},
|
||||
{
|
||||
step: 'stl_cache_generate',
|
||||
label: 'Generate STL Cache',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.generate_stl_cache',
|
||||
category: 'processing',
|
||||
description: 'Generate STL cache.',
|
||||
node_type: 'processNode',
|
||||
icon: 'database',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'cad_file', requires: ['glb_preview'] },
|
||||
output_contract: { context: 'cad_file', provides: ['stl_cache'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.stl_cache_generate',
|
||||
},
|
||||
{
|
||||
step: 'blender_render',
|
||||
label: 'Render Thumbnail (Blender)',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.thumbnail.blender',
|
||||
category: 'rendering',
|
||||
description: 'Render Blender thumbnail.',
|
||||
node_type: 'renderNode',
|
||||
icon: 'camera',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'cad_file', requires: ['glb_preview'] },
|
||||
output_contract: { context: 'cad_file', provides: ['rendered_image'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.blender_render',
|
||||
},
|
||||
{
|
||||
step: 'threejs_render',
|
||||
label: 'Render Thumbnail (Three.js)',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.thumbnail.threejs',
|
||||
category: 'rendering',
|
||||
description: 'Render Three.js thumbnail.',
|
||||
node_type: 'renderNode',
|
||||
icon: 'box',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'cad_file', requires: ['glb_preview'] },
|
||||
output_contract: { context: 'cad_file', provides: ['rendered_image'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.threejs_render',
|
||||
},
|
||||
{
|
||||
step: 'thumbnail_save',
|
||||
label: 'Save Thumbnail',
|
||||
family: 'cad_file',
|
||||
module_key: 'cad.thumbnail.save',
|
||||
category: 'output',
|
||||
description: 'Persist thumbnail.',
|
||||
node_type: 'outputNode',
|
||||
icon: 'download',
|
||||
defaults: {},
|
||||
fields: [],
|
||||
execution_kind: 'bridge',
|
||||
legacy_compatible: true,
|
||||
input_contract: { context: 'cad_file', requires: ['rendered_image'] },
|
||||
output_contract: { context: 'cad_file', provides: ['saved_thumbnail'] },
|
||||
artifact_roles_produced: [],
|
||||
artifact_roles_consumed: [],
|
||||
legacy_source: 'legacy.thumbnail_save',
|
||||
},
|
||||
]
|
||||
|
||||
describe('workflowModuleBundles', () => {
|
||||
test('exposes family-scoped bundles when required steps exist', () => {
|
||||
const bundles = getWorkflowModuleBundles(definitions, 'order_line')
|
||||
|
||||
expect(bundles.map(bundle => bundle.id)).toEqual(['still_render_core', 'output_publish_notify'])
|
||||
})
|
||||
|
||||
test('creates a connected bundle insertion graph for still-render authoring', () => {
|
||||
const insertion = createWorkflowModuleBundleInsertion({
|
||||
bundleId: 'still_render_core',
|
||||
graphFamily: 'order_line',
|
||||
nodeDefinitionsByStep: Object.fromEntries(definitions.map(definition => [definition.step, definition])),
|
||||
existingNodes: [],
|
||||
preferredPosition: { x: 200, y: 320 },
|
||||
})
|
||||
|
||||
expect(insertion.ok).toBe(true)
|
||||
if (!insertion.ok) return
|
||||
|
||||
expect(insertion.nodes).toHaveLength(6)
|
||||
expect(insertion.edges).toHaveLength(5)
|
||||
expect(insertion.nodes[0].position).toEqual({ x: 200, y: 320 })
|
||||
expect(insertion.nodes[1].position).toEqual({ x: 420, y: 320 })
|
||||
expect(insertion.nodes[0].data).toMatchObject({ step: 'order_line_setup', label: 'Order Line Setup' })
|
||||
expect(insertion.nodes[5].data).toMatchObject({ step: 'blender_still', label: 'Blender Still' })
|
||||
expect(insertion.edges[0]).toMatchObject({
|
||||
source: insertion.nodes[0].id,
|
||||
target: insertion.nodes[1].id,
|
||||
})
|
||||
})
|
||||
|
||||
test('exposes full reference paths for complete non-legacy authoring flows', () => {
|
||||
const bundles = getWorkflowReferenceBundles(definitions, 'order_line')
|
||||
|
||||
expect(bundles.map(bundle => bundle.id)).toEqual(['still_render_reference'])
|
||||
})
|
||||
|
||||
test('creates the canonical still-render reference graph with branched edges', () => {
|
||||
const insertion = createWorkflowReferenceBundleInsertion({
|
||||
bundleId: 'still_render_reference',
|
||||
graphFamily: 'order_line',
|
||||
nodeDefinitionsByStep: Object.fromEntries(definitions.map(definition => [definition.step, definition])),
|
||||
existingNodes: [],
|
||||
preferredPosition: { x: 200, y: 320 },
|
||||
})
|
||||
|
||||
expect(insertion.ok).toBe(true)
|
||||
if (!insertion.ok) return
|
||||
|
||||
expect(insertion.nodes).toHaveLength(8)
|
||||
expect(insertion.edges).toHaveLength(10)
|
||||
expect(insertion.nodes[0].position).toEqual({ x: 200, y: 440 })
|
||||
expect(insertion.nodes[1].position).toEqual({ x: 420, y: 440 })
|
||||
expect(insertion.nodes[5].data).toMatchObject({
|
||||
step: 'blender_still',
|
||||
label: 'Still Render',
|
||||
params: { use_custom_render_settings: true },
|
||||
})
|
||||
expect(insertion.edges).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
source: insertion.nodes[0].id,
|
||||
target: insertion.nodes[1].id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
source: insertion.nodes[5].id,
|
||||
target: insertion.nodes[6].id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
source: insertion.nodes[5].id,
|
||||
target: insertion.nodes[7].id,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
test('creates the canonical CAD intake reference graph from the shared blueprint', () => {
|
||||
const insertion = createWorkflowReferenceBundleInsertion({
|
||||
bundleId: 'cad_intake_reference',
|
||||
graphFamily: 'cad_file',
|
||||
nodeDefinitionsByStep: Object.fromEntries(cadDefinitions.map(definition => [definition.step, definition])),
|
||||
existingNodes: [],
|
||||
preferredPosition: { x: 120, y: 240 },
|
||||
})
|
||||
|
||||
expect(insertion.ok).toBe(true)
|
||||
if (!insertion.ok) return
|
||||
|
||||
expect(insertion.nodes).toHaveLength(8)
|
||||
expect(insertion.edges).toHaveLength(7)
|
||||
expect(insertion.nodes.map(node => node.data.step)).toEqual([
|
||||
'resolve_step_path',
|
||||
'occ_object_extract',
|
||||
'occ_glb_export',
|
||||
'stl_cache_generate',
|
||||
'blender_render',
|
||||
'threejs_render',
|
||||
'thumbnail_save',
|
||||
'thumbnail_save',
|
||||
])
|
||||
expect(insertion.edges).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
source: insertion.nodes[2].id,
|
||||
target: insertion.nodes[4].id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
source: insertion.nodes[2].id,
|
||||
target: insertion.nodes[5].id,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user