/** * MAX_PARALLEL_LIMIT Configuration Bug Test * * This test reproduces and verifies the fix for issue #23083: * MAX_PARALLEL_LIMIT environment variable does not take effect in iteration panel */ import { render, screen } from '@testing-library/react' import React from 'react' // Mock environment variables before importing constants const originalEnv = process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT // Test with different environment values function setupEnvironment(value?: string) { if (value) process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT = value else delete process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT // Clear module cache to force re-evaluation jest.resetModules() } function restoreEnvironment() { if (originalEnv) process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT = originalEnv else delete process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT jest.resetModules() } // Mock i18next with proper implementation jest.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => { if (key.includes('MaxParallelismTitle')) return 'Max Parallelism' if (key.includes('MaxParallelismDesc')) return 'Maximum number of parallel executions' if (key.includes('parallelMode')) return 'Parallel Mode' if (key.includes('parallelPanelDesc')) return 'Enable parallel execution' if (key.includes('errorResponseMethod')) return 'Error Response Method' return key }, }), initReactI18next: { type: '3rdParty', init: jest.fn(), }, })) // Mock i18next module completely to prevent initialization issues jest.mock('i18next', () => ({ use: jest.fn().mockReturnThis(), init: jest.fn().mockReturnThis(), t: jest.fn(key => key), isInitialized: true, })) // Mock the useConfig hook jest.mock('@/app/components/workflow/nodes/iteration/use-config', () => ({ __esModule: true, default: () => ({ inputs: { is_parallel: true, parallel_nums: 5, error_handle_mode: 'terminated', }, changeParallel: jest.fn(), changeParallelNums: jest.fn(), changeErrorHandleMode: jest.fn(), }), })) // Mock other components jest.mock('@/app/components/workflow/nodes/_base/components/variable/var-reference-picker', () => { return function MockVarReferencePicker() { return
VarReferencePicker
} }) jest.mock('@/app/components/workflow/nodes/_base/components/split', () => { return function MockSplit() { return
Split
} }) jest.mock('@/app/components/workflow/nodes/_base/components/field', () => { return function MockField({ title, children }: { title: string, children: React.ReactNode }) { return (
{children}
) } }) jest.mock('@/app/components/base/switch', () => { return function MockSwitch({ defaultValue }: { defaultValue: boolean }) { return } }) jest.mock('@/app/components/base/select', () => { return function MockSelect() { return } }) // Use defaultValue to avoid controlled input warnings jest.mock('@/app/components/base/slider', () => { return function MockSlider({ value, max, min }: { value: number, max: number, min: number }) { return ( ) } }) // Use defaultValue to avoid controlled input warnings jest.mock('@/app/components/base/input', () => { return function MockInput({ type, max, min, value }: { type: string, max: number, min: number, value: number }) { return ( ) } }) describe('MAX_PARALLEL_LIMIT Configuration Bug', () => { const mockNodeData = { id: 'test-iteration-node', type: 'iteration' as const, data: { title: 'Test Iteration', desc: 'Test iteration node', iterator_selector: ['test'], output_selector: ['output'], is_parallel: true, parallel_nums: 5, error_handle_mode: 'terminated' as const, }, } beforeEach(() => { jest.clearAllMocks() }) afterEach(() => { restoreEnvironment() }) afterAll(() => { restoreEnvironment() }) describe('Environment Variable Parsing', () => { it('should parse MAX_PARALLEL_LIMIT from NEXT_PUBLIC_MAX_PARALLEL_LIMIT environment variable', () => { setupEnvironment('25') const { MAX_PARALLEL_LIMIT } = require('@/config') expect(MAX_PARALLEL_LIMIT).toBe(25) }) it('should fallback to default when environment variable is not set', () => { setupEnvironment() // No environment variable const { MAX_PARALLEL_LIMIT } = require('@/config') expect(MAX_PARALLEL_LIMIT).toBe(10) }) it('should handle invalid environment variable values', () => { setupEnvironment('invalid') const { MAX_PARALLEL_LIMIT } = require('@/config') // Should fall back to default when parsing fails expect(MAX_PARALLEL_LIMIT).toBe(10) }) it('should handle empty environment variable', () => { setupEnvironment('') const { MAX_PARALLEL_LIMIT } = require('@/config') // Should fall back to default when empty expect(MAX_PARALLEL_LIMIT).toBe(10) }) // Edge cases for boundary values it('should clamp MAX_PARALLEL_LIMIT to MIN when env is 0 or negative', () => { setupEnvironment('0') let { MAX_PARALLEL_LIMIT } = require('@/config') expect(MAX_PARALLEL_LIMIT).toBe(10) // Falls back to default setupEnvironment('-5') ;({ MAX_PARALLEL_LIMIT } = require('@/config')) expect(MAX_PARALLEL_LIMIT).toBe(10) // Falls back to default }) it('should handle float numbers by parseInt behavior', () => { setupEnvironment('12.7') const { MAX_PARALLEL_LIMIT } = require('@/config') // parseInt truncates to integer expect(MAX_PARALLEL_LIMIT).toBe(12) }) }) describe('UI Component Integration (Main Fix Verification)', () => { it('should render iteration panel with environment-configured max value', () => { // Set environment variable to a different value setupEnvironment('30') // Import Panel after setting environment const Panel = require('@/app/components/workflow/nodes/iteration/panel').default const { MAX_PARALLEL_LIMIT } = require('@/config') render( , ) // Behavior-focused assertion: UI max should equal MAX_PARALLEL_LIMIT const numberInput = screen.getByTestId('number-input') expect(numberInput).toHaveAttribute('data-max', String(MAX_PARALLEL_LIMIT)) const slider = screen.getByTestId('slider') expect(slider).toHaveAttribute('data-max', String(MAX_PARALLEL_LIMIT)) // Verify the actual values expect(MAX_PARALLEL_LIMIT).toBe(30) expect(numberInput.getAttribute('data-max')).toBe('30') expect(slider.getAttribute('data-max')).toBe('30') }) it('should maintain UI consistency with different environment values', () => { setupEnvironment('15') const Panel = require('@/app/components/workflow/nodes/iteration/panel').default const { MAX_PARALLEL_LIMIT } = require('@/config') render( , ) // Both input and slider should use the same max value from MAX_PARALLEL_LIMIT const numberInput = screen.getByTestId('number-input') const slider = screen.getByTestId('slider') expect(numberInput.getAttribute('data-max')).toBe(slider.getAttribute('data-max')) expect(numberInput.getAttribute('data-max')).toBe(String(MAX_PARALLEL_LIMIT)) }) }) describe('Legacy Constant Verification (For Transition Period)', () => { // Marked as transition/deprecation tests it('should maintain MAX_ITERATION_PARALLEL_NUM for backward compatibility', () => { const { MAX_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants') expect(typeof MAX_ITERATION_PARALLEL_NUM).toBe('number') expect(MAX_ITERATION_PARALLEL_NUM).toBe(10) // Hardcoded legacy value }) it('should demonstrate MAX_PARALLEL_LIMIT vs legacy constant difference', () => { setupEnvironment('50') const { MAX_PARALLEL_LIMIT } = require('@/config') const { MAX_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants') // MAX_PARALLEL_LIMIT is configurable, MAX_ITERATION_PARALLEL_NUM is not expect(MAX_PARALLEL_LIMIT).toBe(50) expect(MAX_ITERATION_PARALLEL_NUM).toBe(10) expect(MAX_PARALLEL_LIMIT).not.toBe(MAX_ITERATION_PARALLEL_NUM) }) }) describe('Constants Validation', () => { it('should validate that required constants exist and have correct types', () => { const { MAX_PARALLEL_LIMIT } = require('@/config') const { MIN_ITERATION_PARALLEL_NUM } = require('@/app/components/workflow/constants') expect(typeof MAX_PARALLEL_LIMIT).toBe('number') expect(typeof MIN_ITERATION_PARALLEL_NUM).toBe('number') expect(MAX_PARALLEL_LIMIT).toBeGreaterThanOrEqual(MIN_ITERATION_PARALLEL_NUM) }) }) })