Skip to content

Commit 4d410ad

Browse files
committed
fix subflows to make them consistent
1 parent a364431 commit 4d410ad

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import type { VariableResolver } from '@/executor/variables/resolver'
3+
import { resolveArrayInput } from './subflow-utils'
4+
5+
describe('resolveArrayInput', () => {
6+
const fakeCtx = {} as any
7+
8+
it('returns arrays as-is', () => {
9+
expect(resolveArrayInput(fakeCtx, [1, 2, 3], null)).toEqual([1, 2, 3])
10+
})
11+
12+
it('converts plain objects to entries', () => {
13+
expect(resolveArrayInput(fakeCtx, { a: 1, b: 2 }, null)).toEqual([
14+
['a', 1],
15+
['b', 2],
16+
])
17+
})
18+
19+
it('returns empty array when a pure reference resolves to null (skipped block)', () => {
20+
// `resolveSingleReference` returns `null` for a reference that points at a
21+
// block that exists in the workflow but did not execute on this path.
22+
// A loop/parallel over such a reference should run zero iterations rather
23+
// than fail the workflow.
24+
const resolver = {
25+
resolveSingleReference: vi.fn().mockReturnValue(null),
26+
} as unknown as VariableResolver
27+
28+
const result = resolveArrayInput(fakeCtx, '<SkippedBlock.result.items>', resolver)
29+
30+
expect(result).toEqual([])
31+
expect(resolver.resolveSingleReference).toHaveBeenCalled()
32+
})
33+
34+
it('returns the array from a pure reference that resolved to an array', () => {
35+
const resolver = {
36+
resolveSingleReference: vi.fn().mockReturnValue([1, 2, 3]),
37+
} as unknown as VariableResolver
38+
39+
expect(resolveArrayInput(fakeCtx, '<Block.items>', resolver)).toEqual([1, 2, 3])
40+
})
41+
42+
it('converts resolved objects to entries', () => {
43+
const resolver = {
44+
resolveSingleReference: vi.fn().mockReturnValue({ x: 1, y: 2 }),
45+
} as unknown as VariableResolver
46+
47+
expect(resolveArrayInput(fakeCtx, '<Block.obj>', resolver)).toEqual([
48+
['x', 1],
49+
['y', 2],
50+
])
51+
})
52+
53+
it('throws when a pure reference resolves to a non-array, non-object, non-null value', () => {
54+
const resolver = {
55+
resolveSingleReference: vi.fn().mockReturnValue(42),
56+
} as unknown as VariableResolver
57+
58+
expect(() => resolveArrayInput(fakeCtx, '<Block.count>', resolver)).toThrow(
59+
/did not resolve to an array or object/
60+
)
61+
})
62+
63+
it('throws when a pure reference resolves to undefined (unknown block)', () => {
64+
// `undefined` means the reference could not be matched to any block at
65+
// all (typo / deleted block). This must still fail loudly.
66+
const resolver = {
67+
resolveSingleReference: vi.fn().mockReturnValue(undefined),
68+
} as unknown as VariableResolver
69+
70+
expect(() => resolveArrayInput(fakeCtx, '<Missing.items>', resolver)).toThrow(
71+
/did not resolve to an array or object/
72+
)
73+
})
74+
75+
it('parses a JSON array string', () => {
76+
expect(resolveArrayInput(fakeCtx, '[1, 2, 3]', null)).toEqual([1, 2, 3])
77+
})
78+
79+
it('throws on a string that is neither a reference nor valid JSON array/object', () => {
80+
expect(() => resolveArrayInput(fakeCtx, 'not json', null)).toThrow()
81+
})
82+
})

apps/sim/executor/utils/subflow-utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ export function resolveArrayInput(
216216
if (typeof resolved === 'object' && resolved !== null) {
217217
return Object.entries(resolved)
218218
}
219+
if (resolved === null) {
220+
return []
221+
}
219222
throw new Error(`Reference "${items}" did not resolve to an array or object`)
220223
} catch (error) {
221224
if (error instanceof Error && error.message.startsWith('Reference "')) {

0 commit comments

Comments
 (0)