import { describe, expect, test } from 'bun:test' describe('workflow input identity', () => { test('buildWorkflowSpecV3 preserves explicit workflow input ids', async () => { const { buildWorkflowSpecV3 } = await import('./workflow-spec-builders.ts') const spec = buildWorkflowSpecV3({ inputs: [{ id: 'input-id-1', name: 'document', type: 'file', multimodal: true }], }) expect(spec.inputs).toEqual({ document: { id: 'input-id-1', name: 'document', type: 'file', required: true, processingOptions: { allowMultimodal: true }, }, }) }) test('createVariable can preserve a stable workflow input id', async () => { const { createVariable } = await import('./canvas.ts') expect(createVariable('topic', 'text', { id: 'input-id-2', required: false })).toEqual({ id: 'input-id-2', name: 'topic', type: 'text', required: false, description: undefined, processingOptions: undefined, }) }) test('createWorkflowInput generates a stable id once for authoring reuse', async () => { const { buildWorkflowSpecV3, createWorkflowInput } = await import('./workflow-spec-builders.ts') const input = createWorkflowInput({ name: 'topic', type: 'text' }) const first = buildWorkflowSpecV3({ inputs: [input] }) const second = buildWorkflowSpecV3({ inputs: [input] }) expect(typeof input.id).toBe('string') expect((first.inputs as any).topic.id).toBe(input.id) expect((second.inputs as any).topic.id).toBe(input.id) }) }) describe('workflow reference builders', () => { test('inputRef prefers variable id over name', async () => { const { inputRef } = await import('./workflow-references.ts') expect(inputRef({ id: 'input-id-1', name: 'document' })).toBe('{{input://input-id-1}}') }) test('inputRefByName supports explicit name fallback', async () => { const { inputRefByName } = await import('./workflow-references.ts') expect(inputRefByName('document')).toBe('{{input://document}}') }) test('inputRefByName preserves case-sensitive variable identifiers', async () => { const { inputRefByName } = await import('./workflow-references.ts') expect(inputRefByName('ClientName')).toBe('{{input://ClientName}}') }) test('stepRef supports repeated paths, global format, and per-path formats', async () => { const { stepRef } = await import('./workflow-references.ts') expect(stepRef('node-1', { path: [['user', 'email'], 'items[]'], format: 'ai-xml', pathFormats: { 'user[email]': 'string' }, })).toBe('{{step://node-1?path=user%5Bemail%5D&path=items%5B%5D&format=ai-xml&format%5Buser%5Bemail%5D%5D=string}}') }) test('stepRef treats a flat path segment array as one compound path', async () => { const { stepRef } = await import('./workflow-references.ts') expect(stepRef('node-1', { path: ['user', 'email'] })).toBe('{{step://node-1?path=user%5Bemail%5D}}') }) }) describe('workflow run input identity', () => { test('buildRunInput includes id inside the runtime variable value', async () => { const { buildRunInput } = await import('./workflow-run-builders.ts') expect(buildRunInput('document', '', { id: 'input-id-1', type: 'file', files: [{ fileUrl: 'vault://file', name: 'file.pdf' }], })).toEqual({ id: 'input-id-1', name: 'document', type: 'file', required: true, content: { files: [{ file_url: 'vault://file', file_name: 'file.pdf', vault_reference: undefined, }], }, files: [{ fileUrl: 'vault://file', name: 'file.pdf' }], }) }) test('buildRunPayload keeps inputs keyed by variable name', async () => { const { buildRunInput, buildRunPayload } = await import('./workflow-run-builders.ts') const payload = buildRunPayload({ id: 'run-id-1', promptVersionId: 'version-id', graph: { nodes: [], edges: [] }, inputs: { document: buildRunInput('document', '', { id: 'input-id-1', type: 'file' }), }, }) expect(Object.keys(payload.inputs)).toEqual(['document']) expect(payload.inputs.document).toMatchObject({ id: 'input-id-1', name: 'document' }) expect(payload.id).toBe('run-id-1') }) test('buildRunPayload generates a run id when omitted', async () => { const { buildRunPayload } = await import('./workflow-run-builders.ts') const payload = buildRunPayload({ promptVersionId: 'version-id', graph: { nodes: [], edges: [] }, inputs: {}, }) expect(typeof payload.id).toBe('string') expect(payload.id).not.toHaveLength(0) }) })