|
5 | 5 | FlowEdgeSchema, |
6 | 6 | FlowVariableSchema, |
7 | 7 | FlowNodeAction, |
| 8 | + FlowVersionHistorySchema, |
8 | 9 | defineFlow, |
9 | 10 | type Flow, |
10 | 11 | type FlowNode, |
@@ -130,6 +131,39 @@ describe('FlowNodeSchema', () => { |
130 | 131 | expect(() => FlowNodeSchema.parse(node)).not.toThrow(); |
131 | 132 | }); |
132 | 133 | }); |
| 134 | + |
| 135 | + it('should accept node with timeoutMs', () => { |
| 136 | + const result = FlowNodeSchema.safeParse({ |
| 137 | + id: 'http_1', |
| 138 | + type: 'http_request', |
| 139 | + label: 'Call API', |
| 140 | + timeoutMs: 5000, |
| 141 | + }); |
| 142 | + expect(result.success).toBe(true); |
| 143 | + if (result.success) { |
| 144 | + expect(result.data.timeoutMs).toBe(5000); |
| 145 | + } |
| 146 | + }); |
| 147 | + |
| 148 | + it('should accept node with inputSchema and outputSchema', () => { |
| 149 | + const result = FlowNodeSchema.safeParse({ |
| 150 | + id: 'script_1', |
| 151 | + type: 'script', |
| 152 | + label: 'Process Data', |
| 153 | + inputSchema: { |
| 154 | + name: { type: 'string', required: true, description: 'User name' }, |
| 155 | + age: { type: 'number', required: false }, |
| 156 | + }, |
| 157 | + outputSchema: { |
| 158 | + greeting: { type: 'string', description: 'Generated greeting' }, |
| 159 | + }, |
| 160 | + }); |
| 161 | + expect(result.success).toBe(true); |
| 162 | + if (result.success) { |
| 163 | + expect(result.data.inputSchema).toBeDefined(); |
| 164 | + expect(result.data.outputSchema).toBeDefined(); |
| 165 | + } |
| 166 | + }); |
133 | 167 | }); |
134 | 168 |
|
135 | 169 | describe('FlowEdgeSchema', () => { |
@@ -597,6 +631,53 @@ describe('FlowSchema - errorHandling', () => { |
597 | 631 | }); |
598 | 632 | expect(result.errorHandling).toBeUndefined(); |
599 | 633 | }); |
| 634 | + |
| 635 | + it('should accept exponential backoff configuration', () => { |
| 636 | + const result = FlowSchema.safeParse({ |
| 637 | + name: 'backoff_flow', |
| 638 | + label: 'Backoff Flow', |
| 639 | + type: 'autolaunched', |
| 640 | + nodes: [ |
| 641 | + { id: 'start', type: 'start', label: 'Start' }, |
| 642 | + { id: 'end', type: 'end', label: 'End' }, |
| 643 | + ], |
| 644 | + edges: [{ id: 'e1', source: 'start', target: 'end' }], |
| 645 | + errorHandling: { |
| 646 | + strategy: 'retry', |
| 647 | + maxRetries: 5, |
| 648 | + retryDelayMs: 1000, |
| 649 | + backoffMultiplier: 2, |
| 650 | + maxRetryDelayMs: 30000, |
| 651 | + jitter: true, |
| 652 | + }, |
| 653 | + }); |
| 654 | + expect(result.success).toBe(true); |
| 655 | + if (result.success) { |
| 656 | + expect(result.data.errorHandling!.backoffMultiplier).toBe(2); |
| 657 | + expect(result.data.errorHandling!.maxRetryDelayMs).toBe(30000); |
| 658 | + expect(result.data.errorHandling!.jitter).toBe(true); |
| 659 | + } |
| 660 | + }); |
| 661 | + |
| 662 | + it('should use defaults for backoff fields', () => { |
| 663 | + const result = FlowSchema.safeParse({ |
| 664 | + name: 'default_backoff', |
| 665 | + label: 'Default Backoff', |
| 666 | + type: 'autolaunched', |
| 667 | + nodes: [ |
| 668 | + { id: 'start', type: 'start', label: 'Start' }, |
| 669 | + { id: 'end', type: 'end', label: 'End' }, |
| 670 | + ], |
| 671 | + edges: [{ id: 'e1', source: 'start', target: 'end' }], |
| 672 | + errorHandling: { strategy: 'retry' }, |
| 673 | + }); |
| 674 | + expect(result.success).toBe(true); |
| 675 | + if (result.success) { |
| 676 | + expect(result.data.errorHandling!.backoffMultiplier).toBe(1); |
| 677 | + expect(result.data.errorHandling!.maxRetryDelayMs).toBe(30000); |
| 678 | + expect(result.data.errorHandling!.jitter).toBe(false); |
| 679 | + } |
| 680 | + }); |
600 | 681 | }); |
601 | 682 |
|
602 | 683 | describe('defineFlow', () => { |
@@ -642,3 +723,31 @@ describe('defineFlow', () => { |
642 | 723 | })).toThrow(); |
643 | 724 | }); |
644 | 725 | }); |
| 726 | + |
| 727 | +describe('FlowVersionHistorySchema', () => { |
| 728 | + it('should validate a flow version history entry', () => { |
| 729 | + const result = FlowVersionHistorySchema.safeParse({ |
| 730 | + flowName: 'my_flow', |
| 731 | + version: 1, |
| 732 | + definition: { |
| 733 | + name: 'my_flow', |
| 734 | + label: 'My Flow', |
| 735 | + type: 'autolaunched', |
| 736 | + nodes: [ |
| 737 | + { id: 'start', type: 'start', label: 'Start' }, |
| 738 | + { id: 'end', type: 'end', label: 'End' }, |
| 739 | + ], |
| 740 | + edges: [{ id: 'e1', source: 'start', target: 'end' }], |
| 741 | + }, |
| 742 | + createdAt: '2026-01-01T00:00:00Z', |
| 743 | + createdBy: 'admin', |
| 744 | + changeNote: 'Initial version', |
| 745 | + }); |
| 746 | + expect(result.success).toBe(true); |
| 747 | + }); |
| 748 | + |
| 749 | + it('should require flowName, version, definition, and createdAt', () => { |
| 750 | + const result = FlowVersionHistorySchema.safeParse({}); |
| 751 | + expect(result.success).toBe(false); |
| 752 | + }); |
| 753 | +}); |
0 commit comments