UNPKG

@elevenlabs/convai-cli

Version:

CLI tool to manage ElevenLabs conversational AI agents

385 lines 17.5 kB
"use strict"; /** * Integration tests for sync functionality with mocked API calls */ 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const utils_1 = require("../utils"); const templates_1 = require("../templates"); const elevenLabsApi = __importStar(require("../elevenlabs-api")); const config = __importStar(require("../config")); // Mock the entire elevenlabs-api module jest.mock('../elevenlabs-api'); const mockedElevenLabsApi = elevenLabsApi; // Mock the config module jest.mock('../config'); const mockedConfig = config; // Mock os module for config path jest.mock('os', () => ({ ...jest.requireActual('os'), homedir: jest.fn() })); const mockedOs = os; describe('Sync Integration Tests', () => { let tempDir; let agentsConfigPath; let lockFilePath; beforeEach(async () => { // Create a temporary directory tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'convai-sync-test-')); agentsConfigPath = path.join(tempDir, 'agents.json'); lockFilePath = path.join(tempDir, 'convai.lock'); // Mock os.homedir for config mockedOs.homedir.mockReturnValue(tempDir); // Mock config to return a valid API key mockedConfig.getApiKey.mockResolvedValue('test-api-key'); // Mock ElevenLabs client const mockClient = { conversationalAi: { agents: { update: jest.fn() } } }; mockedElevenLabsApi.getElevenLabsClient.mockResolvedValue(mockClient); mockedElevenLabsApi.updateAgentApi.mockResolvedValue('agent_123'); // Clear all mocks jest.clearAllMocks(); }); afterEach(async () => { // Clean up temp directory await fs.remove(tempDir); jest.clearAllMocks(); }); describe('Config change detection and API calls', () => { it('should call updateAgentApi when agent config changes', async () => { // Setup: Create initial agent configuration const agentName = 'Test Agent'; const agentId = 'agent_123'; const environment = 'prod'; // Create agents.json const agentsConfig = { agents: [{ name: agentName, environments: { [environment]: { config: `agent_configs/${environment}/test_agent.json` } } }] }; await (0, utils_1.writeAgentConfig)(agentsConfigPath, agentsConfig); // Create initial agent config const initialConfig = (0, templates_1.getDefaultAgentTemplate)(agentName); const configPath = path.join(tempDir, agentsConfig.agents[0].environments[environment].config); await fs.ensureDir(path.dirname(configPath)); await (0, utils_1.writeAgentConfig)(configPath, initialConfig); // Create lock file with existing agent (using a different hash to ensure change detection) const lockData = { agents: { [agentName]: { [environment]: { id: agentId, hash: 'old_hash_that_will_not_match' } } }, tools: {} }; await (0, utils_1.saveLockFile)(lockFilePath, lockData); // Modify the agent config to trigger a change const modifiedConfig = { ...initialConfig, conversation_config: { ...initialConfig.conversation_config, agent: { ...initialConfig.conversation_config.agent, prompt: { ...initialConfig.conversation_config.agent.prompt, prompt: 'Modified prompt for testing', temperature: 0.5 // Changed from 0.0 } } } }; await (0, utils_1.writeAgentConfig)(configPath, modifiedConfig); // Import and call the sync logic const { syncAgentsWithMocks } = await createSyncFunction(); // Execute sync await syncAgentsWithMocks(tempDir, agentName, false, environment); // Verify that updateAgentApi was called expect(mockedElevenLabsApi.updateAgentApi).toHaveBeenCalledTimes(1); expect(mockedElevenLabsApi.updateAgentApi).toHaveBeenCalledWith(expect.any(Object), // client agentId, agentName, modifiedConfig.conversation_config, modifiedConfig.platform_settings, modifiedConfig.tags); }); it('should not call updateAgentApi when config has not changed', async () => { // Setup: Create agent configuration const agentName = 'Test Agent'; const agentId = 'agent_123'; const environment = 'prod'; // Create agents.json const agentsConfig = { agents: [{ name: agentName, environments: { [environment]: { config: `agent_configs/${environment}/test_agent.json` } } }] }; await (0, utils_1.writeAgentConfig)(agentsConfigPath, agentsConfig); // Create agent config const agentConfig = (0, templates_1.getDefaultAgentTemplate)(agentName); const configPath = path.join(tempDir, agentsConfig.agents[0].environments[environment].config); await fs.ensureDir(path.dirname(configPath)); await (0, utils_1.writeAgentConfig)(configPath, agentConfig); // Create lock file with matching hash (no changes) const configHash = (0, utils_1.calculateConfigHash)(agentConfig); const lockData = { agents: { [agentName]: { [environment]: { id: agentId, hash: configHash } } }, tools: {} }; await (0, utils_1.saveLockFile)(lockFilePath, lockData); // Import and call the sync logic const { syncAgentsWithMocks } = await createSyncFunction(); // Execute sync await syncAgentsWithMocks(tempDir, agentName, false, environment); // Verify that updateAgentApi was NOT called expect(mockedElevenLabsApi.updateAgentApi).not.toHaveBeenCalled(); }); it('should update lock file hash after successful API call', async () => { // Setup: Create agent with changed config const agentName = 'Test Agent'; const agentId = 'agent_123'; const environment = 'prod'; // Create agents.json const agentsConfig = { agents: [{ name: agentName, environments: { [environment]: { config: `agent_configs/${environment}/test_agent.json` } } }] }; await (0, utils_1.writeAgentConfig)(agentsConfigPath, agentsConfig); // Create modified agent config const modifiedConfig = (0, templates_1.getDefaultAgentTemplate)(agentName); modifiedConfig.conversation_config.agent.prompt.prompt = 'Modified prompt'; const configPath = path.join(tempDir, agentsConfig.agents[0].environments[environment].config); await fs.ensureDir(path.dirname(configPath)); await (0, utils_1.writeAgentConfig)(configPath, modifiedConfig); // Create lock file with old hash const lockData = { agents: { [agentName]: { [environment]: { id: agentId, hash: 'old_hash_value' } } }, tools: {} }; await (0, utils_1.saveLockFile)(lockFilePath, lockData); // Import and call the sync logic const { syncAgentsWithMocks } = await createSyncFunction(); // Execute sync await syncAgentsWithMocks(tempDir, agentName, false, environment); // Verify lock file was updated with new hash const updatedLockData = await (0, utils_1.loadLockFile)(lockFilePath); const newHash = (0, utils_1.calculateConfigHash)(modifiedConfig); expect(updatedLockData.agents[agentName][environment].hash).toBe(newHash); expect(updatedLockData.agents[agentName][environment].hash).not.toBe('old_hash_value'); }); it('should handle API errors gracefully', async () => { // Setup: Create agent configuration const agentName = 'Test Agent'; const agentId = 'agent_123'; const environment = 'prod'; // Mock API to throw an error mockedElevenLabsApi.updateAgentApi.mockRejectedValue(new Error('API Error')); // Create agents.json const agentsConfig = { agents: [{ name: agentName, environments: { [environment]: { config: `agent_configs/${environment}/test_agent.json` } } }] }; await (0, utils_1.writeAgentConfig)(agentsConfigPath, agentsConfig); // Create modified agent config const modifiedConfig = (0, templates_1.getDefaultAgentTemplate)(agentName); modifiedConfig.conversation_config.agent.prompt.prompt = 'Modified prompt'; const configPath = path.join(tempDir, agentsConfig.agents[0].environments[environment].config); await fs.ensureDir(path.dirname(configPath)); await (0, utils_1.writeAgentConfig)(configPath, modifiedConfig); // Create lock file const lockData = { agents: { [agentName]: { [environment]: { id: agentId, hash: 'old_hash' } } }, tools: {} }; await (0, utils_1.saveLockFile)(lockFilePath, lockData); // Import and call the sync logic const { syncAgentsWithMocks } = await createSyncFunction(); // Execute sync and expect it to handle the error await syncAgentsWithMocks(tempDir, agentName, false, environment); // Verify API was called but failed expect(mockedElevenLabsApi.updateAgentApi).toHaveBeenCalledTimes(1); // Verify lock file was NOT updated due to error const lockDataAfter = await (0, utils_1.loadLockFile)(lockFilePath); expect(lockDataAfter.agents[agentName][environment].hash).toBe('old_hash'); }); }); }); // Helper function to create a testable sync function async function createSyncFunction() { // This mimics the core sync logic from cli.ts but in a testable way async function syncAgentsWithMocks(projectPath, agentName, dryRun = false, environment) { const AGENTS_CONFIG_FILE = "agents.json"; const LOCK_FILE = "convai.lock"; // Load agents configuration const agentsConfigPath = path.join(projectPath, AGENTS_CONFIG_FILE); const agentsConfig = await fs.readJson(agentsConfigPath); const lockFilePath = path.join(projectPath, LOCK_FILE); const lockData = await (0, utils_1.loadLockFile)(lockFilePath); // Initialize ElevenLabs client let client; if (!dryRun) { client = await mockedElevenLabsApi.getElevenLabsClient(); } // Filter agents if specific agent name provided let agentsToProcess = agentsConfig.agents; if (agentName) { agentsToProcess = agentsConfig.agents.filter((agent) => agent.name === agentName); } // Determine environments to sync let environmentsToSync = []; if (environment) { environmentsToSync = [environment]; } else { const envSet = new Set(); for (const agentDef of agentsToProcess) { if (agentDef.environments) { Object.keys(agentDef.environments).forEach(env => envSet.add(env)); } } environmentsToSync = Array.from(envSet); } let changesMade = false; for (const currentEnv of environmentsToSync) { for (const agentDef of agentsToProcess) { const agentDefName = agentDef.name; // Get config path for current environment let configPath; if (agentDef.environments && currentEnv in agentDef.environments) { configPath = agentDef.environments[currentEnv].config; } else { continue; } // Check if config file exists if (!configPath) { continue; } const fullConfigPath = path.join(projectPath, configPath); if (!(await fs.pathExists(fullConfigPath))) { continue; } // Load agent config const agentConfig = await fs.readJson(fullConfigPath); // Calculate config hash const configHash = (0, utils_1.calculateConfigHash)(agentConfig); // Get environment-specific agent data from lock file const lockedAgent = lockData.agents?.[agentDefName]?.[currentEnv]; let needsUpdate = true; if (lockedAgent && lockedAgent.hash === configHash) { needsUpdate = false; } if (!needsUpdate || dryRun) { continue; } // Perform API operation try { const agentId = lockedAgent?.id; // Extract config components const conversationConfig = agentConfig.conversation_config || {}; const platformSettings = agentConfig.platform_settings; const tags = agentConfig.tags || []; const agentDisplayName = agentConfig.name || agentDefName; if (agentId) { // Update existing agent await mockedElevenLabsApi.updateAgentApi(client, agentId, agentDisplayName, conversationConfig, platformSettings, tags); (0, utils_1.updateAgentInLock)(lockData, agentDefName, currentEnv, agentId, configHash); changesMade = true; } } catch (error) { // Log error but continue (matches CLI behavior) console.log(`Error processing ${agentDefName}: ${error}`); } } } // Save lock file if changes were made if (changesMade && !dryRun) { await (0, utils_1.saveLockFile)(lockFilePath, lockData); } } return { syncAgentsWithMocks }; } //# sourceMappingURL=sync.integration.test.js.map