Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

workflow-parallel-limit.test.tsx 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /**
  2. * MAX_PARALLEL_LIMIT Configuration Bug Test
  3. *
  4. * This test reproduces and verifies the fix for issue #23083:
  5. * MAX_PARALLEL_LIMIT environment variable does not take effect in iteration panel
  6. */
  7. import { render, screen } from '@testing-library/react'
  8. import React from 'react'
  9. // Mock environment variables before importing constants
  10. const originalEnv = process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT
  11. // Test with different environment values
  12. function setupEnvironment(value?: string) {
  13. if (value)
  14. process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT = value
  15. else
  16. delete process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT
  17. // Clear module cache to force re-evaluation
  18. jest.resetModules()
  19. }
  20. function restoreEnvironment() {
  21. if (originalEnv)
  22. process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT = originalEnv
  23. else
  24. delete process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT
  25. jest.resetModules()
  26. }
  27. // Mock i18next with proper implementation
  28. jest.mock('react-i18next', () => ({
  29. useTranslation: () => ({
  30. t: (key: string) => {
  31. if (key.includes('MaxParallelismTitle')) return 'Max Parallelism'
  32. if (key.includes('MaxParallelismDesc')) return 'Maximum number of parallel executions'
  33. if (key.includes('parallelMode')) return 'Parallel Mode'
  34. if (key.includes('parallelPanelDesc')) return 'Enable parallel execution'
  35. if (key.includes('errorResponseMethod')) return 'Error Response Method'
  36. return key
  37. },
  38. }),
  39. initReactI18next: {
  40. type: '3rdParty',
  41. init: jest.fn(),
  42. },
  43. }))
  44. // Mock i18next module completely to prevent initialization issues
  45. jest.mock('i18next', () => ({
  46. use: jest.fn().mockReturnThis(),
  47. init: jest.fn().mockReturnThis(),
  48. t: jest.fn(key => key),
  49. isInitialized: true,
  50. }))
  51. // Mock the useConfig hook
  52. jest.mock('@/app/components/workflow/nodes/iteration/use-config', () => ({
  53. __esModule: true,
  54. default: () => ({
  55. inputs: {
  56. is_parallel: true,
  57. parallel_nums: 5,
  58. error_handle_mode: 'terminated',
  59. },
  60. changeParallel: jest.fn(),
  61. changeParallelNums: jest.fn(),
  62. changeErrorHandleMode: jest.fn(),
  63. }),
  64. }))
  65. // Mock other components
  66. jest.mock('@/app/components/workflow/nodes/_base/components/variable/var-reference-picker', () => {
  67. return function MockVarReferencePicker() {
  68. return <div data-testid="var-reference-picker">VarReferencePicker</div>
  69. }
  70. })
  71. jest.mock('@/app/components/workflow/nodes/_base/components/split', () => {
  72. return function MockSplit() {
  73. return <div data-testid="split">Split</div>
  74. }
  75. })
  76. jest.mock('@/app/components/workflow/nodes/_base/components/field', () => {
  77. return function MockField({ title, children }: { title: string, children: React.ReactNode }) {
  78. return (
  79. <div data-testid="field">
  80. <label>{title}</label>
  81. {children}
  82. </div>
  83. )
  84. }
  85. })
  86. jest.mock('@/app/components/base/switch', () => {
  87. return function MockSwitch({ defaultValue }: { defaultValue: boolean }) {
  88. return <input type="checkbox" defaultChecked={defaultValue} data-testid="switch" />
  89. }
  90. })
  91. jest.mock('@/app/components/base/select', () => {
  92. return function MockSelect() {
  93. return <select data-testid="select">Select</select>
  94. }
  95. })
  96. // Use defaultValue to avoid controlled input warnings
  97. jest.mock('@/app/components/base/slider', () => {
  98. return function MockSlider({ value, max, min }: { value: number, max: number, min: number }) {
  99. return (
  100. <input
  101. type="range"
  102. defaultValue={value}
  103. max={max}
  104. min={min}
  105. data-testid="slider"
  106. data-max={max}
  107. data-min={min}
  108. readOnly
  109. />
  110. )
  111. }
  112. })
  113. // Use defaultValue to avoid controlled input warnings
  114. jest.mock('@/app/components/base/input', () => {
  115. return function MockInput({ type, max, min, value }: { type: string, max: number, min: number, value: number }) {
  116. return (
  117. <input
  118. type={type}
  119. defaultValue={value}
  120. max={max}
  121. min={min}
  122. data-testid="number-input"
  123. data-max={max}
  124. data-min={min}
  125. readOnly
  126. />
  127. )
  128. }
  129. })
  130. describe('MAX_PARALLEL_LIMIT Configuration Bug', () => {
  131. const mockNodeData = {
  132. id: 'test-iteration-node',
  133. type: 'iteration' as const,
  134. data: {
  135. title: 'Test Iteration',
  136. desc: 'Test iteration node',
  137. iterator_selector: ['test'],
  138. output_selector: ['output'],
  139. is_parallel: true,
  140. parallel_nums: 5,
  141. error_handle_mode: 'terminated' as const,
  142. },
  143. }
  144. beforeEach(() => {
  145. jest.clearAllMocks()
  146. })
  147. afterEach(() => {
  148. restoreEnvironment()
  149. })
  150. afterAll(() => {
  151. restoreEnvironment()
  152. })
  153. describe('Environment Variable Parsing', () => {
  154. it('should parse MAX_PARALLEL_LIMIT from NEXT_PUBLIC_MAX_PARALLEL_LIMIT environment variable', () => {
  155. setupEnvironment('25')
  156. const { MAX_PARALLEL_LIMIT } = require('@/config')
  157. expect(MAX_PARALLEL_LIMIT).toBe(25)
  158. })
  159. it('should fallback to default when environment variable is not set', () => {
  160. setupEnvironment() // No environment variable
  161. const { MAX_PARALLEL_LIMIT } = require('@/config')
  162. expect(MAX_PARALLEL_LIMIT).toBe(10)
  163. })
  164. it('should handle invalid environment variable values', () => {
  165. setupEnvironment('invalid')
  166. const { MAX_PARALLEL_LIMIT } = require('@/config')
  167. // Should fall back to default when parsing fails
  168. expect(MAX_PARALLEL_LIMIT).toBe(10)
  169. })
  170. it('should handle empty environment variable', () => {
  171. setupEnvironment('')
  172. const { MAX_PARALLEL_LIMIT } = require('@/config')
  173. // Should fall back to default when empty
  174. expect(MAX_PARALLEL_LIMIT).toBe(10)
  175. })
  176. // Edge cases for boundary values
  177. it('should clamp MAX_PARALLEL_LIMIT to MIN when env is 0 or negative', () => {
  178. setupEnvironment('0')
  179. let { MAX_PARALLEL_LIMIT } = require('@/config')
  180. expect(MAX_PARALLEL_LIMIT).toBe(10) // Falls back to default
  181. setupEnvironment('-5')
  182. ;({ MAX_PARALLEL_LIMIT } = require('@/config'))
  183. expect(MAX_PARALLEL_LIMIT).toBe(10) // Falls back to default
  184. })
  185. it('should handle float numbers by parseInt behavior', () => {
  186. setupEnvironment('12.7')
  187. const { MAX_PARALLEL_LIMIT } = require('@/config')
  188. // parseInt truncates to integer
  189. expect(MAX_PARALLEL_LIMIT).toBe(12)
  190. })
  191. })
  192. describe('UI Component Integration (Main Fix Verification)', () => {
  193. it('should render iteration panel with environment-configured max value', () => {
  194. // Set environment variable to a different value
  195. setupEnvironment('30')
  196. // Import Panel after setting environment
  197. const Panel = require('@/app/components/workflow/nodes/iteration/panel').default
  198. const { MAX_PARALLEL_LIMIT } = require('@/config')
  199. render(
  200. <Panel
  201. id="test-node"
  202. data={mockNodeData.data}
  203. />,
  204. )
  205. // Behavior-focused assertion: UI max should equal MAX_PARALLEL_LIMIT
  206. const numberInput = screen.getByTestId('number-input')
  207. expect(numberInput).toHaveAttribute('data-max', String(MAX_PARALLEL_LIMIT))
  208. const slider = screen.getByTestId('slider')
  209. expect(slider).toHaveAttribute('data-max', String(MAX_PARALLEL_LIMIT))
  210. // Verify the actual values
  211. expect(MAX_PARALLEL_LIMIT).toBe(30)
  212. expect(numberInput.getAttribute('data-max')).toBe('30')
  213. expect(slider.getAttribute('data-max')).toBe('30')
  214. })
  215. it('should maintain UI consistency with different environment values', () => {
  216. setupEnvironment('15')
  217. const Panel = require('@/app/components/workflow/nodes/iteration/panel').default
  218. const { MAX_PARALLEL_LIMIT } = require('@/config')
  219. render(
  220. <Panel
  221. id="test-node"
  222. data={mockNodeData.data}
  223. />,
  224. )
  225. // Both input and slider should use the same max value from MAX_PARALLEL_LIMIT
  226. const numberInput = screen.getByTestId('number-input')
  227. const slider = screen.getByTestId('slider')
  228. expect(numberInput.getAttribute('data-max')).toBe(slider.getAttribute('data-max'))
  229. expect(numberInput.getAttribute('data-max')).toBe(String(MAX_PARALLEL_LIMIT))
  230. })
  231. })
  232. describe('Legacy Constant Verification (For Transition Period)', () => {
  233. // Marked as transition/deprecation tests
  234. it('should maintain MAX_ITERATION_PARALLEL_NUM for backward compatibility', () => {
  235. const { MAX_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants')
  236. expect(typeof MAX_ITERATION_PARALLEL_NUM).toBe('number')
  237. expect(MAX_ITERATION_PARALLEL_NUM).toBe(10) // Hardcoded legacy value
  238. })
  239. it('should demonstrate MAX_PARALLEL_LIMIT vs legacy constant difference', () => {
  240. setupEnvironment('50')
  241. const { MAX_PARALLEL_LIMIT } = require('@/config')
  242. const { MAX_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants')
  243. // MAX_PARALLEL_LIMIT is configurable, MAX_ITERATION_PARALLEL_NUM is not
  244. expect(MAX_PARALLEL_LIMIT).toBe(50)
  245. expect(MAX_ITERATION_PARALLEL_NUM).toBe(10)
  246. expect(MAX_PARALLEL_LIMIT).not.toBe(MAX_ITERATION_PARALLEL_NUM)
  247. })
  248. })
  249. describe('Constants Validation', () => {
  250. it('should validate that required constants exist and have correct types', () => {
  251. const { MAX_PARALLEL_LIMIT } = require('@/config')
  252. const { MIN_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants')
  253. expect(typeof MAX_PARALLEL_LIMIT).toBe('number')
  254. expect(typeof MIN_ITERATION_PARALLEL_NUM).toBe('number')
  255. expect(MAX_PARALLEL_LIMIT).toBeGreaterThanOrEqual(MIN_ITERATION_PARALLEL_NUM)
  256. })
  257. })
  258. })