contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
258 lines (257 loc) โข 10.9 kB
JavaScript
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();
}