chore: snapshot workflow migration progress

This commit is contained in:
2026-04-12 11:49:04 +02:00
parent 0cd02513d5
commit 3e810c74a3
163 changed files with 31773 additions and 2752 deletions
@@ -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,
}),
]),
)
})
})