"use strict";
/**
 * Tests for Parallel Execution System
 */
Object.defineProperty(exports, "__esModule", { value: true });
const parallel_executor_1 = require("./parallel-executor");
describe('ParallelExecutor', () => {
    let executor;
    let mockContext;
    beforeEach(() => {
        executor = new parallel_executor_1.ParallelExecutor();
        mockContext = {
            workflow: {
                workflow: {
                    id: 'test',
                    version: '1.0.0',
                    name: 'Test',
                    description: 'Test',
                    actors: [],
                },
                stack: {
                    backend: {
                        type: 'backend',
                        technology: 'express',
                        endpoints: [],
                    },
                },
            },
            executionPlan: {},
            outputDirectory: './test-output',
        };
    });
    describe('Constructor', () => {
        it('should create a parallel executor', () => {
            expect(executor).toBeInstanceOf(parallel_executor_1.ParallelExecutor);
        });
        it('should set reasonable default max concurrency', () => {
            const concurrency = executor.getRecommendedConcurrency(10);
            expect(concurrency).toBeGreaterThan(1);
            expect(concurrency).toBeLessThanOrEqual(8);
        });
    });
    describe('execute()', () => {
        it('should execute single generator', async () => {
            const mockGenerator = {
                id: 'test-gen',
                name: 'Test Generator',
                description: 'Test',
                generate: jest.fn().mockResolvedValue({
                    files: [{ path: 'test.ts', content: 'test' }],
                }),
            };
            const result = await executor.execute([{ name: 'Test Gen', generator: mockGenerator }], mockContext);
            expect(result.allFiles).toHaveLength(1);
            expect(result.allErrors).toHaveLength(0);
            expect(result.results.get('Test Gen')).toBeDefined();
            expect(mockGenerator.generate).toHaveBeenCalledTimes(1);
        });
        it('should execute multiple generators in parallel', async () => {
            const generators = [];
            for (let i = 0; i < 4; i++) {
                generators.push({
                    name: `Gen ${i}`,
                    generator: {
                        id: `gen-${i}`,
                        name: `Generator ${i}`,
                        description: 'Test',
                        generate: jest.fn().mockImplementation(async () => {
                            // Simulate some work
                            await new Promise(resolve => setTimeout(resolve, 50));
                            return {
                                files: [{ path: `file${i}.ts`, content: `content${i}` }],
                            };
                        }),
                    },
                });
            }
            const startTime = Date.now();
            const result = await executor.execute(generators, mockContext, {
                maxConcurrency: 4,
            });
            const duration = Date.now() - startTime;
            // Should complete in ~50ms (parallel) not ~200ms (sequential)
            expect(duration).toBeLessThan(150);
            expect(result.allFiles).toHaveLength(4);
            expect(result.allErrors).toHaveLength(0);
            // Verify speedup
            const totalSequentialTime = Array.from(result.executionTimes.values())
                .reduce((sum, time) => sum + time, 0);
            expect(result.totalDuration).toBeLessThan(totalSequentialTime);
        });
        it('should respect max concurrency', async () => {
            let concurrentCount = 0;
            let maxConcurrent = 0;
            const generators = Array.from({ length: 6 }, (_, i) => ({
                name: `Gen ${i}`,
                generator: {
                    id: `gen-${i}`,
                    name: `Generator ${i}`,
                    description: 'Test',
                    generate: jest.fn().mockImplementation(async () => {
                        concurrentCount++;
                        maxConcurrent = Math.max(maxConcurrent, concurrentCount);
                        await new Promise(resolve => setTimeout(resolve, 50));
                        concurrentCount--;
                        return { files: [] };
                    }),
                },
            }));
            await executor.execute(generators, mockContext, {
                maxConcurrency: 2,
            });
            expect(maxConcurrent).toBeLessThanOrEqual(2);
        });
        it('should handle generator errors gracefully', async () => {
            const goodGen = {
                id: 'good',
                name: 'Good Generator',
                description: 'Test',
                generate: jest.fn().mockResolvedValue({
                    files: [{ path: 'good.ts', content: 'good' }],
                }),
            };
            const badGen = {
                id: 'bad',
                name: 'Bad Generator',
                description: 'Test',
                generate: jest.fn().mockRejectedValue(new Error('Generation failed')),
            };
            const result = await executor.execute([
                { name: 'Good', generator: goodGen },
                { name: 'Bad', generator: badGen },
            ], mockContext, {
                failFast: false,
            });
            expect(result.allFiles).toHaveLength(1); // Only good generator's files
            expect(result.allErrors).toHaveLength(1);
            expect(result.allErrors[0]).toContain('Generation failed');
        });
        it('should fail fast if enabled', async () => {
            const fastGen = {
                id: 'fast',
                name: 'Fast',
                description: 'Test',
                generate: jest.fn().mockRejectedValue(new Error('Fast failure')),
            };
            const slowGen = {
                id: 'slow',
                name: 'Slow',
                description: 'Test',
                generate: jest.fn().mockImplementation(async () => {
                    await new Promise(resolve => setTimeout(resolve, 1000));
                    return { files: [] };
                }),
            };
            await expect(executor.execute([
                { name: 'Fast', generator: fastGen },
                { name: 'Slow', generator: slowGen },
            ], mockContext, {
                failFast: true,
            })).rejects.toThrow('Fast failure');
            // Slow generator should not have completed
            expect(slowGen.generate).toHaveBeenCalled();
        });
        it('should handle timeout', async () => {
            const slowGen = {
                id: 'slow',
                name: 'Slow Generator',
                description: 'Test',
                generate: jest.fn().mockImplementation(async () => {
                    await new Promise(resolve => setTimeout(resolve, 5000));
                    return { files: [] };
                }),
            };
            const result = await executor.execute([{ name: 'Slow', generator: slowGen }], mockContext, {
                timeout: 100, // 100ms timeout
                failFast: false,
            });
            expect(result.allErrors).toHaveLength(1);
            expect(result.allErrors[0]).toContain('timed out');
        });
        it('should call progress callback', async () => {
            const progressCalls = [];
            const generators = Array.from({ length: 3 }, (_, i) => ({
                name: `Gen ${i}`,
                generator: {
                    id: `gen-${i}`,
                    name: `Generator ${i}`,
                    description: 'Test',
                    generate: jest.fn().mockResolvedValue({ files: [] }),
                },
            }));
            await executor.execute(generators, mockContext, {
                onProgress: (completed, total, name) => {
                    progressCalls.push({ completed, total, name });
                },
            });
            expect(progressCalls).toHaveLength(3);
            expect(progressCalls[0]).toEqual({ completed: 1, total: 3, name: expect.any(String) });
            expect(progressCalls[2]).toEqual({ completed: 3, total: 3, name: expect.any(String) });
        });
        it('should collect warnings from generators', async () => {
            const gen = {
                id: 'test',
                name: 'Test',
                description: 'Test',
                generate: jest.fn().mockResolvedValue({
                    files: [],
                    warnings: ['Warning 1', 'Warning 2'],
                }),
            };
            const result = await executor.execute([{ name: 'Test', generator: gen }], mockContext);
            expect(result.allWarnings).toHaveLength(2);
            expect(result.allWarnings[0]).toContain('Warning 1');
        });
        it('should collect errors from generation results', async () => {
            const gen = {
                id: 'test',
                name: 'Test',
                description: 'Test',
                generate: jest.fn().mockResolvedValue({
                    files: [],
                    errors: ['Error 1', 'Error 2'],
                }),
            };
            const result = await executor.execute([{ name: 'Test', generator: gen }], mockContext);
            expect(result.allErrors).toHaveLength(2);
            expect(result.allErrors[0]).toContain('Error 1');
        });
        it('should track execution times', async () => {
            const gen = {
                id: 'test',
                name: 'Test',
                description: 'Test',
                generate: jest.fn().mockImplementation(async () => {
                    await new Promise(resolve => setTimeout(resolve, 50));
                    return { files: [] };
                }),
            };
            const result = await executor.execute([{ name: 'Test', generator: gen }], mockContext);
            expect(result.executionTimes.get('Test')).toBeGreaterThan(40);
            expect(result.executionTimes.get('Test')).toBeLessThan(200);
            expect(result.totalDuration).toBeGreaterThan(40);
        });
    });
    describe('getRecommendedConcurrency()', () => {
        it('should return generator count for small workloads', () => {
            expect(executor.getRecommendedConcurrency(1)).toBe(1);
            expect(executor.getRecommendedConcurrency(2)).toBe(2);
        });
        it('should cap at reasonable maximum', () => {
            const concurrency = executor.getRecommendedConcurrency(100);
            expect(concurrency).toBeGreaterThanOrEqual(2);
            expect(concurrency).toBeLessThanOrEqual(8);
        });
    });
    describe('analyzeDependencies()', () => {
        it('should return empty dependencies (current implementation)', () => {
            const generators = [
                { name: 'Gen A', generator: {} },
                { name: 'Gen B', generator: {} },
            ];
            const deps = executor.analyzeDependencies(generators);
            expect(deps.get('Gen A')).toEqual([]);
            expect(deps.get('Gen B')).toEqual([]);
        });
    });
    describe('createExecutionPlan()', () => {
        it('should create batched execution plan', () => {
            const generators = Array.from({ length: 6 }, (_, i) => ({
                name: `Gen ${i}`,
                generator: {},
            }));
            const deps = new Map();
            generators.forEach(g => deps.set(g.name, []));
            const plan = executor.createExecutionPlan(generators, deps, 2);
            expect(plan).toHaveLength(3); // 6 generators / 2 concurrency = 3 batches
            expect(plan[0]).toHaveLength(2);
            expect(plan[1]).toHaveLength(2);
            expect(plan[2]).toHaveLength(2);
        });
    });
});
describe('ExecutionMetrics', () => {
    let metrics;
    beforeEach(() => {
        metrics = new parallel_executor_1.ExecutionMetrics();
    });
    describe('record()', () => {
        it('should record generator execution', () => {
            metrics.record('Gen A', 100, 5, true);
            const all = metrics.getAll();
            expect(all.get('Gen A')).toEqual({
                duration: 100,
                filesGenerated: 5,
                success: true,
                startTime: expect.any(Number),
                endTime: expect.any(Number),
            });
        });
        it('should record multiple generators', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 200, 10, true);
            expect(metrics.getAll().size).toBe(2);
        });
    });
    describe('getTotalTime()', () => {
        it('should return 0 for empty metrics', () => {
            expect(metrics.getTotalTime()).toBe(0);
        });
        it('should calculate wall clock time for parallel execution', () => {
            // Simulate parallel execution where Gen B starts while Gen A is running
            const _baseTime = Date.now();
            // Gen A: 0-100ms
            metrics.record('Gen A', 100, 5, true);
            // Gen B: 50-150ms (overlaps with Gen A)
            metrics.record('Gen B', 100, 5, true);
            const totalTime = metrics.getTotalTime();
            // Total time should be ~150ms (wall clock), not 200ms (sum of durations)
            expect(totalTime).toBeGreaterThan(0);
            expect(totalTime).toBeLessThan(300);
        });
    });
    describe('getAverageTime()', () => {
        it('should return 0 for empty metrics', () => {
            expect(metrics.getAverageTime()).toBe(0);
        });
        it('should calculate average execution time', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 200, 5, true);
            metrics.record('Gen C', 150, 5, true);
            expect(metrics.getAverageTime()).toBe(150);
        });
    });
    describe('getSlowestGenerator()', () => {
        it('should return null for empty metrics', () => {
            expect(metrics.getSlowestGenerator()).toBeNull();
        });
        it('should find slowest generator', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 300, 5, true);
            metrics.record('Gen C', 200, 5, true);
            const slowest = metrics.getSlowestGenerator();
            expect(slowest).toEqual({ name: 'Gen B', duration: 300 });
        });
    });
    describe('getFastestGenerator()', () => {
        it('should return null for empty metrics', () => {
            expect(metrics.getFastestGenerator()).toBeNull();
        });
        it('should find fastest generator', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 300, 5, true);
            metrics.record('Gen C', 200, 5, true);
            const fastest = metrics.getFastestGenerator();
            expect(fastest).toEqual({ name: 'Gen A', duration: 100 });
        });
    });
    describe('getSuccessRate()', () => {
        it('should return 0 for empty metrics', () => {
            expect(metrics.getSuccessRate()).toBe(0);
        });
        it('should calculate success rate', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 200, 5, false);
            metrics.record('Gen C', 150, 5, true);
            expect(metrics.getSuccessRate()).toBeCloseTo(66.67, 1);
        });
        it('should return 100% for all successful', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 200, 5, true);
            expect(metrics.getSuccessRate()).toBe(100);
        });
        it('should return 0% for all failed', () => {
            metrics.record('Gen A', 100, 5, false);
            metrics.record('Gen B', 200, 5, false);
            expect(metrics.getSuccessRate()).toBe(0);
        });
    });
    describe('getSummary()', () => {
        it('should return message for empty metrics', () => {
            expect(metrics.getSummary()).toBe('No metrics recorded');
        });
        it('should generate formatted summary', () => {
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 200, 10, true);
            metrics.record('Gen C', 150, 7, true);
            const summary = metrics.getSummary();
            expect(summary).toContain('Total generators: 3');
            expect(summary).toContain('Average time');
            expect(summary).toContain('Success rate: 100.0%');
            expect(summary).toContain('Slowest: Gen B');
            expect(summary).toContain('Fastest: Gen A');
        });
        it('should show speedup if applicable', () => {
            // Record metrics that show clear parallelization benefit
            metrics.record('Gen A', 100, 5, true);
            metrics.record('Gen B', 100, 5, true);
            const summary = metrics.getSummary();
            // If these ran in parallel, speedup should be close to 2x
            if (summary.includes('Speedup')) {
                expect(summary).toMatch(/Speedup: \d+\.\d+x/);
            }
        });
    });
});
//# sourceMappingURL=parallel-executor.test.js.map