"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const cli_progress_1 = __importDefault(require("cli-progress"));
const fs = __importStar(require("fs"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const logger_1 = __importDefault(require("../../src/logger"));
const providers_1 = require("../../src/providers");
const constants_1 = require("../../src/redteam/constants");
const entities_1 = require("../../src/redteam/extraction/entities");
const purpose_1 = require("../../src/redteam/extraction/purpose");
const index_1 = require("../../src/redteam/index");
const plugins_1 = require("../../src/redteam/plugins");
const remoteGeneration_1 = require("../../src/redteam/remoteGeneration");
const strategies_1 = require("../../src/redteam/strategies");
const strategies_2 = require("../../src/redteam/strategies");
const apiHealth_1 = require("../../src/util/apiHealth");
jest.mock('cli-progress');
jest.mock('../../src/logger');
jest.mock('../../src/providers');
jest.mock('../../src/redteam/extraction/entities');
jest.mock('../../src/redteam/extraction/purpose');
jest.mock('../../src/util/templates', () => {
    const originalModule = jest.requireActual('../../src/util/templates');
    return {
        ...originalModule,
        extractVariablesFromTemplates: jest.fn(originalModule.extractVariablesFromTemplates),
    };
});
jest.mock('process', () => ({
    ...jest.requireActual('process'),
    exit: jest.fn(),
}));
jest.mock('../../src/redteam/strategies', () => ({
    ...jest.requireActual('../../src/redteam/strategies'),
    validateStrategies: jest.fn().mockImplementation((strategies) => {
        if (strategies.some((s) => s.id === 'invalid-strategy')) {
            throw new Error('Invalid strategies');
        }
    }),
}));
jest.mock('../../src/util/apiHealth');
jest.mock('../../src/redteam/remoteGeneration');
describe('synthesize', () => {
    const mockProvider = {
        callApi: jest.fn(),
        generate: jest.fn(),
        id: () => 'test-provider',
    };
    beforeEach(() => {
        jest.clearAllMocks();
        jest.mocked(entities_1.extractEntities).mockResolvedValue(['entity1', 'entity2']);
        jest.mocked(purpose_1.extractSystemPurpose).mockResolvedValue('Test purpose');
        jest.mocked(providers_1.loadApiProvider).mockResolvedValue(mockProvider);
        jest.spyOn(process, 'exit').mockImplementation((code) => {
            throw new Error(`Process.exit called with code ${code}`);
        });
        jest.mocked(strategies_2.validateStrategies).mockImplementation(async () => { });
        jest.mocked(cli_progress_1.default.SingleBar).mockReturnValue({
            increment: jest.fn(),
            start: jest.fn(),
            stop: jest.fn(),
            update: jest.fn(),
        });
    });
    // Input handling tests
    describe('Input handling', () => {
        it('should use provided purpose and entities if given', async () => {
            const result = await (0, index_1.synthesize)({
                entities: ['custom-entity'],
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                purpose: 'Custom purpose',
                strategies: [],
            });
            expect(result).toEqual(expect.objectContaining({
                entities: ['custom-entity'],
                purpose: 'Custom purpose',
            }));
            expect(entities_1.extractEntities).not.toHaveBeenCalled();
            expect(purpose_1.extractSystemPurpose).not.toHaveBeenCalled();
        });
        it('should extract purpose and entities if not provided', async () => {
            await (0, index_1.synthesize)({
                language: 'english',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(entities_1.extractEntities).toHaveBeenCalledWith(expect.any(Object), ['Test prompt']);
            expect(purpose_1.extractSystemPurpose).toHaveBeenCalledWith(expect.any(Object), ['Test prompt']);
        });
        it('should handle empty prompts array', async () => {
            await expect((0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: [],
                strategies: [],
            })).rejects.toThrow('Prompts array cannot be empty');
        });
        it('should correctly process multiple prompts', async () => {
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Prompt 1', 'Prompt 2', 'Prompt 3'],
                strategies: [],
            });
            expect(purpose_1.extractSystemPurpose).toHaveBeenCalledWith(expect.any(Object), [
                'Prompt 1',
                'Prompt 2',
                'Prompt 3',
            ]);
            expect(entities_1.extractEntities).toHaveBeenCalledWith(expect.any(Object), [
                'Prompt 1',
                'Prompt 2',
                'Prompt 3',
            ]);
        });
    });
    // API provider tests
    describe('API provider', () => {
        it('should use the provided API provider if given', async () => {
            const customProvider = {
                callApi: jest.fn(),
                generate: jest.fn(),
                id: () => 'custom-provider',
            };
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                provider: customProvider,
                strategies: [],
            });
            expect(providers_1.loadApiProvider).not.toHaveBeenCalled();
        });
    });
    // Plugin and strategy tests
    describe('Plugins and strategies', () => {
        it('should generate test cases for each plugin', async () => {
            const mockPluginAction = jest.fn().mockResolvedValue([{ test: 'case' }]);
            jest.spyOn(plugins_1.Plugins, 'find').mockReturnValue({ action: mockPluginAction, key: 'mockPlugin' });
            const result = await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [
                    { id: 'plugin1', numTests: 2 },
                    { id: 'plugin2', numTests: 3 },
                ],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(mockPluginAction).toHaveBeenCalledTimes(2);
            expect(result.testCases).toEqual([
                expect.objectContaining({ metadata: expect.objectContaining({ pluginId: 'plugin1' }) }),
                expect.objectContaining({ metadata: expect.objectContaining({ pluginId: 'plugin2' }) }),
            ]);
        });
        it('should warn about unregistered plugins', async () => {
            jest.spyOn(plugins_1.Plugins, 'find').mockReturnValue(undefined);
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'unregistered-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(logger_1.default.warn).toHaveBeenCalledWith('Plugin unregistered-plugin not registered, skipping');
        });
        it('should handle HARM_PLUGINS and PII_PLUGINS correctly', async () => {
            const mockPluginAction = jest.fn().mockResolvedValue([{ test: 'case' }]);
            jest.spyOn(plugins_1.Plugins, 'find').mockReturnValue({ action: mockPluginAction, key: 'mockPlugin' });
            const result = await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [
                    { id: 'harmful', numTests: 2 },
                    { id: 'pii', numTests: 3 },
                ],
                prompts: ['Test prompt'],
                strategies: [],
            });
            const expectedTestCaseCount = (Object.keys(constants_1.HARM_PLUGINS).length + constants_1.PII_PLUGINS.length) * 1; // Each plugin is called once
            expect(result.testCases).toHaveLength(expectedTestCaseCount);
        });
        it('should generate a correct report for plugins and strategies', async () => {
            const mockPluginAction = jest.fn().mockResolvedValue([{ test: 'case' }]);
            jest.spyOn(plugins_1.Plugins, 'find').mockReturnValue({ action: mockPluginAction, key: 'mockPlugin' });
            const mockStrategyAction = jest.fn().mockReturnValue([{ test: 'strategy case' }]);
            jest
                .spyOn(strategies_1.Strategies, 'find')
                .mockReturnValue({ action: mockStrategyAction, id: 'mockStrategy' });
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 2,
                plugins: [{ id: 'test-plugin', numTests: 2 }],
                prompts: ['Test prompt'],
                strategies: [{ id: 'mockStrategy' }],
            });
            expect(logger_1.default.info).toHaveBeenCalledWith(expect.stringContaining('Test Generation Report:'));
            expect(logger_1.default.info).toHaveBeenCalledWith(expect.stringContaining('test-plugin'));
            expect(logger_1.default.info).toHaveBeenCalledWith(expect.stringContaining('mockStrategy'));
        });
    });
    describe('Logger', () => {
        it('debug log level hides progress bar', async () => {
            const originalLogLevel = process.env.LOG_LEVEL;
            process.env.LOG_LEVEL = 'debug';
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(cli_progress_1.default.SingleBar).not.toHaveBeenCalled();
            process.env.LOG_LEVEL = originalLogLevel;
        });
    });
    describe('API Health Check', () => {
        beforeEach(() => {
            jest.clearAllMocks();
            jest.mocked(remoteGeneration_1.shouldGenerateRemote).mockReturnValue(true);
            jest.mocked(remoteGeneration_1.getRemoteHealthUrl).mockReturnValue('https://api.test/health');
            jest.mocked(apiHealth_1.checkRemoteHealth).mockResolvedValue({
                status: 'OK',
                message: 'Cloud API is healthy',
            });
        });
        it('should check API health when remote generation is enabled', async () => {
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(remoteGeneration_1.shouldGenerateRemote).toHaveBeenCalledWith();
            expect(remoteGeneration_1.getRemoteHealthUrl).toHaveBeenCalledWith();
            expect(apiHealth_1.checkRemoteHealth).toHaveBeenCalledWith('https://api.test/health');
        });
        it('should skip health check when remote generation is disabled', async () => {
            jest.mocked(remoteGeneration_1.shouldGenerateRemote).mockReturnValue(false);
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(remoteGeneration_1.shouldGenerateRemote).toHaveBeenCalledWith();
            expect(remoteGeneration_1.getRemoteHealthUrl).not.toHaveBeenCalled();
            expect(apiHealth_1.checkRemoteHealth).not.toHaveBeenCalled();
        });
        it('should throw error when health check fails', async () => {
            jest.mocked(apiHealth_1.checkRemoteHealth).mockResolvedValue({
                status: 'ERROR',
                message: 'API is not accessible',
            });
            await expect((0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            })).rejects.toThrow('Unable to proceed with test generation: API is not accessible');
        });
        it('should skip health check when URL is null', async () => {
            jest.mocked(remoteGeneration_1.getRemoteHealthUrl).mockReturnValue(null);
            await (0, index_1.synthesize)({
                language: 'en',
                numTests: 1,
                plugins: [{ id: 'test-plugin', numTests: 1 }],
                prompts: ['Test prompt'],
                strategies: [],
            });
            expect(remoteGeneration_1.shouldGenerateRemote).toHaveBeenCalledWith();
            expect(remoteGeneration_1.getRemoteHealthUrl).toHaveBeenCalledWith();
            expect(apiHealth_1.checkRemoteHealth).not.toHaveBeenCalled();
        });
    });
    it('should handle basic strategy configuration', async () => {
        jest.mocked(providers_1.loadApiProvider).mockResolvedValue({
            id: () => 'test',
            callApi: jest.fn().mockResolvedValue({ output: 'test output' }),
        });
        // Mock plugin to generate a test case
        const mockPlugin = {
            id: 'test-plugin',
            numTests: 1,
        };
        const mockProvider = {
            id: () => 'test',
            callApi: jest.fn().mockResolvedValue({ output: 'test output' }),
        };
        // Test with basic strategy enabled
        const resultEnabled = await (0, index_1.synthesize)({
            plugins: [mockPlugin],
            strategies: [{ id: 'basic', config: { enabled: true } }],
            prompts: ['test prompt'],
            injectVar: 'input',
            provider: mockProvider,
            language: 'en',
            numTests: 1,
        });
        expect(resultEnabled.testCases.length).toBeGreaterThan(0);
        // Test with basic strategy disabled
        const resultDisabled = await (0, index_1.synthesize)({
            plugins: [mockPlugin],
            strategies: [{ id: 'basic', config: { enabled: false } }],
            prompts: ['test prompt'],
            injectVar: 'input',
            provider: mockProvider,
            language: 'en',
            numTests: 1,
        });
        expect(resultDisabled.testCases).toHaveLength(0);
    });
});
jest.mock('fs');
jest.mock('js-yaml');
describe('resolvePluginConfig', () => {
    afterEach(() => {
        jest.resetAllMocks();
    });
    it('should return an empty object if config is undefined', () => {
        const result = (0, index_1.resolvePluginConfig)(undefined);
        expect(result).toEqual({});
    });
    it('should return the original config if no file references are present', () => {
        const config = { key: 'value' };
        const result = (0, index_1.resolvePluginConfig)(config);
        expect(result).toEqual(config);
    });
    it('should resolve YAML file references', () => {
        const config = { key: 'file://test.yaml' };
        const yamlContent = { nested: 'value' };
        jest.spyOn(fs, 'existsSync').mockReturnValue(true);
        jest.spyOn(fs, 'readFileSync').mockReturnValue('yaml content');
        jest.mocked(js_yaml_1.default.load).mockReturnValue(yamlContent);
        const result = (0, index_1.resolvePluginConfig)(config);
        expect(result).toEqual({ key: yamlContent });
        expect(fs.existsSync).toHaveBeenCalledWith('test.yaml');
        expect(fs.readFileSync).toHaveBeenCalledWith('test.yaml', 'utf8');
        expect(js_yaml_1.default.load).toHaveBeenCalledWith('yaml content');
    });
    it('should resolve JSON file references', () => {
        const config = { key: 'file://test.json' };
        const jsonContent = { nested: 'value' };
        jest.spyOn(fs, 'existsSync').mockReturnValue(true);
        jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify(jsonContent));
        const result = (0, index_1.resolvePluginConfig)(config);
        expect(result).toEqual({ key: jsonContent });
        expect(fs.existsSync).toHaveBeenCalledWith('test.json');
        expect(fs.readFileSync).toHaveBeenCalledWith('test.json', 'utf8');
    });
    it('should resolve text file references', () => {
        const config = { key: 'file://test.txt' };
        const fileContent = 'text content';
        jest.spyOn(fs, 'existsSync').mockReturnValue(true);
        jest.spyOn(fs, 'readFileSync').mockReturnValue(fileContent);
        const result = (0, index_1.resolvePluginConfig)(config);
        expect(result).toEqual({ key: fileContent });
        expect(fs.existsSync).toHaveBeenCalledWith('test.txt');
        expect(fs.readFileSync).toHaveBeenCalledWith('test.txt', 'utf8');
    });
    it('should throw an error if the file does not exist', () => {
        const config = { key: 'file://nonexistent.yaml' };
        jest.spyOn(fs, 'existsSync').mockReturnValue(false);
        expect(() => (0, index_1.resolvePluginConfig)(config)).toThrow('File not found: nonexistent.yaml');
    });
    it('should handle multiple file references', () => {
        const config = {
            yaml: 'file://test.yaml',
            json: 'file://test.json',
            txt: 'file://test.txt',
        };
        const yamlContent = { nested: 'yaml' };
        const jsonContent = { nested: 'json' };
        const txtContent = 'text content';
        jest.spyOn(fs, 'existsSync').mockReturnValue(true);
        jest
            .spyOn(fs, 'readFileSync')
            .mockReturnValueOnce('yaml content')
            .mockReturnValueOnce(JSON.stringify(jsonContent))
            .mockReturnValueOnce(txtContent);
        jest.mocked(js_yaml_1.default.load).mockReturnValue(yamlContent);
        const result = (0, index_1.resolvePluginConfig)(config);
        expect(result).toEqual({
            yaml: yamlContent,
            json: jsonContent,
            txt: txtContent,
        });
    });
});
//# sourceMappingURL=index.test.js.map