UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

258 lines (257 loc) โ€ข 10.9 kB
import { AudioProjectTool } from '../services/tools/AudioProjectTool.js'; import { AudioGenerationTool } from '../services/tools/AudioGenerationTool.js'; import { LLMFactory } from '../services/llm/LLMFactory.js'; import fs from 'fs/promises'; import path from 'path'; /** * Test the AudioProjectTool functionality */ export class AudioProjectToolTest { constructor() { this.testDir = path.join(process.cwd(), 'test_audio_project'); this.audioProjectTool = new AudioProjectTool(this.testDir); this.audioGenerationTool = new AudioGenerationTool(this.testDir); } async runTests() { console.log('๐Ÿงช Starting AudioProjectTool tests...\n'); try { // Setup test environment await this.setupTestEnvironment(); // Test 1: Generate test audio files await this.testGenerateAudioFiles(); // Test 2: Create audio project await this.testCreateProject(); // Test 3: Add clips to project await this.testAddClips(); // Test 4: Mix and export project await this.testMixExport(); // Test 5: Test error cases await this.testErrorCases(); console.log('โœ… All AudioProjectTool tests passed!\n'); } catch (error) { console.error('โŒ AudioProjectTool test failed:', error); throw error; } finally { // Cleanup await this.cleanup(); } } async setupTestEnvironment() { console.log('๐Ÿ“ Setting up test environment...'); // Create test directory await fs.mkdir(this.testDir, { recursive: true }); await fs.mkdir(path.join(this.testDir, 'audio'), { recursive: true }); await fs.mkdir(path.join(this.testDir, 'projects'), { recursive: true }); await fs.mkdir(path.join(this.testDir, 'output'), { recursive: true }); console.log(` Created test directory: ${this.testDir}`); } async testGenerateAudioFiles() { console.log('๐ŸŽ™๏ธ Test 1: Generating test audio files...'); // Check if we have LLM configuration for audio generation try { const llm = await LLMFactory.getConfiguredProvider(); if (!llm) { console.log(' โš ๏ธ No LLM configured, creating mock audio files instead...'); await this.createMockAudioFiles(); return; } } catch (error) { console.log(' โš ๏ธ LLM configuration error, creating mock audio files instead...'); await this.createMockAudioFiles(); return; } // Generate real audio files const audioTexts = [ 'Welcome to our test podcast. This is the introduction segment.', 'This is the main content of our podcast episode about audio projects.', 'Thank you for listening to our test podcast. This concludes our episode.' ]; const audioFiles = ['intro.wav', 'main.wav', 'outro.wav']; for (let i = 0; i < audioTexts.length; i++) { console.log(` Generating ${audioFiles[i]}...`); const result = await this.audioGenerationTool.execute({ text: audioTexts[i], output_path: `audio/${audioFiles[i]}`, voice: 'zephyr' }); if (!result.success) { console.log(` โš ๏ธ Failed to generate ${audioFiles[i]}, creating mock file instead...`); await this.createMockAudioFile(`audio/${audioFiles[i]}`); } else { console.log(` โœ… Generated ${audioFiles[i]}`); } } } async createMockAudioFiles() { const audioFiles = ['intro.wav', 'main.wav', 'outro.wav']; for (const file of audioFiles) { await this.createMockAudioFile(`audio/${file}`); } } async createMockAudioFile(relativePath) { // Create a minimal valid WAV file (1 second of silence at 44.1kHz, 16-bit, mono) const sampleRate = 44100; const duration = 1; // 1 second const numSamples = sampleRate * duration; const dataLength = numSamples * 2; // 2 bytes per sample const header = Buffer.alloc(44); // RIFF header header.write('RIFF', 0); header.writeUInt32LE(36 + dataLength, 4); header.write('WAVE', 8); // Format chunk header.write('fmt ', 12); header.writeUInt32LE(16, 16); header.writeUInt16LE(1, 20); // PCM format header.writeUInt16LE(1, 22); // Mono header.writeUInt32LE(sampleRate, 24); header.writeUInt32LE(sampleRate * 2, 28); // Byte rate header.writeUInt16LE(2, 32); // Block align header.writeUInt16LE(16, 34); // Bits per sample // Data chunk header.write('data', 36); header.writeUInt32LE(dataLength, 40); // Silent audio data (all zeros) const audioData = Buffer.alloc(dataLength, 0); const wavFile = Buffer.concat([header, audioData]); const filePath = path.join(this.testDir, relativePath); await fs.writeFile(filePath, wavFile); console.log(` โœ… Created mock audio file: ${relativePath}`); } async testCreateProject() { console.log('๐Ÿ“ Test 2: Creating audio project...'); const result = await this.audioProjectTool.execute({ operation: 'create', project_path: 'projects/test_podcast.json', title: 'Test Podcast Episode', clips: [ { file_path: 'audio/intro.wav', start_time: 0.0, volume: 1.0 } ] }); if (!result.success) { throw new Error(`Failed to create project: ${result.error}`); } console.log(` โœ… Created project: ${result.data.title} with ${result.data.clips_count} clips`); // Verify project file exists and has correct content const projectPath = path.join(this.testDir, 'projects/test_podcast.json'); const projectData = JSON.parse(await fs.readFile(projectPath, 'utf-8')); if (projectData.title !== 'Test Podcast Episode') { throw new Error('Project title mismatch'); } if (projectData.clips.length !== 1) { throw new Error('Project clips count mismatch'); } console.log(' โœ… Project file validation passed'); } async testAddClips() { console.log('โž• Test 3: Adding clips to project...'); const result = await this.audioProjectTool.execute({ operation: 'add_clip', project_path: 'projects/test_podcast.json', clips: [ { file_path: 'audio/main.wav', start_time: 5.0, volume: 0.9 }, { file_path: 'audio/outro.wav', start_time: 10.0, volume: 0.8 } ] }); if (!result.success) { throw new Error(`Failed to add clips: ${result.error}`); } console.log(` โœ… Added ${result.data.clips_added} clips. Total clips: ${result.data.total_clips}`); // Verify project has correct number of clips const projectPath = path.join(this.testDir, 'projects/test_podcast.json'); const projectData = JSON.parse(await fs.readFile(projectPath, 'utf-8')); if (projectData.clips.length !== 3) { throw new Error(`Expected 3 clips, got ${projectData.clips.length}`); } console.log(' โœ… Clip addition validation passed'); } async testMixExport() { console.log('๐ŸŽต Test 4: Mixing and exporting project...'); const result = await this.audioProjectTool.execute({ operation: 'mix_export', project_path: 'projects/test_podcast.json', output_path: 'output/final_podcast.wav' }); if (!result.success) { throw new Error(`Failed to mix and export: ${result.error}`); } console.log(` โœ… Mixed ${result.data.clips_mixed} clips and exported to: ${result.data.output_path}`); console.log(` ๐Ÿ“Š File size: ${result.data.file_size} bytes, Duration: ${result.data.duration_seconds.toFixed(2)}s`); // Verify output file exists const outputPath = path.join(this.testDir, 'output/final_podcast.wav'); const stats = await fs.stat(outputPath); if (stats.size === 0) { throw new Error('Output file is empty'); } console.log(' โœ… Export validation passed'); } async testErrorCases() { console.log('โš ๏ธ Test 5: Testing error cases...'); // Test creating project with existing name const duplicateResult = await this.audioProjectTool.execute({ operation: 'create', project_path: 'projects/test_podcast.json', clips: [] }); if (duplicateResult.success) { throw new Error('Should have failed to create duplicate project'); } console.log(' โœ… Duplicate project creation properly rejected'); // Test adding clip with non-existent file const badClipResult = await this.audioProjectTool.execute({ operation: 'add_clip', project_path: 'projects/test_podcast.json', clips: [ { file_path: 'audio/nonexistent.wav', start_time: 0.0 } ] }); if (badClipResult.success) { throw new Error('Should have failed to add non-existent audio file'); } console.log(' โœ… Non-existent file properly rejected'); // Test invalid operation const invalidOpResult = await this.audioProjectTool.execute({ operation: 'invalid_operation', project_path: 'projects/test_podcast.json' }); if (invalidOpResult.success) { throw new Error('Should have failed with invalid operation'); } console.log(' โœ… Invalid operation properly rejected'); console.log(' โœ… All error cases handled correctly'); } async cleanup() { console.log('๐Ÿงน Cleaning up test files...'); try { await fs.rm(this.testDir, { recursive: true, force: true }); console.log(' โœ… Test directory cleaned up'); } catch (error) { console.log(' โš ๏ธ Cleanup warning:', error); } } } // Export test function for CLI usage export async function testAudioProjectTool() { const test = new AudioProjectToolTest(); await test.runTests(); }